Menu

Tabelas STRICT no SQLite: tipagem forte de verdade

Veja como as tabelas STRICT do SQLite desligam o armazenamento flexível, rejeitam valores de tipo errado e finalmente entregam a checagem de tipos que você esperava desde o começo.

Esta página tem editores executáveis — edite, execute e veja a saída na hora.

Por que existem as STRICT tables

O SQLite, por padrão, é famoso por ser relaxado com tipos. Você declara uma coluna como INTEGER, insere a string "hello" e o SQLite simplesmente dá de ombros e guarda a string assim mesmo. Essa flexibilidade foi uma escolha de design intencional lá nos anos 90, mas pega de surpresa quem vem do Postgres ou do MySQL — e, pior, esconde bugs.

As STRICT tables, introduzidas no SQLite 3.37, resolvem isso. Você ativa o modo strict por tabela e, a partir daí, os tipos das colunas passam a valer de verdade.

A palavra-chave STRICT vem depois do parêntese de fechamento. O resto continua igual a um CREATE TABLE comum. A diferença só aparece na hora em que você tenta gravar um valor do tipo errado em uma coluna.

O que o STRICT realmente garante

Numa tabela comum, a type affinity do SQLite tenta converter o valor para o tipo declarado e, se não rolar, ainda assim guarda como veio. Já numa tabela STRICT, qualquer divergência de tipo vira erro.

Faça o mesmo teste em uma tabela não-STRICT e o terceiro insert vai passar numa boa — o SQLite alegremente guarda a string 'oops' numa coluna que você declarou como INTEGER. Meses depois, uma consulta de agregação retorna um valor sem pé nem cabeça e você perde uma tarde inteira caçando o bug. Com STRICT, o erro estoura na hora do insert, exatamente onde dá pra corrigir.

O erro que aparece:

Erro de execução: não é possível armazenar valor TEXT na coluna INTEGER accounts.balance

Claro, direto e impossível de ignorar.

Os cinco tipos permitidos

Tabelas STRICT só aceitam cinco nomes de tipo:

  • INTEGER — números inteiros.
  • REAL — números de ponto flutuante.
  • TEXT — strings.
  • BLOB — bytes brutos.
  • ANY — qualquer tipo, sem conversão.

É só isso. Os apelidos informais que o SQLite normalmente aceita — VARCHAR(255), DOUBLE, BOOLEAN, DATETIME, INT — todos disparam erro dentro de uma tabela STRICT:

O erro:

Erro de análise: tipo de dado desconhecido para bad.name: "VARCHAR(255)"

A solução é usar um dos cinco nomes canônicos. VARCHAR(255) vira TEXT, DATETIME vira TEXT (afinal, o SQLite já armazena datas como strings ISO) e BOOLEAN vira INTEGER (com 0 e 1).

A válvula de escape: o tipo ANY

ANY é o único tipo que permite uma coluna STRICT guardar valores de tipos diferentes — útil, por exemplo, numa coluna value genérica de uma tabela chave/valor:

ANY tem um comportamento especial em tabelas STRICT: os valores são guardados sem a coerção de tipo que essa mesma palavra causaria em outros contextos. Uma string '100' continua sendo string; um inteiro 100 continua sendo inteiro. As chamadas a typeof() na consulta acima comprovam isso.

Numa tabela não-STRICT, uma coluna com afinidade ANY converteria strings que parecem números em números de fato. Já a STRICT preserva o tipo original na íntegra.

STRICT e PRIMARY KEY

Tem uma diferença sutil aqui: em uma tabela comum, INTEGER PRIMARY KEY é um caso especial — vira um alias para rowid e só aceita inteiros. Outras declarações de chave primária são mais flexíveis.

Numa tabela STRICT, o tipo da coluna é validado independentemente de ela ser chave primária ou não:

O segundo INSERT falha. Numa tabela não-STRICT, o valor 42 seria gravado silenciosamente na coluna TEXT da chave primária. Aqui, o SQLite avisa na hora.

Misturando tabelas STRICT e não-STRICT

O modo STRICT vale por tabela, não pelo banco inteiro. Dá pra ter uma tabela users no modo strict e uma events no modo relaxado dentro do mesmo arquivo. As foreign keys continuam funcionando entre elas normalmente, como em qualquer outro caso.

A tabela events não tem STRICT nem tipo declarado em payload, então ela aceita qualquer coisa que você jogar lá dentro. Útil de vez em quando — perigoso como padrão. Deixe esse tipo de armazenamento sem tipo para casos em que você realmente precisa de uma coluna coringa.

Quando usar STRICT em tabelas SQLite

Para schemas novos, a resposta é "quase sempre". O custo é mínimo — uma palavra-chave por tabela e lembrar dos cinco nomes de tipo canônicos. Em troca, bugs que normalmente ficariam escondidos nos seus dados aparecem logo no insert que os causou.

Pule o STRICT quando:

  • Você está mantendo um banco SQLite antigo cujo schema depende da tipagem relaxada.
  • Você precisa rodar em versões do SQLite anteriores à 3.37 (outubro de 2021) — a palavra-chave simplesmente não existe nelas.
  • Você realmente quer que uma coluna armazene tipos mistos; mesmo nesse caso, prefira uma tabela STRICT com uma coluna ANY em vez de uma tabela sem STRICT, porque todo o resto continua sendo validado.

Um checklist rápido para converter uma tabela comum em STRICT:

  • Troque VARCHAR, CHAR, NVARCHAR por TEXT.
  • Troque DOUBLE, FLOAT, NUMERIC por REAL.
  • Troque BOOLEAN, BIT, TINYINT por INTEGER.
  • Troque DATETIME, TIMESTAMP, DATE por TEXT (ou INTEGER se você guarda timestamps unix).
  • Adicione STRICT depois do parêntese de fechamento.

A seguir: chaves primárias

Tabelas STRICT apertam o controle sobre como cada coluna guarda seus dados. O próximo ponto que vale apertar é qual coluna identifica cada linha — e as chaves primárias do SQLite têm algumas peculiaridades (principalmente em torno de INTEGER PRIMARY KEY e do rowid) que vale a pena conhecer antes de modelar um schema de verdade.

Perguntas frequentes

O que é uma tabela STRICT no SQLite?

Uma tabela STRICT obriga o SQLite a respeitar o tipo declarado de cada coluna — se você disse que a coluna é INTEGER, qualquer valor que não seja inteiro ou NULL é rejeitado. Para ativar, basta colocar a palavra-chave STRICT depois do parêntese de fechamento do CREATE TABLE. Sem isso, o SQLite usa type affinity, que tenta converter o valor quando dá e armazena do jeito que veio quando não dá.

Quais tipos posso usar em uma tabela STRICT?

Apenas cinco: INTEGER, REAL, TEXT, BLOB e ANY. Aqueles aliases que funcionam em tabelas comuns — VARCHAR, DOUBLE, BOOLEAN, DATETIME — todos disparam erro em uma tabela STRICT. A coluna ANY serve como válvula de escape: aceita qualquer tipo sem fazer conversão.

Vale a pena usar tabelas STRICT em bancos SQLite novos?

Para a maioria dos esquemas novos, sim. Tabelas STRICT pegam bugs que as tabelas comuns engolem caladas — uma string parar em uma coluna INTEGER, uma lista serializada por acidente em um REAL. O custo é uma palavra-chave a mais por tabela e abrir mão dos nomes de tipo exóticos. Disponível desde o SQLite 3.37 (2021).

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR