O servidor de linguagem para PostgreSQL (Postgres Language Server) é uma ferramenta de desenvolvimento moderna baseada no Language Server Protocol (LSP), projetada especificamente para o desenvolvimento de bancos de dados PostgreSQL. Desenvolvido e mantido pela comunidade Supabase, o projeto visa oferecer uma experiência de desenvolvimento comparável a IDEs modernas.
Este artigo explora em profundidade a arquitetura, os componentes fundamentais e os princípios de funcionamento deste servidor de linguagem.
Visão Geral da Arquitetura
O servidor adota uma arquitetura em camadas, podendo ser decomposto nas seguintes camadas principais:
Princípios de Design
- Agnosticismo de Transporte: Toda funcionalidade, além do protocolo LSP, também pode ser acessada via CLI, HTTP API ou como um módulo WebAssembly.
- Compatibilidade Sintática: Baseado no parser oficial do PostgreSQL,
libpg_query, garantindo 100% de compatibilidade sintática. - Análise Incremental: Reanalisa apenas as partes necessárias, otimizando a performance.
- Design Modular: Módulos funcionais independentes, facilitando extensão e manutenção.
Componentes Fundamentais
1. Núcleo do Servidor de Linguagem (pgt_lsp)
Este crate atua como o ponto de entrada principal do sistema, implementando o protocolo LSP e gerenciando o ciclo de vida do servidor.
pub struct GeradorServidor {
cancelamento: Arc<Notify>,
espaco_trabalho: Option<Arc<dyn EspacoTrabalho>>,
sessoes: Sessoes,
proxima_chave_sessao: AtomicU64,
parar_ao_desconectar: bool,
inicializado: Arc<AtomicBool>,
}
2. Gerenciamento do Espaço de Trabalho (pgt_workspace)
Este módulo é responsável por administrar o estado do ambiente de desenvolvimento, incluindo documentos, resultados de análise e informações de diagnóstico.
pub struct Aplicativo<'app> {
pub sistema_arquivos: RefDinamica<'app, dyn SistemaArquivos>,
pub espaco_trabalho: RefEspacoTrabalho<'app>,
pub console: &'app mut dyn Console,
}
3. Pipeline de Parsing e Processamento SQL
Responsável por transformar código SQL em representações estruturadas que podem ser analisadas semanticamente.
4. Sistema de Cache do Schema (pgt_schema_cache)
O cache do schema é um componente chave para a performence, mantendo uma representação completa do schema do banco de dados em memória.
| Tipo de Cache | Conteúdo Armazenado | Estratégia de Atualização |
|---|---|---|
| Cache de Tabelas | Nomes de tabelas, informações de colunas, restrições | Pré-carregamento na conexão, atualização periódica |
| Cache de Funções | Assinaturas de funções, tipos de parâmetros | Carregamento sob demanda, invalidação do cache |
| Cache de Tipos | Tipos customizados, enums | Carregamento no startup, atualização manual |
pub trait CacheEsquema {
fn obter_tabela(&self, esquema: &str, tabela: &str) -> Option<&Tabela>;
fn obter_funcao(&self, esquema: &str, funcao: &str) -> Option<&Funcao>;
fn obter_tipo(&self, esquema: &str, nome_tipo: &str) -> Option<&Tipo>;
fn atualizar(&mut self) -> Result<(), ErroCacheEsquema>;
}
5. Módulos de Serviços Funcionais
| Nome do Módulo | Descrição da Funcionalidade | Características Técnicas |
|---|---|---|
pgt_completions |
Autocompletar código | Sugestões inteligentes baseadas no contexto |
pgt_hover |
Exibição de informações ao passar o mouse | Informações de tipo e documentação em tempo real |
pgt_typecheck |
Verificação de tipos | Análise profunda utilizando EXPLAIN |
pgt_lint |
Verificação de código | Motor de regras baseado no Squawk |
Mecanismos de Funcionamento
Fluxo de Processamento de Documentos
Quando o cliente envia uma alteração de documento, o servidor executa o seguinte fluxo:
- Recepção do Documento: Via métodos LSP
didOpen/didChange/didClose. - Divisão em Declarações: Uso do
pgt_statement_splitterpara dividir o documento SQL em declarações independentes. - Análise Sintática: Utilização do
libpg_querypara parsing e geração da AST (Abstract Syntax Tree). - Análise Semântica: Verificação de tipos e validação semântica usando o cache do schema.
- Retorno dos Resultados: Geração de diagnósticos, sugestões de autocompletar, etc., para o cliente.
Estratégias de Otimização de Performance
| Estratégia | Implementação | Efeito |
|---|---|---|
| Análise Incremental | Reanalisa apenas a parte alterada | Redução de ~70% no tempo de análise |
| Mecanismo de Cache | Mantém informações do schema em memória | Evita consultas repetidas ao banco de dados |
| Carregamento Preguiçoso (Lazy Loading) | Carrega objetos do banco sob demanda | Reduz o tempo de inicialização |
Casos de Uso Práticos
1. Autocompletar Inteligente
-- Obter sugestões ao digitar
SELECT * FROM usu|
-- Sugestões: usuarios, cargos_usuario, sessoes_usuario, etc.
2. Detecção de Erros em Tempo Real
-- Detecção imediata de erros de sintaxe
SELECT * FROM tabela_inexistente;
-- Erro exibido imediatamente: tabela não existe
3. Verificação de Segurança de Tipos
-- Detecção de incompatibilidade de tipos
SELECT nome_usuario + 1 FROM usuarios;
-- Aviso: strings e números não podem ser somados diretamente
Extensibilidade e Customização
O sistema suporta regras de verificação customizáveis e uma arquitetura baseada em plugins para expansão de funcionalidades.
Otimização de Configuração
{
"pgt": {
"banco_dados": {
"host": "localhost",
"porta": 5432,
"banco": "meu_db",
"intervalo_atualizacao_cache": 300
},
"funcionalidades": {
"autocompletar": true,
"hover": true,
"diagnosticos": {
"habilitado": true,
"nivel": "warning"
}
}
}
}