Documentazione

Come si usa PrismDB, passo per passo.

Installazione, primo programma e riferimento per la shell, il gateway, .NET, Node.js, JDBC, le estensioni degli IDE e l'API C. I file si scaricano dalla sezione Download.

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.1 con 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 a host: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 tabella sqlite_sequence, l'EXPLAIN in bytecode (c'è solo EXPLAIN QUERY PLAN). Nelle righe di EXPLAIN QUERY PLAN la colonna id è numerata da PrismDB (in SQLite è un indirizzo della sua macchina virtuale); parent e detail coincidono.
  • 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).
  • CALL esegue il corpo come un'unica istruzione: tutto o niente.
  • Se l'ultima istruzione del corpo è una SELECT, CALL restituisce le sue righe.
  • I parametri non accettano NULL.
  • CREATE PROCEDURE IF NOT EXISTS, DROP PROCEDURE [IF EXISTS] nome e SHOW 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):

  1. Estrai lo zip in una cartella, per esempio C:\PrismDB.
  2. Facoltativo, per usare prism da qualsiasi cartella: aggiungi la cartella al PATH dell'utente (poi apri un nuovo terminale):

    $p = [Environment]::GetEnvironmentVariable('Path', 'User')
    [Environment]::SetEnvironmentVariable('Path', "$p;C:\PrismDB", 'User')
  3. Prova: prism -version stampa PrismDB 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.

  1. Estrai prismdb-dotnet-2.0.0-nuget.zip in una cartella, per esempio C:\PrismDB\nuget.
  2. 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
  3. Il gateway: copia prismdb-gateway.exe (Windows) o prismdb-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: GatewayPath nelle opzioni, la cartella dell'app, le sottocartelle BINS\windows, BINS\linux e gateway, poi la variabile d'ambiente PRISMDB_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 con NextResult().
  • CommandType.StoredProcedure esegue CALL nome(...) con i parametri.
  • I valori arrivano con le classi di SQLite (long, double, string, byte[], DBNull); GetFieldValue<T> converte in DateTime, DateOnly, TimeOnly, DateTimeOffset, TimeSpan, Guid, decimal, bool, enum.
  • BeginTransaction() apre una transazione vera; i savepoint funzionano.
  • Ogni connessione ha la sua sessione: tabelle TEMP, ATTACH e PRAGMA restano suoi.
  • Errori: PrismDbException con il messaggio di SQLite e SqliteErrorCode (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 stessa partial class; togli quella riga e il file diventa tuo (non viene più toccato);
  • in un'app pubblicata (nessun .csproj sopra l'eseguibile) non fa niente;
  • senza argomenti la connection string viene da PRISMDB_CONNECTION o da ConnectionStrings:PrismDB in appsettings.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

  1. Estrai prismdb-node-2.0.0.zip (tre file .tgz) in una cartella, per esempio vendor/ dentro il progetto.
  2. 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   # Linux

    Per 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 .tgz nel repository (o in un registro npm privato): package.json li 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.mjs import { withPrismDB } from 'prismdb/next'; export default withPrismDB({ ... }) (genera i tipi all'avvio di next dev e tiene prismdb fuori 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, readonly o full) e usa prismdb/browser nel 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

  1. Avvia il gateway sul tuo file: prismdb-gateway --db C:\dati\app.prismdb (lascia aperta la finestra).
  2. In DBeaver: Database → Gestione driver → Nuovo.
  3. Scheda Librerie → Aggiungi file → scegli prismdb-jdbc-2.0.0.jar.
  4. Nome classe: com.prismdb.jdbc.PrismDBDriver (o Trova classe).
  5. Modello URL: jdbc:prismdb://{host}:{port}/{database}, porta predefinita 15432. Salva.
  6. Nuova connessione → driver PrismDB → host 127.0.0.1, porta 15432, database app (è solo un'etichetta: il file è quello del gateway). Se il gateway ha un token, scrivilo nel campo Password (l'utente è ignorato).
  7. 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:porta o prismdb://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 mostra EXPLAIN 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-env e 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.

  1. Settings → Plugins → icona ⚙ → Install Plugin from Disk...
  2. Scegli il file zip così com'è (non estrarlo) → OK → riavvia l'IDE.

  3. 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.

  4. Albero: tabelle (colonne, chiavi, FK), viste, indici, trigger, procedure; menu: dati, definizione, script, backup (VACUUM INTO), elimina.
  5. 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.
  6. Finestra PrismDB Risultati in basso; editor dei dati con salvataggio in transazione; completamento di tabelle, colonne e procedure.
  7. 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-env e 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 sostituisci app.prismdb con la copia. Se accanto al file vecchio è rimasto un app.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 rispondere ok. * 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

  1. Con sqlite3 (lo strumento di SQLite) esporta il database come SQL:

    sqlite3 vecchio.db .dump > dump.sql
  2. Se il database usava AUTOINCREMENT, togli dal file le righe su sqlite_sequence (PrismDB non ha quella tabella; i contatori ripartono comunque dal valore più alto):

    grep -v sqlite_sequence dump.sql > dump-prism.sql                      # Linux
    Get-Content dump.sql | Where-Object { $_ -notmatch 'sqlite_sequence' } | Set-Content -Encoding utf8 dump-prism.sql   # Windows
  3. 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