Firebird Documentation IndexFirebird 3.0.6 Release Notes → Changes in the Firebird Engine
Firebird Home Firebird Home Prev: Compatibility with Older VersionsFirebird Documentation IndexUp: Firebird 3.0.6 Release NotesNext: Optimizer Improvements

Chapter 3: Changes in the Firebird Engine

Table of Contents

Remodelled Architecture
Optimizer Improvements
Other Optimizations
Remote Interface/Network Protocol
Miscellaneous Improvements

In Firebird 3, the remodelling of the architecture that was begun in v.2.5 was completed with the implementation of full SMP support for the Superserver model. In the new scheme, it is possible to configure the execution model individually per database.

Remodelled Architecture

Dmitry Yemanov

The remodelled architecture integrates the core engine for Classic/Superclassic, Superserver and embedded models in a common binary. The cache and lock behaviours that distinguish the execution models are now determined externally by the settings in the new configuration parameter ServerMode. The connection method is determined by the order and content of another parameter, Providers and the connection protocol that is deduced at run-time from the connection string supplied when a client requests an attachment.

The parameters for configuring the architecture are specified globally (in firebird.conf). Providers can be overridden specifically for a database (in databases.conf).


databases.conf is the old aliases.conf from previous versions, with a new name. In Firebird 3, the role of this file involves (potentially) much more than being just a lookup for database file paths. For more details about what can be configured at database level, refer to the chapter Configuration Additions and Changes.

Server Modes

Table 3.1. Matrix of Server Modes

ServerMode Synonym Resource Model Provider[s]
1 Only if exclusive access is available
Super ThreadedDedicated Database is opened exclusively by a single server process. User attachments are processed by threads launched from the common pool and all share a single database page cache inside the process. This is the installation default.
Superclassic ThreadedShared Databases are opened by a single server process, but access is not exclusive: an embedded process can open the same database concurrently. User attachments are processed by threads launched from the common pool, each having its own database page cache.
Classic MultiProcess A separate process is started for each attachment to server. A database may be opened by multiple Classic processes, including local processes for embedded access. Each process has its own database page cache.


The providers are more or less what we traditionally thought of as the methods used to connect a client to a server, that is to say, across a network, host-locally, via the local loopback (localhost) or by a more direct local connection (the old on POSIX, now implemented as the plug-in library; on Windows, engine12.dll; on MacOSX, engine12.dylib).

  • In firebird.conf, all are available by default, as follows:

    #Providers = Remote,Engine12,Loopback
  • In databases.conf, one or more providers can be blocked by pasting the line from firebird.conf, uncommenting it, and deleting the unwanted provider[s].

The Providers Architecture

Alex Peshkov

Although a key feature of Firebird 3, the Providers architecture is not new. Providers existed historically in Firebird's predecessors and, though well hidden, are present in all previous versions of Firebird. They were introduced originally to deal with a task that has been performed since then by interface layers such as ODBC, ADO, BDE and the like, to enable access to different database engines using a single external interface.

Subsequently, this Providers architecture (known then as Open Systems Relational Interface, OSRI) also showed itself as very efficient for supporting a mix of old and new database formats—different major on-disk structure versions—on a single server having mixed connections to local and remote databases.

The providers implemented in Firebird 3 make it possible to support all these modes (remote connections, databases with differing ODS, foreign engines) as well as chaining providers. Chaining is a term for a situation where a provider is using a callback to the standard API when performing an operation on a database.

The Components

The main element of the Providers architecture is the y-valve. On the initial attach or create database call y-valve scans the list of known providers and calls them one by one until one of them completes the requested operation successfully. For a connection that is already established, the appropriate provider is called at once with almost zero overhead.

Let's take a look at some samples of y-valve operation when it selects the appropriate provider at the attach stage. These use the default configuration, which contains three providers:

  • Remote (establish network connection)

  • Engine12 (main database engine)

  • Loopback (force network connection to the local server for <database name> without an explicit network protocol being supplied).

The typical client configuration works this way: when one attaches to a database called RemoteHost:dbname (TCP/IP syntax) or \\RemoteHost\dbname (NetBios) the Remote provider detects explicit network protocol syntax and, finding it first in the Provider list, redirects the call to RemoteHost.

When <database name> does not contain a network protocol but just the database name, the Remote provider rejects it and the Engine12 provider comes to the fore and tries to open the named database file. If it succeeds, we get an embedded connection to the database.


A special embedded library is no longer required. To make the embedded connection, the standard client loads the appropriate provider and becomes an embedded server.

Failure Response

But what happens if the engine returns an error on an attempt to attach to a database?

  • If the database file to be attached to does not exist there is no interest at all.

  • An embedded connection may fail if the user attaching to it does not have enough rights to open the database file. That would be the normal case if the database was not created by that user in embedded mode or if he was not explicitly given OS rights for embedded access to databases on that box.


    Setting access rights in such a manner is a requirement for correct Superserver operation.

  • After a failure of Engine12 to access the database, the Loopback provider is attempted for an attach. It is not very different to Remote except that it tries to access the named database <dbname> on a server running a TCP/IP local loopback.

    On Windows, XNET is tried first, then TCP/IP loopback (with localhost: prepended to <dbname>), then Named Pipes (NetBEUI) loopback (with \\.\ prepended). The server may be started with XNET (or any other protocol) disabled, so we try all the options. On POSIX only TCP/IP protocol is supported, other options are not available

    If the attachment succeeds, a remote-like connection is established with the database even though it is located on the local machine.

Other Providers

Use of providers is not limited to the three standard ones. Firebird 3 does not support pre-ODS 12 databases. Removing support for old formats from the engine helps to simplify its code and gain a little speed. Taking into account that this speed gain sometimes takes place in performance-critical places, like searching a key in an index block, avoiding old code and related branches really does make Firebird fly faster.

Nevertheless, the Providers architecture does make it possible to access old databases when changing to a higher version of Firebird. A suitable provider may be considered for inclusion in a later sub-release.

Custom Providers

A strong feature of the Providers architecture is ability for the deployer to add his own providers to the server, the client, or both.

So what else might be wanted on a client, other than a remote connection? Recall Provider chaining that was mentioned earlier. Imagine a case where a database is accessed via very slow network connection, say something like 3G or, worse, GPRS. What comes to mind as a way to speed it up is to cache on the client some big tables that rarely change. Such systems were actually implemented but, to do it, one had to rename fbclient to something arbitrary and load it into its own library called fbclient, thus making it possible to use standard tools to access the database at the same time as caching required tables. It works but, as a solution, it is clearly not ideal.

With the Providers architecture, instead of renaming libraries, one just adds a local caching provider which can use any method to detect connections to it (something like a cache@ prefix at the beginning of the database name, or whatever else you choose).

In this example, when the database name cache@RemoteHost:dbname is used, the caching provider accepts the connection and invokes the y-valve once more with the traditional database name RemoteHost:dbname. When the user later performs any call to his database, the caching provider gets control of it before Remote does and, for a locally cached table, can forestall calls to the remote server.

Use of chaining allows a lot of other useful things to be implemented. An example might be MySQL-style replication at statement level without the need for triggers: just repeat the same calls for the replication host, perhaps whena transaction is committed. In this case, the chaining provider would be installed on the server, not the client, and no modification of the command line would be needed.


That said, statement-level replication is very questionable feature.

To avoid cycling when performing a callback to y-valve at attach time, such a provider can modify the list of providers using the isc_dpb_config parameter in the DPB. The same technique may be used at the client, too.

For details, see the Configuration Additions and Changes chapter.

The ability to access foreign database engines using providers should not be overlooked, either. It might seem strange to consider this, given the number of tools available for this sort of task. Think about the ability to access other Firebird databases using EXECUTE STATEMENT, that became available in Firebird 2.5. With a provider to ODBC or other common tool to access various data sources it is within reach to use EXECUTE STATEMENT to get direct access from procedures and triggers, to data from any database having a driver for the chosen access tool. It is even possible to have a provider to access some particular type of foreign database engine if there is some reason to want to avoid the ODBC layer.

Providers Q & A

Q. Interfaces and providers are probably very good, but I have an old task written using plain API functions and for a lot of reasons I can't rewrite it in the near future. Does it mean I will have problems migrating to Firebird 3?

  • A. Definitely no problems. The old API is supported for backward compatibility in Firebird 3 and will be supported in future versions as long as people need it.

And what about performance when using the old API?

  • A. The functional API is implemented as a very thin layer over interfaces. Code in most cases is trivial: convert passed handles to pointers to interfaces—hitherto referred to as handle validation—and invoke the appropriate function from the interface.

    Functions that execute an SQL operation and fetch data from it are one place where coding is a little more complex, involving the SQLDA construct. The data moves related to the SQLDA have always created an overhead. The logic between the new and old APIs does not add significantly to that old overhead.

Connection String and Protocols

Connection string refers to the local or remote path to the database to which a client requests an attachment (connection). The syntax of the connection string determines the transport protocol by which clients and the server communicate. The legacy syntaxes for the available protocols, supported by all Firebird versions, are as follows:

For TCP/IP (aka INET) protocol:
  <host> [ / <port>] : <database file path or alias>
For named pipes (aka NetBEUI, aka WNET) protocol:
  \\ <host> [ @ <port>] \ <database file path or alias>
For local connections, simply:
  <database file path or alias>

Local connection is implied if <host> is omitted. Depending on settings, platform and Firebird version, it could be performed via either the embedded engine, XNET (shared memory) protocol or TCP/IP localhost loopback.


Connect via TCP/IP using database name:\db\mydb.fdb
Connect via TCP/IP using database alias:
Connect via TCP/IP using non-default port 3051:\db\mydb.fdb
Connect via TCP/IP using non-default service name:\db\mydb.fdb
Connect via named pipes (Windows only):
Local connection:

URL-Style Connection Strings

Firebird 3.0 introduces an additional, generalized, URL-style syntax for connection strings. The pattern is:

  [ <protocol> : // [ <host> [ : <port> ] ] ] / <database file path or alias>

  <protocol> ::= INET | WNET | XNET

INET resolves to TCP/IP, WNET to Named Pipes, while XNET surfaces the old Windows local protocol (shared memory).

Connect via TCP/IP using database name:
Connect via TCP/IP using database alias:
Connect via TCP/IP using non-default port 3051:
Connect via TCP/IP using non-default service name:
Connect via named pipes:
Loopback connection via TCP/IP:
Loopback connection via named pipes:
Local connection via shared memory:
Local (embedded by default) connection:

Local connection is implied if <host> is omitted. Depending on settings, platform and Firebird version, it could be performed via either the embedded engine, XNET (shared memory) protocol or TCP/IP localhost loopback.

On the server side, the provider configuration is in the default order Remote, Engine12, Loopback. If the Remote provider fails to match the connection string because the protocol or host parts are missing, then Engine12, the embedded engine, handles it as a hostless connection. To connect locally using a specific transport protocol, it is necessary to specify that protocol:

  inet://<database file path or alias>
  wnet://<database file path or alias>
  xnet://<database file path or alias>


WNET (named pipes) and XNET (shared memory) protocols are available only on Windows.


Alex Peshkov

From version 3 onward, Firebird's architecture supports plug-ins. For a number of predefined points in the Firebird code, a developer can write his own fragment of code for execution when needed.

A plug-in is not necessarily one written by a third party: Firebird has a number of intrinsic plug-ins. Even some core parts of Firebird are implemented as plug-ins.

What is a Plug-In?

The term plug-in is used to name related but different things:

  • a dynamic library, containing code to be loaded as a plug-in (often called a plug-in module) and stored in the $FIREBIRD/plugins directory;

  • code implementing a plug-in. That is slightly different from the library, since a single dynamic library may contain code for more than one plug-in;

  • a plug-in's factory: an object created by that code (pure virtual C++ class), creating instances of the plug-in at Firebird's request;

  • an instance of the plug-in, created by its factory.

Plug-In Types

Firebird's plug-in architecture makes it possible to create plug-ins of predefined types. Each version of Firebird will have a fixed set of supported plug-in types. To add a further type, the first requirement is to modify the Firebird code. Our plug-in architecture facilitates both adding new types of plug-ins and simplifying the coding of the plug-in along generic lines.

To be able to implement a plug-in, say, for encrypting a database on the disk, the Firebird code has to be prepared for it: it must have a point from which the plug-in is called.

The set of plug-in types implemented in Firebird 3 comprises:

user authentication related:
  • AuthServer (validates user's credentials on server when logins are used)

  • AuthClient (prepares credentials to be passed over the wire)

  • AuthUserManagement (maintains a list of users on a server in a format known to AuthServer)


Controls the use of various engines, see External Engines.


The Trace plug-in was introduced in Firebird 2.5, but the way it interacts with the engine was changed in Firebird 3 to accord with the new generic rules.


encrypting plug-ins are for

  • network (WireCrypt)

  • disk (DbCrypt)

  • a helper plug-in (KeyHolder), used to help maintain the secret key(s) for DbCrypt


Firebird 3 supports providers as a plug-in type.

Technical Details

Plug-ins use a set of special Firebird interfaces. All plug-in-specific interfaces are reference counted, thus putting their lifetime under specific control. Interfaces are declared in the include file plug-in.h. A simple example for writing a plug-in module can be found in DbCrypt_example.


The example does not perform any actual encryption, it is just a sample of how to write a plug-in. Complete instructions for writing plug-ins are not in scope for this document.

Features of a Plug-In

A short list of plug-in features:

  • You can write a plug-in in any language that supports pure virtual interfaces. Interface declarations will need to be written for your language if they are missing.

  • As with UDFs, you are free to add any reasonable code to your plug-in—with emphasis on reasonable. For example, prompting for user input at the server's console from a plug-in is hardly reasonable!

  • Calling the Firebird API from your plug-in is OK, if needed. For example, the default authentication server and user manager use a Firebird database to store accounts.

  • Firebird provides a set of interfaces to help with configuring your plug-ins. It is not obligatory to use them, since the plug-in code is generic and can employ any useful method for capturing configuration information. However, using the standard tools provides commonality with the established configuration style and should save the additional effort of rolling your own and documenting it separately.

Configuring Plug-ins

Configuration of plug-ins has two parts:

  1. The engine has to be instructed what plug-ins it should load

  2. The plug-ins themselves sometimes need some configuration.

The plug-ins to be loaded for each plug-in type are defined in the main configuration file, firebird.conf, usually with defaults. The ones defined in Firebird 3 are discussed in the chapter entitled Configuration Additions and Changes. In summary, the set that provides normal operation in the server, client and embedded cases consists of:

  • AuthServer = Srp, Win_Sspi

  • AuthClient = Srp, Win_Sspi, Legacy_Auth

  • UserManager = Srp

  • TracePlugin = fbtrace

  • Providers = Remote,Engine12,Loopback

  • WireCryptPlugin = Arc4


If you want to add other plug-ins, they must be cited in firebird.conf. Apart from other considerations, this requirement acts as a security measure to avoid loading unknown code.

Taking the entry TracePlugin = fbtrace as an example, what does the value fbtrace signify? In a trivial case, it can indicate the name of a dynamic library but the precise answer is more complicated.

As mentioned earlier, a single plug-in module may implement more than one plug-in. In addition, a single plug-in may have more than one configuration at once, with a separate plug-in factory created for each configuration. Each of these three object contexts (module | implementation | factory) has its own name:

  • The name of a module is the file name of a dynamic library

  • The name of a plug-in implementation is the one given to it by the developer of the plug-in. It is hard-coded inside the module.

  • The name of a factory is, by default, the same as the name of the plug-in implementation's name. It is the factory name which is actually used in firebird.conf.

In a typical trivial case, a module contains one plug-in that works with just one configuration and all three names are equal, and no more configuration is needed. An example would be | Engine12.dll | Engine12.dylib, that contains the implementation of the embedded provider Engine12. Nothing other than the record Providers = Engine12 is needed to load it.

For something more complex a file will help you to set up the plug-in factories precisely.


The file $(root)/plugins.conf has two types of records: config and plugin.

the plugin record is a set of rules for loading land activating the plug-in. Its format is:

Plugin = PlugName ## this is the name to be referenced in firebird.conf
    Module = LibName ## name of dynamic library
    RegisterName = RegName ## name given to plug-in by its developer
    Config = ConfName ## name of config record to be used
    ConfigFile = ConfFile ## name of a file that contains plug-in's configuration

When plug-in PlugName is needed, Firebird loads the library LibName and locates the plug-in registered with the name RegName. The configuration values from the config record ConfName or the config file ConfFile are passed to the library.


If both ConfName and ConfFile are given, then the config record will be used.

If both parameters are missing, the default PlugName is used; except that if the ConfigFile is present and its name is the same as the module's dynamic library but with a .conf extension, it will be used.

The ConfigFile is expected to use the format Key=Value, in line with other Firebird configuration files.

For the plug-in configuration record the same format is used:

Config = ConfName
    Key1 = Value1
    Key2 = Value2

A Sample Setup

Suppose you have a server for which some clients trust the wire encryption from one vendor and others prefer a different one. They have different licences for the appropriate client components but both vendors use the name BestCrypt for their products.

The situation would require renaming the libraries to, say, WC1 and WC2, since there cannot be two files in the same directory with the same name. Now, the modules stop loading automatically because neither is called BestCrypt any longer.

To fix the problem, plug-ins.conf should contain something like this:

Plugin = WC1
    RegisterName = BestCrypt
Plugin = WC2
    RegisterName = BestCrypt

The module names will be automatically set to WC1 and WC2 and found. You can add any configuration info that the plug-ins need.

Remember to modify firebird.conf to enable both plug-ins for the WireCryptPlugin parameter:

WireCryptPlugin = WC1, WC2

The server will now select appropriate plug-in automatically to talk to the client.

Another sample is distributed with Firebird, in $(root)/plugins.conf, configuring one of the standard plug-ins, UDR. Because it was written to a use non-default configuration, the module name and one configuration parameter are supplied explicitly.

Plug-Ins Q & A

Q. There are plug-ins named Remote, Loopback, Arc4 in the default configuration, but no libraries with such names. How do they work?

  • A. They are built-in plug-ins, built into the fbclient library, and thus always present. Their existence is due to the old ability to distribute the Firebird client for Windows as a single dll. The feature is retained for cases where the standard set of plug-ins is used.

Q. What do the names of Srp and Arc4 plug-ins mean?

  • A. Srp implements the Secure Remote Passwords protocol, the default way of authenticating users in Firebird 3. Its effective password length is 20 bytes, resistant to most attacks (including man in the middle) and works without requiring any key exchange between client and server to work.

    Arc4 means Alleged RC4 - an implementation of RC4 cypher. Its advantage is that it can generate a unique, cryptographically strong key on both client and server that is impossible to guess by capturing data transferred over the wire during password validation by SRP.

    The key is used after the SRP handshake by Arc4, which makes wire encryption secure without need to exchange any keys between client and server explicitly.

Q. What do Win_Sspi and Legacy_Auth mean?

  • A. Windows SSPI has been in use since Firebird 2.1 for Windows trusted user authentication. Legacy_Auth is a compatibility plug-in to enable connection by the Firebird 3 client to older servers. It is enabled by default in the client.

    And Yes, it still transfers almost plain passwords over the wire, for compatibility.

    On the server it works with security3.fdb just as with a security database from Firebird 2.5 It should be avoided except in situations where you understand well what you are sacrificing.

    To use Legacy_Auth on the server you will need to avert network traffic encryption in firebird.conf by reducing the default Required setting for the WireCrypt parameter, either

      WireCrypt = Enabled


      WireCrypt = Disabled

Q. How can I find out what the standard Authentication and User Manager plug-ins are?

  • They are listed in firebird.conf.

External Engines

Adriano dos Santos Fernandes

The UDR (User Defined Routines) engine adds a layer on top of the FirebirdExternal engine interface with the purpose of

  • establishing a way to hook external modules into the server and make them available for use

  • creating an API so that external modules can register their available routines

  • making instances of routines per attachment, rather than dependent on the internal implementation details of the engine

External Names

An external name for the UDR engine is defined as

  '<module name>!<routine name>!<misc info>'

The <module name> is used to locate the library, <routine name> is used to locate the routine registered by the given module, and <misc info> is an optional user-defined string that can be passed to the routine to be read by the user.

Module Availability

Modules available to the UDR engine should be in a directory listed by way of the path attribute of the corresponding plugin_config tag. By default, a UDR module should be on <fbroot>/plugins/udr, in accordance with its path attribute in <fbroot>/plugins/udr_engine.conf.

The user library should include FirebirdUdr.h (or FirebirdUdrCpp.h) and link with the udr_engine library. Routines are easily defined and registered, using some macros, but nothing prevents you from doing things manually.


A sample routine library is implemented in examples/udr, showing how to write functions, selectable procedures and triggers. It also shows how to interact with the current attachment through the legacy API.


The state of a UDR routine (i.e., its member variables) is shared among multiple invocations of the same routine until it is unloaded from the metadata cache. However, it should be noted that the instances are isolated per session.

Character Set

By default, UDR routines use the character set that was specified by the client.


In future, routines will be able to modify the character set by overriding the getCharSet method. The chosen character set will be valid for communication with the old Firebird client library as well as the communications passed through the FirebirdExternal API.

Enabling UDRs in the Database

Enabling an external routine in the database involves a DDL command to create it. Of course, it was already created externally and (we hope) well tested.

Syntax Pattern

    [ ( <parameter list> ) ]
    [ RETURNS ( <parameter list> ) ]
    EXTERNAL NAME '<external name>' ENGINE <engine>

    [ <parameter list> ]
    RETURNS <data type>
    EXTERNAL NAME '<external name>' ENGINE <engine>

    EXTERNAL NAME '<external name>' ENGINE <engine>


create procedure gen_rows (
  start_n integer not null,
  end_n integer not null
) returns (
  n integer not null
) external name 'udrcpp_example!gen_rows'
  engine udr;

create function wait_event (
  event_name varchar(31) character set ascii
) returns integer
  external name 'udrcpp_example!wait_event'
  engine udr;

create trigger persons_replicate
  after insert on persons
  external name 'udrcpp_example!replicate!ds1'
  engine udr;

How it Works

The external names are opaque strings to Firebird. They are recognized by specific external engines. External engines are declared in configuration files, possibly in the same file as a plug-in, as in the sample UDR library that is implemented in $(root)/plugins.

  external_engine = UDR {
  	plugin_module = UDR_engine

  plugin_module = UDR_engine {
  	filename = $(this)/udr_engine
  	plugin_config = UDR_config

plugin_config = UDR_config {
	path = $(this)/udr

When Firebird wants to load an external routine (function, procedure or trigger) into its metadata cache, it gets the external engine through the plug-in external engine factory and asks it for the routine. The plug-in used is the one referenced by the attribute plugin_module of the external engine.


Depending on the server architecture (Superserver, Classic, etc) and implementation details, Firebird may get external engine instances per database or per connection. Currently, it always gets instances per database.

Prev: Compatibility with Older VersionsFirebird Documentation IndexUp: Firebird 3.0.6 Release NotesNext: Optimizer Improvements
Firebird Documentation IndexFirebird 3.0.6 Release Notes → Changes in the Firebird Engine