Por que o SQLite foi escrito em C
(sqlite.org)- O SQLite foi desenvolvido em linguagem C desde o início (2000) por causa de desempenho, compatibilidade, poucas dependências e estabilidade
- C pode ser usado em quase todos os sistemas operacionais e linguagens e, em especial, oferece execução rápida como biblioteca de baixo nível
- O motivo para escolher C em vez de uma linguagem orientada a objetos foi a extensibilidade, a possibilidade de chamada a partir de várias linguagens e a imaturidade de C++ e Java na época do desenvolvimento
- O SQLite tem uma estrutura de arquivo único com quase nenhuma dependência e usa apenas o mínimo de funções da biblioteca padrão de C
- Há discussões sobre reescrevê-lo em "linguagens seguras" como Rust e Go, mas C ainda leva vantagem em controle de qualidade, desempenho e possibilidade de chamada por bibliotecas
1. Por que C é a escolha ideal
- O SQLite é mantido em linguagem C desde o primeiro desenvolvimento em 29 de maio de 2000 até hoje
- No momento, não há planos de reescrevê-lo em outra linguagem
- C oferece controle próximo ao hardware e, ao mesmo tempo, excelente portabilidade, sendo chamada de “linguagem assembly portátil”
- Outras linguagens podem afirmar que são “tão rápidas quanto C”, mas nenhuma afirma ser mais rápida que C
1.1. Desempenho
- Uma biblioteca de baixo nível como o SQLite é chamada com frequência e, por isso, precisa operar com muita rapidez
- A linguagem C é adequada para escrever código rápido, mantendo alta portabilidade e acesso próximo ao hardware
- Outras linguagens modernas também afirmam ser “tão rápidas quanto C”, mas, em programação de propósito geral, não há linguagem que se possa afirmar com convicção ser mais rápida que C
- C permite controle fino sobre memória e recursos de CPU, podendo até apresentar desempenho 35% superior ao sistema de arquivos
- Exemplo: Internal vs External BLOBs
1.2. Compatibilidade
- Quase todo sistema consegue chamar bibliotecas escritas em C
- Por exemplo, até no Android (baseado em Java) é possível usar SQLite por meio de um adaptador
- Se o SQLite tivesse sido escrito em Java, ele não poderia ser usado no iPhone (Objective-C, Swift), reduzindo muito sua universalidade
1.3. Baixas dependências
- Como foi desenvolvido como biblioteca em C, ele tem pouquíssimas dependências de runtime
- Na configuração mínima, usa apenas funções muito básicas da biblioteca padrão de C (
memcmp(),memcpy(),memmove(),memset(),strcmp(),strlen(),strncmp()) - Mesmo em builds mais completos, depende apenas de poucos elementos como
malloc(),free()e entrada/saída de arquivos - Linguagens modernas muitas vezes exigem grandes runtimes e milhares de interfaces
1.4. Estabilidade
- C é uma linguagem antiga, pouco mutável e sem grandes surpresas, e isso significa previsibilidade e estabilidade
- Ao criar um mecanismo de banco de dados pequeno, rápido e confiável como o SQLite, uma linguagem cujo padrão não muda com frequência é mais adequada
- Se a especificação ou a implementação da linguagem mudar com frequência, isso é desfavorável à estabilidade do SQLite
2. Por que não foi escrito em uma linguagem orientada a objetos
- Alguns desenvolvedores acham que seria difícil implementar um sistema complexo como o SQLite sem orientação a objetos, mas, em comparação com C, criar uma biblioteca em C++ ou Java dificulta a chamada a partir de outras linguagens
- Para dar suporte a várias linguagens, como Haskell e Java, a escolha de uma biblioteca em C foi adequada
- Orientação a objetos não é uma linguagem, mas um padrão de projeto, então não está limitada a uma linguagem específica
- Mesmo em C, é possível implementar padrões orientados a objetos com structs e ponteiros de função
- Orientação a objetos nem sempre é a melhor estrutura; às vezes, código procedural é mais claro, mais fácil de manter e também mais rápido
- No início do desenvolvimento do SQLite (por volta de 2000):
- Java era imaturo
- C++ tinha graves problemas de compatibilidade entre compiladores
→ Na época, C era a escolha mais prática e segura
- Ainda hoje, há poucos benefícios claros para justificar uma reescrita do SQLite
3. Por que não foi escrito em uma "linguagem segura"
- Recentemente, cresceu o interesse por linguagens de programação seguras como Rust e Go, mas elas não existiam quando o SQLite foi desenvolvido originalmente (nem nos seus primeiros 10 anos)
- Se fosse reescrito em Go ou Rust, poderia haver mais bugs ou queda de desempenho
- Essas linguagens inserem código adicional de ramificação (
branch) para verificações como checagem de memória, e, na estratégia de qualidade do SQLite, cobertura de branches de 100% é importante, algo que isso não satisfaz - Linguagens seguras geralmente encerram o programa em situações de falta de memória, mas o SQLite foi projetado para conseguir se recuperar mesmo em cenários de memória insuficiente
- Rust e Go ainda são linguagens relativamente novas e exigem desenvolvimento contínuo
- Por isso, a equipe do SQLite apoia a evolução das linguagens seguras, mas, na implementação do SQLite, continua priorizando a estabilidade comprovada de C
Mesmo assim, no futuro pode haver a possibilidade de uma reescrita em Rust. Já Go provavelmente é menos provável, porque não gosta de assert()
- Porém, para que ele seja escrito em Rust, há pré-condições:
- Rust precisa amadurecer mais e ter um ciclo de mudanças mais lento, tornando-se uma “linguagem antiga e sem grandes surpresas”
- Deve ficar comprovado que é possível criar uma biblioteca universal chamável por várias linguagens
- Deve ser possível gerar código objeto que funcione também em dispositivos embarcados sem sistema operacional
- Devem existir ferramentas de teste de cobertura de branches de 100% para os binários compilados
- Deve ser possível se recuperar de erros de OOM (falta de memória)
- Rust precisa conseguir executar, sem perda de desempenho, tudo o que C faz no SQLite
- Se algum entusiasta de Rust (
rustacean) achar que todas as condições acima já foram atendidas e que o SQLite deveria ser reescrito em Rust, a recomendação é entrar em contato diretamente com os desenvolvedores do SQLite para defender essa ideia
2 comentários
Comentários do Hacker News
if (i >= array_length) panic("index out of bounds")como código de defesa, mas esse código em si já foi bem testado pelo compilador Rust, então imagino que não seja necessário se preocupar com isso. Fico em dúvida se entendi essa lógica corretamenteget_unchecked(), o que permite manter segurança e ainda melhorar o desempenho documentação de get_uncheckedassert(). Para migrar para Rust, acho que seriam necessários pré-requisitos como: Rust passar mais tempo com poucas mudanças, ser adequado para escrever bibliotecas genéricas, funcionar também em sistemas embarcados sem OS, ter tooling para 100% de branch coverage, ter um mecanismo para tratar erros de OOM e substituir o papel de C sem perda de desempenhoif condition { panic(err) }não poderia ser usado como uma espécie de função assertA frase de que o C também representa um risco de segurança para o SQLite vale mesmo que os testes sejam muito bem escritos e os desenvolvedores sejam bastante experientes? Pode ser que o problema esteja na lógica e no processo de desenvolvimento, mas é difícil entender que a própria linguagem seja uma vulnerabilidade de segurança. Na verdade, quase não existem programas que não dependam de alguma infraestrutura escrita em C.