10 pontos por GN⁺ 2025-10-16 | 2 comentários | Compartilhar no WhatsApp
  • 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

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

 
GN⁺ 2025-10-16
Comentários do Hacker News
  • Mesmo que linguagens de programação seguras não existissem nos primeiros 10 anos, acho que reimplementar o SQLite em Go ou Rust hoje provavelmente criaria mais bugs do que corrigiria, e talvez ainda ficasse mais lento. Se um código praticamente sem bugs já foi concluído após uma quantidade enorme de tempo e testes, então, em um cenário de baixa taxa de mudança, pouco importa em que linguagem ele foi escrito. A esse ponto, tanto faz até se for assembly
    • O ponto de que “quando a taxa de mudança é baixa, há menos problemas” já foi explicado no Google Security Blog. A ideia ali é que a maioria dos problemas de segurança de memória surge em código novo, e que o código fica mais seguro com o tempo link relacionado
    • Do lado de Rust, projetos como o Turso estão se movendo de forma bastante ativa Turso
    • Também há quem defenda que os utilitários básicos do Linux não deveriam ser reescritos em Rust. São softwares usados há décadas e dos quais a maioria dos bugs já foi eliminada, então não haveria motivo para reescrevê-los
    • Acho que Zig é bom para substituir parte de código em C. Ele também se integra bem com Python e binários C existentes. A filosofia do Go é boa, mas havia a limitação de que ele é difícil de otimizar e exige desenvolvedores muito competentes. Rust também poderia ser usado, mas foi muito mais fácil continuar usando C existente e introduzir Zig gradualmente. Não conseguimos eliminar completamente os bugs do código em C, mas sentimos que migrar para Rust é inviável na prática
    • Já existe uma implementação do sqlite portada para Go cznic/sqlite
  • Além do fato de que C era a melhor escolha quando o SQLite foi desenvolvido, e das vantagens que ele ainda tem hoje, acho que não existe nenhum motivo especial para reescrever o SQLite em outra linguagem. Qualquer pessoa pode implementar um banco de dados SQL leve, então é possível criar uma nova implementação em Rust, C++, Go, Lisp ou qualquer linguagem desejada. Não faz sentido jogar fora uma implementação existente em C que funciona bem, nem forçar os desenvolvedores que mantêm o SQLite em C há mais de 25 anos a aprender uma nova linguagem e refazer tudo do zero
    • Em muitos fandoms de linguagens há uma tendência de impor aos outros aquilo que se quer, e sinto que a adoção de linguagens virou, em certa medida, uma disputa de soma zero. Se um projeto está sendo desenvolvido em uma linguagem específica, só o fato de não usar essa linguagem já leva a questionamentos sobre a necessidade de outras. Na prática, as opções são muito mais variadas, e mesmo se fosse reescrito, haveria Rust, Go, D, Lisp, Julia e várias outras linguagens na mesa
    • Na prática, os desenvolvedores do SQLite estão abertos a um rewrite em Rust. Existe até a possibilidade de refazê-lo se Rust atender aos pré-requisitos necessários. Também há a orientação de que, se você é fã de Rust, entre em contato diretamente com os desenvolvedores do SQLite
    • Já existem projetos implementados em Rust, como rqlite e turso
    • Há um adaptador escrito em Go, então é possível usar sqlite em golang sem cgo. Agora, o sqlite não é apenas uma biblioteca em C, mas também um formato de arquivo de banco de dados. Acho que no futuro pode surgir uma implementação pure Rust e, algum dia, ela pode até virar a implementação principal
    • Fico frustrado com a moda atual de tratar qualquer tecnologia com mais de 5 anos como obsoleta. Acho que é preciso haver mais respeito por tecnologias refinadas ao longo de muito tempo
  • Linguagens seguras geram branches extras para checagem de limites em acesso a arrays, mas em código correto esses branches nunca são executados. Ou seja, é difícil atingir 100% de branch testing, e isso se relaciona com a estratégia de qualidade do SQLite. Achei interessante ouvir essa lógica nova
    • Se é possível ter certeza de que aquele branch do código nunca será executado, então não seria desnecessário testá-lo? Dá a sensação de sacrificar segurança para conseguir 100% de cobertura de testes
    • Em linguagens seguras, o compilador adiciona automaticamente algo como 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 corretamente
    • Para um especialista como o Dr. Hipp e um projeto como o sqlite, acho que esse argumento também faz sentido
    • Em Rust, também é possível acessar sem bounds check usando algo como get_unchecked(), o que permite manter segurança e ainda melhorar o desempenho documentação de get_unchecked
    • Fico pensando se não daria para reduzir esse problema tornando opcional a exigência de cobertura para branches que condicionalmente terminam em panic
  • O SQLite deixa em aberto a possibilidade de um dia ser reescrito em Rust, enquanto Go parece improvável por causa de restrições relacionadas a assert(). 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 desempenho
    • Desde o Rust 1.0, a linguagem vem evoluindo de forma compatível há mais de 10 anos. Há uma diferença entre quem quer que a linguagem pare totalmente de mudar e quem aceita que ela continue mudando. O desenvolvimento de bibliotecas genéricas já foi comprovado, e o suporte a embarcados sem OS é claramente viável. Sobre branch coverage eu não sei bem, porque não sou especialista, mas há trabalho nessa área em iniciativas como o Ferrocene. A linguagem Rust em si não faz alocação de memória, então o tratamento de OOM pode ser decidido no nível da biblioteca padrão. Já a questão de desempenho pode variar conforme a definição usada
    • Fico pensando se, em Go, if condition { panic(err) } não poderia ser usado como uma espécie de função assert
  • A maioria dos argumentos parece plausível à primeira vista, mas, olhando com cuidado, não é perfeita. Acho que bastaria explicar claramente por que C foi escolhido por volta de 2000, e hoje seria só aceitar a existência de uma base de código bem refinada. Alguns argumentos adicionais podem ser contestados
    • Gostaria de ouvir especificamente quais argumentos seriam contestáveis
    • Os argumentos apresentados servem para manter a base de código antiga, mas, se a intenção é convencer novos desenvolvedores a adotar C em vez de uma linguagem mais complexa, seria preciso ter mais justificativas
    • (Esse documento foi escrito em 2017)
    • Presumo que tenham escrito um documento longo e detalhado para responder às inúmeras perguntas do tipo “por que não reescrever em X” ao longo dos anos
  • Já há anos existe um projeto que traduz o SQLite automaticamente para Go e continua sendo distribuído ativamente modernc.org/sqlite. Ele também passa bem pelo mesmo test suite. Porém, a versão em Go é bem mais lenta, e muitas vezes a conveniência de um port nativo em Go importa mais do que a velocidade em si. No fim, acho que, em vez de reescrever o SQLite em Go, Rust, Zig, Nim, Swift etc., o caminho mais realista é traduzi-lo automaticamente a partir de C
    • Ouvi dizer que ele passa no test suite público, mas que o SQLite também tem um test suite interno bem mais rigoroso
    • Passar no test suite não significa ausência de bugs; ainda podem existir novos edge cases ou problemas de desempenho
  • “Por que o SQLite foi desenvolvido em C?” está bem explicado na documentação oficial, mas quando ouço a pergunta “por que não Rust?”, na verdade penso primeiro em “por que teria que ser Rust?”
    • Há quem diga que isso é por causa do título sobreposto à discussão
    • Já existe um projeto de rewrite em Rust: tursodatabase/turso e também houve discussão sobre o “porquê” neste post de blog
    • O tom é de que também daria para perguntar por que o SQLite foi escrito em C e não em BASIC
  • Quanto mais vejo textos sobre programação, uso de software e rewrites, mais acho que um problema central é que, quando o rewrite tem apenas “equivalência funcional” como objetivo, ele tende a deixar de fora inúmeras exceções e patches acumulados ao longo do tempo. No fim, o software volta a quebrar, ou coisas que antes funcionavam bem deixam de funcionar. Esse tipo de rewrite exige ênfase e cautela suficientes, e acho difícil reproduzir 100% de tudo. Bibliotecas importantes como SDL também entram nisso. Dá para prever releases quebrando repetidamente e reclamações de usuários. Acho que C vai continuar vivo por muito tempo mesmo depois de Rust se tornar dominante. Rewrite não deveria ser a escolha padrão
  • Acho mais interessante o fato de o DuckDB ter sido escrito em C++ em vez de Rust. DuckDB é um projeto novo, surgido em 2019, então poderia perfeitamente ter adotado Rust, mas no fim escolheu C++. Ele é novo e sua base de código também é muito menor que a do SQLite
    • Ouvi dizer que a equipe do DuckDB escolheu C++ porque tinha confiança nessa linguagem e confiava na autovetorização dos compiladores. Na época (2019), Rust não tinha um suporte claro a SIMD de alto nível. Eles também não queriam manter código SIMD escrito manualmente
    • Se o objetivo for desempenho máximo, acho que C++ consegue gerar binários mais rápidos com menos código. O C++ moderno também tem bastante segurança em tempo de compilação, então parece adequado para código de banco de dados
    • Acho que, se for escrito em C++ moderno, tudo bem
  • Já houve muitos debates antes sobre reescrever o SQLite em 2021, em 2018
    • O comentário do tptacek é interessante: em documentos anteriores havia um parágrafo sobre security, mas ele desapareceu na versão mais recente. C é claramente uma vulnerabilidade de segurança também para o SQLite. Na versão anterior, havia a explicação de que “SQLite não é uma biblioteca tão sensível do ponto de vista de segurança”. Dizia-se que executar SQL não confiável já é, por si só, um problema maior; quanto à importação de arquivos externos, os problemas seriam evitados com código defensivo e testes rigorosos, além da existência de rotinas de validação prévia documento arquivado da web de 2021
 
aer0700 2025-10-16

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