O que são SQLite STRICT Tables
O SQLite tem uma peculiaridade famosa: você pode inserir um texto em uma coluna declarada como INTEGER e ele aceita sem reclamar. Isso e chamado de type affinity - o banco tenta converter, mas não rejeita o dado. Para muitos projetos isso é um pesadelo.
Na versão 3.37.0 (novembro de 2021), o SQLite ganhou um novo modo: STRICT tables. Com ele, a tabela passa a ter verificação de tipos real, igual ao que você espera de PostgreSQL ou MySQL. Tenta inserir texto onde e número? Erro. Simples assim.
A funcionalidade existia como pedido da comunidade ha anos. Finalmente chegou sem quebrar a compatibilidade retroativa - você escolhe tabela por tabela quais devem ser STRICT.
Como funciona
Basta adicionar a palavra STRICT no final do CREATE TABLE. A partir dai, o SQLite rejeita qualquer valor que não seja do tipo correto. Não ha conversão implícita.
Os tipos validos em modo STRICT são apenas seis: INT, INTEGER, REAL, TEXT, BLOB e ANY. O tipo ANY e especial - aceita qualquer valor como no modo normal, mas preserva o tipo original sem tentar converter. Você perde tipos como VARCHAR(100), BOOLEAN e DATETIME - eles simplesmente não existem no modo STRICT.
O motor faz a verificação no momento da inserção ou atualização. Se o dado não bate com o tipo, a operação retorna erro imediatamente. Nenhum dado ruim entra no banco.
Você pode misturar tabelas STRICT e não-STRICT no mesmo banco .db. Migrar só as tabelas críticas e uma estratégia valida.
Principais recursos
O modo STRICT vai além da verificação de tipos. Veja o que muda no comportamento do banco:
- Rejeição de tipo errado: inserir
'abc'em coluna INTEGER gera erro, sem exceção. - Tipo ANY preservado: ao contrario do modo normal,
ANYno STRICT preserva o tipo exato do valor inserido - texto fica texto, número fica número. - Compatível com CHECK constraints: você ainda pode adicionar
CHECKpara validações extras além do tipo. - Sem impacto em performance: a verificação de tipo e trivial para o SQLite. Benchmarks mostram diferença desprezível.
- Funciona com WAL, FTS, JSON: todos os recursos do SQLite continuam funcionando em tabelas STRICT.
Uma limitação importante: WITHOUT ROWID e compatível com STRICT - você pode combinar os dois modificadores escrevendo STRICT, WITHOUT ROWID no final da declaração.
Como começar: criando sua primeira tabela STRICT
Você não precisa instalar nada além do SQLite 3.37 ou superior. No terminal, verifique sua versão:
sqlite3 --version
# Deve mostrar 3.37.0 ou superiorPara criar uma tabela STRICT, a sintaxe e a mesma de sempre, com STRICT no final:
CREATE TABLE usuários (
id INTEGER PRIMARY KEY,
nome TEXT NOT NULL,
email TEXT NOT NULL,
pontos REAL DEFAULT 0.0
) STRICT;Agora tente inserir um tipo errado e veja o erro:
-- Isso vai funcionar normalmente
INSERT INTO usuários (nome, email, pontos) VALUES ('Ana', '[email protected]', 10.5);
-- Isso vai retornar erro: "cannot store TEXT value in INTEGER column"
INSERT INTO usuários (id, nome, email) VALUES ('não-sou-número', 'Carlos', '[email protected]');Tipos como BOOLEAN, VARCHAR, DATETIME e NUMERIC não existem no modo STRICT. Você precisa mapear para TEXT, INTEGER ou REAL.
Exemplo prático com Python
Em Python, o módulo sqlite3 da stdlib funciona normalmente com tabelas STRICT. O erro de tipo vira uma exceção sqlite3.OperationalError:
import sqlite3
con = sqlite3.connect("app.db")
cur = con.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS pedidos (
id INTEGER PRIMARY KEY,
produto TEXT NOT NULL,
quantidade INTEGER NOT NULL,
preço REAL NOT NULL
) STRICT
""")
# Inserção valida
cur.execute(
"INSERT INTO pedidos (produto, quantidade, preço) VALUES (?, ?, ?)",
("Teclado", 2, 149.90)
)
# Isso vai lançar OperationalError
try:
cur.execute(
"INSERT INTO pedidos (produto, quantidade, preço) VALUES (?, ?, ?)",
("Mouse", "dois", 89.90) # quantidade deveria ser INTEGER
)
except sqlite3.OperationalError as e:
print(f"Erro capturado: {e}")
con.commit()
con.close()O comportamento e previsível: o erro aparece na hora da inserção, não depois de horas debugando por que um calculo retornou resultado errado por causa de dado mal tipado.
Use parâmetros com ? (ou :nome) em vez de interpolar strings. O driver Python passa os valores com tipo preservado, o que colabora com STRICT.
Comparação com alternativas
O SQLite normal usa type affinity: tenta converter, aceita qualquer coisa. PostgreSQL e MySQL sempre tiveram tipos fortes. Com STRICT, o SQLite se aproxima desses bancos em termos de integridade, mas sem precisar de servidor.
- SQLite normal: flexível, aceita tudo, bom para prototipagem rápida. Pode esconder bugs.
- SQLite STRICT: integridade de tipo, melhor para produção, ainda sem servidor. Requer SQLite 3.37+.
- PostgreSQL/MySQL: tipos fortes nativos, mas exigem servidor, configuração, Docker ou serviço pago.
- DuckDB: tipos fortes, otimizado para analítica. Não substitui SQLite para apps transacionais.
O ponto forte do STRICT e justamente o equilíbrio: você tem integridade sem sair do arquivo .db local que já funciona em mobile, desktop e edge.
Pontos positivos e limitações
O que vai bem com STRICT:
- Pega erros de tipo no momento da inserção, não semanas depois
- Torna o schema mais autodocumentado - o tipo e real, não decorativo
- Compatível com ORMs modernos (SQLAlchemy, Prisma, Drizzle) que já mapeiam tipos corretamente
- Sem overhead perceptivel de performance
O que você precisa saber antes de adotar:
- Não funciona em versões anteriores ao SQLite 3.37 - cheque o ambiente de produção
- Tipos como BOOLEAN não existem: você guarda
0e1como INTEGER - Datas ficam como TEXT (ISO 8601) ou INTEGER (Unix timestamp) - escolha e documente
- Migrar tabelas existentes para STRICT exige recriar a tabela, não ha
ALTER TABLE ... STRICT
Não ha como converter uma tabela existente para STRICT com ALTER TABLE. Você precisa criar nova tabela STRICT, copiar os dados e renomear. Faca isso dentro de uma transação.
Casos de uso reais
O STRICT e especialmente útil em cenários onde a integridade do dado e crítica:
- Apps mobile (Android/iOS): bancos locais em apps Flutter ou React Native onde o schema e definido uma vez e deve ser rígido. Erros de tipo viram crashes difíceis de reproduzir.
- CLIs e ferramentas de linha de comando: ferramentas que processam arquivos CSV ou JSON e precisam garantir que os tipos importados são validos antes de salvar.
- Edge functions e SQLite distribuído (Turso, Cloudflare D1): ambientes sem servidor central onde você quer a mesma previsibilidade de um banco relacional tradicional.
- Scripts de ETL e pipelines de dados: quando você importa dados de fontes externas e quer rejeitar linhas invalidas no banco em vez de propagar dados sujos.
Dicas e boas práticas
Adote uma convenção para datas desde o inicio: TEXT em formato ISO 8601 ("2026-07-12") e legível e ordenável. INTEGER Unix timestamp e mais compacto. Escolha um padrão e documente no schema.
Para valores booleanos, crie uma CHECK constraint: CHECK (ativo IN (0, 1)). Combina bem com STRICT e documenta a intenção no próprio schema.
Se você usa Drizzle ORM, o modo STRICT e suportado desde a versão 0.28. Basta adicionar strict: true na definição da tabela e o Drizzle gera o DDL correto automaticamente.
Se você usa um ORM que gera tipos como VARCHAR ou NUMERIC, o DDL vai falhar em tabelas STRICT. Verifique o SQL gerado antes de executar em banco de produção.
Vale a pena?
Se você esta começando um projeto novo com SQLite, a resposta e quase sempre sim. O custo de adicionar STRICT no CREATE TABLE e zero, e o beneficio de pegar erros de tipo cedo e real.
Se você tem um banco existente com dados heterogéneos (o tipo de coisa que acontece quando você insere dados de APIs externas ha anos), a migração exige cuidado. Vale auditar os dados antes de migrar.
Para projetos que usam SQLite em mobile, edge ou como banco embedded em ferramentas desktop, o STRICT e hoje a escolha mais defensiva. Você ganha previsibilidade sem abrir mao da simplicidade que fez o SQLite ser adotado em mais de 1 trilhao de instâncias no mundo.
O próximo passo: verifique sua versão do SQLite com sqlite3 --version e experimente criar uma tabela STRICT no seu próximo projeto. A mudança e pequena, o impacto na qualidade do seu banco pode ser grande.
Comentários
Deixar um comentárioVocê precisa ter uma conta no CuritibaBlog para comentar.