Inscreva-se no 22º Firebird Developers Day, que será realizado online de 6 a 10 de outubro e tenha acesso aos descontos exclusivos para os participantes! |
|||||
|
|||||
Super Descontos no IBExpert
Todos os inscritos têm direito a preços especiais. Os descontos chegam a ultrapassar 60% sobre os valores normais! |
|||||
|
|||||
25% de desconto no uniGUIObtenha 25% de desconto na licença do uniGUI, o framework Web RAD para Delphi. |
|||||
10% de desconto no TeeChartObtenha 10% de desconto na licença do TeeChart e turbine suas aplicações com gráficos de cair o queixo! |
|||||
🎯 INSCREVA-SE AGORA!
Clique aqui e saiba tudo sobre a conferência mais importante sobre Firebird, em português! ⚡ Pagamento via PIX ou boleto. Garanta sua vaga! |
- por Denis Simonov
- 31.12.69
- Prefácio
- 1. Como tudo começou?
- 2. Como a funcionalidade do EDS pode ser estendida?
- 3. Extensão do mecanismo EDS
- 4. Plugin do tipo Provider
- 5. Implementação do provider
- 6. Quais implementações de provider existem?
- 7. Exemplos de funcionamento
- 8. Quais são os problemas com as implementações existentes?
- 9. Conclusão
Dedicatória:
Este material é patrocinado pela IBSurgeon https://www.ib-aid.com, fornecedora do HQbird (distribuição avançada do Firebird) e provedora de serviços de otimização de desempenho, migração e suporte técnico para o Firebird.
O material está licenciado sob a Public Documentation License https://www.firebirdsql.org/file/documentation/html/en/licenses/pdl/public-documentation-license.html
Prefácio
A partir do Firebird 3.0, você pode escrever várias extensões (plugins) para o Firebird SQL. A extensão mais simples é um procedimento/função externa (UDR) (veja o artigo Writing UDF in Pascal). UDR não é um plugin do Firebird no verdadeiro sentido da palavra, mas permite expandir significativamente as capacidades do SGBD. No entanto, a engine do Firebird tem mais capacidades para estender a funcionalidade com vários plugins.
O Firebird possui os seguintes tipos de plugins (constantes da classe IPluginManager
):
TYPE_PROVIDER
— providers;TYPE_AUTH_SERVER
— autenticação do lado do servidor;TYPE_AUTH_CLIENT
— autenticação do lado do cliente;TYPE_AUTH_USER_MANAGEMENT
— gerenciamento de usuários;TYPE_EXTERNAL_ENGINE
— engines externas. Elas são intermediárias entre o código de stored procedures/funções/triggers externos escritos em qualquer linguagem de programação e o Firebird. Exemplos de tais plugins sãoudr_engine
para interação com UDRs escritas em C++/Delphi oulibfbjava
para UDRs em Java;TYPE_TRACE
— plugin de rastreamento;TYPE_WIRE_CRYPT
— criptografia de tráfego de rede;TYPE_DB_CRYPT
— criptografia de banco de dados;TYPE_KEY_HOLDER
— detentor de chave para o plugin de criptografia de banco de dados;TYPE_REPLICATOR
— plugin replicador.
Neste artigo, veremos talvez o tipo de plugin mais complexo e interessante — os providers.
1. Como tudo começou?
Em 2023, comecei a trabalhar no desenvolvimento de um plugin para o HQbird para acessar bancos de dados de outros SGBDs (não Firebird) via EXECUTE STATEMENT ON EXTERNAL DATA SOURCE
(doravante EDS). Vlad Khorsun, desenvolvedor principal do Firebird, foi o consultor deste projeto.
Atualmente, no HQbird 2024, dois plugins estão disponíveis para Firebird 4.0 e 5.0: MySQLEngine
e ODBCEngine
. O MySQLEngine
foi criado primeiro e foi um cavalo de testes, os esforços principais foram focados no ODBCEngine
.
2. Como a funcionalidade do EDS pode ser estendida?
Existem duas opções:
- Estender o mecanismo EDS no núcleo do Firebird;
- Implementar um dos plugins do Firebird.
Serei direto com você - não importa qual caminho você escolha, você precisará mergulhar no código-fonte do Firebird. Simplesmente não há como contornar isso, porque você não encontrará essa informação documentada em nenhum outro lugar.
Primeiro, vamos considerar a primeira opção.
3. Extensão do mecanismo EDS
A implementação do EDS está localizada no diretório /src/jrd/extds/
. O próprio mecanismo EXECUTE STATEMENT
pode ser estendido a partir do Firebird 2.5. Para este propósito, os chamados EDS Providers foram inventados. Existem dois EDS providers no Firebird:
- Provider Firebird.
EXECUTE STATEMENT
é usado para acessar bancos de dados Firebird externos. - Provider Internal.
EXECUTE STATEMENT
é usado para acessar o banco de dados atual.
O código fornece a capacidade de adicionar novos providers (veja /src/jrd/extds/ExtDS.cpp
).
void Manager::addProvider(Provider* provider) { // TODO: if\when usage of user providers will be implemented, // need to check provider name for allowed chars (file system rules ?) // ... }
Embora esses providers estejam ocultos de você, é possível especificar explicitamente o provider a ser usado na string de conexão do EXECUTE STATEMENT ON EXTERNAL
.
SQL = 'SELECT MON$ATTACHMENT_NAME FROM MON$ATTACHMENTS'; FOR EXECUTE STATEMENT :SQL ON EXTERNAL 'Firebird::inet://localhost/ext_db' AS USER 'SYSDBA' PASSWORD 'masterkey' INTO NAME DO SUSPEND; -- returns ext_db FOR EXECUTE STATEMENT :SQL ON EXTERNAL 'Internal::inet://localhost/ext_db' AS USER 'SYSDBA' PASSWORD 'masterkey' INTO NAME DO SUSPEND; -- returns self_db (connection string is ignored)
Então, se planejamos estender o mecanismo do provider EDS ODBC, o EXECUTE STATEMENT ON EXTERNAL
ficaria assim:
FOR EXECUTE STATEMENT :SQL ON EXTERNAL 'ODBC::<conn_string>' AS USER 'user' PASSWORD 'secret' INTO NAME DO SUSPEND;
Isso é possível, mas existem desvantagens significativas nesta abordagem:
- Os dados do EDS Provider não são implementados como plugins (bibliotecas dinâmicas), o que significa que a modificação dos códigos-fonte da engine do Firebird é necessária.
- A solução é muito mais difícil de manter. A portabilidade manual da funcionalidade para novas versões é necessária, por exemplo, quando o Firebird 6.0 e versões posteriores forem lançados.
No entanto, tendo estudado as características do EXECUTE STATEMENT ON EXTERNAL
, pode-se notar um detalhe importante — o provider Firebird::
funciona através da API usual do Firebird, que é usada em aplicações, ou seja, o EDS atua como um cliente regular. E isso leva a outro pensamento: e se a API de uma fonte de dados externa for encapsulada na API usada para acessar o banco de dados Firebird?
4. Plugin do tipo Provider
Se você estudar cuidadosamente a documentação existente do Firebird, descobrirá que para esses propósitos, a partir do Firebird 3.0, um tipo especial de plugin chamado Provider (não confundir com EDS Provider) foi fornecido. Uma breve descrição do que é pode ser encontrada em doc/README.providers.html
.
Um plugin do tipo Provider fornece uma única API para interagir com o Firebird. Não importa se a interação física ocorre pela rede ou diretamente com a engine do Firebird.
Onde você encontra providers? Abra o firebird.conf
e você verá a seguinte linha lá:
Providers = Remote, Engine13, Loopback
O provider Remote
é responsável pela interação pela rede, e o Engine13
é o núcleo para interação com ODS13, usado diretamente ao trabalhar em modo embarcado. Os providers são tentados sequencialmente, e aquele que não se recusa a trabalhar é o ativo.
Talvez o exemplo mais conhecido que demonstra o trabalho dos providers seja a configuração:
Providers = Remote,Engine13,Engine12,Loopback
Esta configuração permite que o Firebird 5.0 funcione tanto com bancos de dados nativos ODS 13.1 quanto com bancos de dados ODS 12.0 (Firebird 3.0).
Se fornecermos uma API do Firebird para trabalhar com MySQL ou ODBC com a ajuda de nosso provider, a configuração pode ser a seguinte:
Providers = Remote,Engine13,ODBCEngine,Loopback Providers = ODBCEngine,Remote,Engine13,Loopback Providers = MySQLEngine,Remote,Engine13,Loopback
5. Implementação do provider
Agora vamos falar diretamente sobre a implementação de nossos próprios providers. Quais interfaces de API precisam ser implementadas?
IProvider
IAttachment
ITransaction
IStatement
IResultSet
IBlob
Por favor, note que nem todos os métodos dessas interfaces precisam ser implementados. Implementaremos apenas aqueles que são necessários para que o EXECUTE STATEMENT ON EXTERNAL
funcione, e faremos stubs para o resto.
Além do próprio provider, sua factory deve ser implementada. Você pode dar uma olhada na implementação da factory e no registro do provider usando o exemplo dos providers EngineXX em /src/jrd/jrd.cpp
(.h
).
namespace { template <class P> class Factory : public IPluginFactoryImpl<Factory<P>, CheckStatusWrapper> { public: // IPluginFactory implementation IPluginBase* createPlugin(CheckStatusWrapper* status, IPluginConfig* factoryParameter) { try { IPluginBase* p = new P(factoryParameter); p->addRef(); return p; } catch (const std::exception& e) { Firebird::setStatusError(status, e.what()); } return nullptr; } }; static Factory<OdbcEngine::OdbcProvider> engineFactory; } // namespace void registerEngine(IPluginManager* iPlugin) { UnloadDetectorHelper* module = getUnloadDetector(); module->setCleanup(shutdownBeforeUnload); module->setThreadDetach(threadDetach); iPlugin->registerPluginFactory(IPluginManager::TYPE_PROVIDER, ODBC_ENGINE_NAME, &engineFactory); module->registerMe(); } extern "C" FB_DLL_EXPORT void FB_PLUGIN_ENTRY_POINT(IMaster * master) { CachedMasterInterface::set(master); registerEngine(PluginManagerInterfacePtr()); }
5.1. Implementação da interface IProvider
O que precisa ser implementado na interface IProvider
(ODBCProvider
, MySQLProvider
)?
A interface IProvider
tem as seguintes funções:
IAttachment* attachDatabase(...)
(obrigatório)IAttachment* createDatabase(...)
(lançar erroisc_unavailable
)IService* attachServiceManager(...)
(lançar erroisc_unavailable
)void shutdown(...)
(não necessário)void setDbCryptCallback(...)
(não necessário)
Nesta interface, o principal é implementar o método IProvider::attachDatabase
. Ele deve fazer o seguinte:
- Analisar a string de conexão e extrair o prefixo nela
- Se o prefixo corresponder ao nosso prefixo, criar uma conexão com o BD, caso contrário, lançar o erro
isc_unavailable
. - O prefixo é necessário para determinar rapidamente se deve ser feita uma tentativa de conexão, que pode não ser barata, ou deixar o próximo provider tentar se conectar ao BD.
- Os seguintes prefixos são fornecidos:
- Para ODBC, é:
:odbc:
ouodbc://
- Para MySQL, é:
:mysql:
oumysql://
- Para ODBC, é:
Aqui está um pequeno fragmento desta função:
IAttachment* ODBCProvider::attachDatabase(CheckStatusWrapper* status, const char* fileName, unsigned dpbLength, const unsigned char* dpb) { debug_print_call(); status->clearException(); std::string dbFileName(fileName); auto poviderPos = dbFileName.find(":odbc:"); std::string connStr; if (poviderPos == 0) { connStr = dbFileName.substr(6); } else if (poviderPos = dbFileName.find("odbc://"); poviderPos == 0) { connStr = dbFileName.substr(7); } else { // It is important to set the error with the status isc_unavailable // to pass control to the next provider const ISC_STATUS statusVector[] = { isc_arg_gds, isc_unavailable, isc_arg_end }; status->setErrors(statusVector); return nullptr; } .... }
As funções IProvider::createDatabase
e IProvider::attachServiceManager
não são necessárias para o funcionamento do EDS, mas ainda precisam ser implementadas e o erro isc_unavailable
lançado nelas. Isso é necessário para que o trabalho das cadeias de providers não seja interrompido.
5.2. Implementação da interface IAttachment
Os seguintes métodos devem ser implementados na interface IAttachment
(MySQLAttachment
, ODBCAttachment
):
void getInfo(status, ...)
ITransaction* startTransaction(status, ...)
IBlob* createBlob(status, ...)
IBlob* openBlob(status, ...)
IStatement* prepare(status, ...)
ITransaction* execute(status, ...)
IResultSet* openCursor(status, ...)
— este método nunca é chamado no EDS, pois ele sempre executa apenas statements preparadosvoid detach(status)
void dropDatabase(status)
— em teoria, este método não é necessário, mas às vezes o fluxo de controle entra nele, então simplesmente chamamosIProvider::detach
nele.
5.2.1. Implementação de IAttachment::getInfo
Neste método, você precisa retornar uma resposta para solicitações com as tags isc_info_db_sql_dialect
e fb_info_features
. A tag fb_info_features
é usada para retornar a funcionalidade suportada do seu provider. Os valores possíveis são descritos pela seguinte enumeração:
enum info_features // response to fb_info_features { fb_feature_multi_statements = 1, // Multiple prepared statements in single attachment fb_feature_multi_transactions= 2, // Multiple concurrent transaction in single attachment fb_feature_named_parameters= 3, // Query parameters can be named fb_feature_session_reset= 4, // ALTER SESSION RESET is supported fb_feature_read_consistency= 5, // Read consistency TIL is supported fb_feature_statement_timeout= 6, // Statement timeout is supported fb_feature_statement_long_life = 7, // Prepared statements are not dropped on transaction end fb_feature_max // Not really a feature. Keep this last. };
5.2.2. Implementação de IAttachment::startTransacxconteudoxconteudo
- por Simonov Denis
- 31.12.69
- por ChatGPT o1 e Carlos H. Cantu
- 31.12.69
- por Denis Simonov
- 31.12.69
- por IBSurgeon
- 31.12.69
- por Carlos H. Cantu
- 31.12.69
- por Carlos H. Cantu
- 31.12.69
Quando se tem apenas um banco de dados em um servidor Firebird, saber quantos usuários estão conectados é uma tarefa simples, bastando, por exemplo, dar uma olhada na tabela mon$attachments.
Mas e se houver dezenas de bancos de dados em um mesmo servidor? Como saber quantos usuários simultâneos estão conectados no FIREBIRD como um todo, e não especificamente em um banco de dados?
Obviamente seria possível conectar em cada banco, rodar o select na tabela mon$statements e ir somando os resultados. Ou mesmo usar o fb_lock_print e observar o owners, mas também teria que ser feito em cada base e ir somando os resultados.
Para quem usa a arquitetura Classic, o trabalho é um pouco mais fácil, bastando contar o número de processos do Firebird em execução, já que cada conexão é atendida por um processo do Classic. Mas no SuperServer e SuperClassic as conexões são atendidas por threads de um mesmo processo do Firebird, portanto, essa solução não funciona para elas.
Felizmente, a solução é mais fácil do que parece! Basta executar o comando abaixo, que usa o utilitário fbsvcmgr do Firebird, para obter a informação de quantos usuários estão conectados no Firebird e também quantos bancos de dados estão sendo acessados naquele momento!
fbsvcmgr.exe localhost:service_mgr user sysdba password masterkey info_svr_db_info
Exemplo:
C:\firebird3>fbsvcmgr.exe localhost:service_mgr user sysdba password masterkey info_svr_db_info Databases: Number of attachments: 3 Number of databases: 2 Database in use: C:\TEMP\TESTE.FDB Database in use: C:\TEMP\TESTE-2.FDB
No exemplo acima, temos dois bancos de dados sendo usados, e 3 usuários/conexões (um dos bancos tem 2 usuários conectados, e o outro tem 1 usuário conectado).
Conexões embedded não entram na conta.
Autor: Carlos H. Cantu
- por Carlos H. Cantu
- 31.12.69
- por Renato Felix de Almeida
- 31.12.69
- por Carlos H. Cantu
- 31.12.69
- por Carlos H. Cantu
- 31.12.69
- Diversos
- 31.12.69
Principais Recursos
- Consultas SQL: Execute consultas SQL em bancos de dados Firebird.
- Análise de Esquema: Obtenha informações detalhadas sobre tabelas, colunas e relacionamentos.
- Gerenciamento de Banco de Dados: Realize operações de backup, restauração e validação.
- Análise de Desempenho: Analise o desempenho de consultas e sugira otimizações.
- Múltiplos Transportes: Suporta transportes STDIO, SSE (Server-Sent Events) e HTTP com suporte a streaming.
- Suporte a Protocolos Modernos: Suporte completo para MCP Streamable HTTP (2025-03-26) e o legado SSE.
- Servidor Unificado: Detecção automática de protocolo e compatibilidade com versões anteriores.
- Integração com o Claude: Funciona perfeitamente com o Claude Desktop e outros clientes MCP.
- Integração com o VSCode: Funciona com o GitHub Copilot no Visual Studio Code.
- Gerenciamento de Sessão: Manuseio robusto de sessões com limpeza automática e tempos limite configuráveis.
- Segurança: Inclui validação de consultas SQL e opções de configuração de segurança.
