Menu

Tabelle STRICT in SQLite: far rispettare i tipi delle colonne

Come le tabelle STRICT di SQLite disattivano l'archiviazione con tipi flessibili, rifiutano i valori del tipo sbagliato e ti danno il controllo dei tipi che probabilmente ti aspettavi fin dall'inizio.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Perché esistono le tabelle STRICT

Il comportamento predefinito di SQLite con i tipi è notoriamente rilassato. Dichiari una colonna INTEGER, inserisci la stringa "hello" e SQLite alza le spalle e salva la stringa. Questa flessibilità è stata una scelta di progetto deliberata negli anni '90, ma sorprende chi arriva da Postgres o MySQL, e nasconde i bug.

Le tabelle STRICT, introdotte in SQLite 3.37, risolvono il problema. Le attivi tabella per tabella, e da quel momento i tipi delle colonne significano quello che dicono.

La parola chiave STRICT va dopo la parentesi di chiusura. Tutto il resto è un normale CREATE TABLE. La differenza si vede appena provi a mettere in una colonna un valore del tipo sbagliato.

Cosa impone davvero STRICT

In una tabella normale, l'affinità di tipo prova a convertire un valore nel tipo dichiarato e, se non ci riesce, lo salva così com'è. In una tabella STRICT, una mancata corrispondenza è un errore.

Prova lo stesso con una tabella non STRICT e il terzo insert va a buon fine: SQLite salva allegramente la stringa 'oops' in una colonna che avevi dichiarato INTEGER. Mesi dopo, una query di aggregazione restituisce risultati senza senso e passi un pomeriggio a cercare il motivo. STRICT fa emergere l'errore al momento dell'insert, dove puoi correggerlo.

L'errore che vedrai:

Runtime error: cannot store TEXT value in INTEGER column accounts.balance

Chiaro, immediato, difficile da ignorare.

I cinque tipi ammessi

Le tabelle STRICT accettano solo cinque nomi di tipo:

  • INTEGER: numeri interi.
  • REAL: numeri in virgola mobile.
  • TEXT: stringhe.
  • BLOB: byte grezzi.
  • ANY: qualsiasi tipo, senza conversione.

Tutto qui. Gli alias informali che SQLite di solito accetta, come VARCHAR(255), DOUBLE, BOOLEAN, DATETIME e INT, generano tutti un errore dentro una tabella STRICT:

L'errore:

Parse error: unknown datatype for bad.name: "VARCHAR(255)"

La soluzione è usare uno dei cinque nomi canonici. VARCHAR(255) diventa TEXT, DATETIME diventa TEXT (SQLite salva comunque le date come stringhe ISO), BOOLEAN diventa INTEGER (con 0 e 1).

La via di fuga ANY

ANY è l'unico tipo che permette a una colonna STRICT di contenere valori eterogenei: utile per esempio per una colonna generica value in una tabella chiave/valore:

Dentro le tabelle STRICT ANY è speciale: salva i valori senza la conversione di tipo che la stessa parola implicherebbe altrove. Una stringa '100' resta una stringa; un intero 100 resta un intero. Le chiamate a typeof() nella query qui sopra lo dimostrano.

In una tabella non STRICT, una colonna con affinità ANY convertirebbe in numeri le stringhe dall'aspetto numerico. STRICT conserva esattamente il tipo originale.

STRICT e PRIMARY KEY

Una differenza sottile: in una tabella normale, INTEGER PRIMARY KEY è speciale, perché diventa un alias del rowid e accetta solo interi. Le altre dichiarazioni di chiave primaria sono più permissive.

In una tabella STRICT, il tipo della colonna viene imposto indipendentemente dal fatto che sia la chiave primaria:

Il secondo insert fallisce. In una tabella non STRICT, 42 verrebbe salvato in silenzio nella colonna di chiave primaria TEXT. Qui, invece, te lo dice.

Mescolare tabelle STRICT e non STRICT

STRICT vale per tabella, non per database. Puoi avere una tabella users rigida e una tabella events rilassata nello stesso file. Le chiavi esterne funzionano tra le due come farebbero comunque.

La tabella events non ha STRICT né un tipo dichiarato su payload, quindi accetta qualsiasi cosa tu le passi. A volte è utile, come impostazione predefinita è rischioso. Riserva l'archiviazione senza tipo ai casi in cui ti serve davvero una colonna tuttofare.

Quando usare STRICT

Per i nuovi schemi la risposta è "quasi sempre". Il costo è minimo: una parola chiave per tabella e ricordarsi i cinque nomi di tipo canonici. Il vantaggio è che i bug che di solito si nasconderebbero nei tuoi dati emergono proprio all'insert che li ha causati.

Lascia perdere STRICT quando:

  • Stai mantenendo un vecchio database SQLite il cui schema esistente si basa sulla tipizzazione rilassata.
  • Il tuo obiettivo è una versione di SQLite precedente alla 3.37 (ottobre 2021): lì la parola chiave non esiste.
  • Vuoi davvero che una colonna contenga tipi misti; anche in quel caso, preferisci STRICT più una colonna ANY a una tabella non STRICT, perché tutto il resto continua a essere controllato.

Una breve checklist per convertire una tabella normale in STRICT:

  • Sostituisci VARCHAR, CHAR, NVARCHAR con TEXT.
  • Sostituisci DOUBLE, FLOAT, NUMERIC con REAL.
  • Sostituisci BOOLEAN, BIT, TINYINT con INTEGER.
  • Sostituisci DATETIME, TIMESTAMP, DATE con TEXT (o INTEGER se salvi timestamp unix).
  • Aggiungi STRICT dopo la parentesi di chiusura.

Prossimo passo: le chiavi primarie

Le tabelle STRICT rendono più rigoroso il modo in cui le colonne salvano i dati. La prossima cosa da rendere più rigorosa è quale colonna identifica ogni riga, e le chiavi primarie di SQLite hanno un paio di stranezze (soprattutto su INTEGER PRIMARY KEY e rowid) che vale la pena conoscere prima di progettare uno schema reale.

Domande frequenti

Cos'è una tabella STRICT in SQLite?

Una tabella STRICT fa rispettare il tipo dichiarato delle colonne: se dici che una colonna è INTEGER, SQLite rifiuterà qualsiasi valore che non sia un intero o NULL. La attivi aggiungendo la parola chiave STRICT dopo la parentesi di chiusura di CREATE TABLE. Senza, SQLite usa l'affinità di tipo, che converte i valori quando può e li salva così come sono quando non può.

Quali tipi posso usare in una tabella STRICT?

Solo cinque: INTEGER, REAL, TEXT, BLOB e ANY. Gli alias che funzionano nelle tabelle normali, come VARCHAR, DOUBLE, BOOLEAN e DATETIME, generano tutti un errore in una tabella STRICT. La colonna ANY è una via di fuga che accetta qualsiasi tipo senza conversione.

Dovrei usare le tabelle STRICT nei nuovi database SQLite?

Per la maggior parte dei nuovi schemi, sì. Le tabelle STRICT intercettano bug che le tabelle normali ingoiano in silenzio: una stringa finita per sbaglio in una colonna INTEGER, una lista serializzata per errore in un REAL. Il costo è una parola chiave in più per tabella e la rinuncia ai nomi di tipo più esotici. Disponibili da SQLite 3.37 (2021).

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA