Subject Re: [Firebird-Architect] External Engines (and Plugins)
Author Vlad
Hi Adriano,

good to hear something new in this direction from you again :)

> Syntax:
>
> { CREATE [ OR ALTER ] | RECREATE | ALTER } PROCEDURE <name>
> [ ( <parameter list> ) ]
> [ RETURNS ( <parameter list> ) ]
> ENTRY_POINT '<entry point>'
> LANGUAGE <language>

What is exact meaning of LANGUAGE clause ? From the explanations below i see
it as plugin identifier, not related directly to any language.

Also, you removed EXTERNAL keyword from original implementation. What is the
reasons for it ?

...

> Here are links to latest version of files that worth discuss. Please
> verify them:
>
> FirebirdApi.h is the generic new C++ API. These classes was designed to
> future replace the ISC API in mind. Only the necessary classes and
> methods for external engines was created -
> http://firebird.cvs.sourceforge.net/firebird/firebird2/src/include/FirebirdApi.h?view=markup&pathrev=B2_5_ExtEngines

Why FB_CALL is defined as __stdcall for WIN32 only ? Why do you use MSVC
extended syntax ? IIRC plain "stdcall" is supported by every compiler while
"__stdcall". is MS extension.

How memory for Values is allocated and freed ? Who is responsible for memory
management ?

Why values within Values class enumerated starting from 1 but not zero ?

...

> Firebird Plugin API:
>
> When Firebird is initializing, it opens all *.conf files from
> <fbroot>/plugins. For each plugin_module tag found, it constructs a
> Plugin object, reads the corresponding plugin_config tag and inserts all
> config information in the object.
>
> It then gets the attribute value of plugin_module/filename, load it as a
> dynamic (shared) library and calls the exported function firebirdPlugin
> (PluginEntryPoint prototype) passing the Plugin object as parameter.

So, Firebird loads *all* plugins at initialization time ? Why ?
If there is needs to load some plugin (or some plugin's kind) within the engine
itself lets add corresponding attribute in config file, but please, don't load
all not needed libraries at startup.

> The plugin library may save the plugin object and call they methods
> later. The object and all pointers returned by it are valid until the
> plugin is unloaded (done through OS unload of the dynamic library) when
> Firebird is shutting down.

I think reference counting is much better for such usage. Also it allows to
reload plugin library without stop of Firebird.

> Inside the plugin entry point (firebirdPlugin), the plugin may register
> extra functionality that may be obtained by Firebird when required.
> Currently only External Engines may be registered through
> Plugin::setExternalEngineFactory.

How it can be extended in future for new kinds of plugins ? Adding new
Plugin::setXXXFactory ? It seems for me it is better to introduce enumeration of
known plugin types and create only universal method to register all kinds of
factories, such as

Plugin::registerFactory(PluginKind, PluginFactory).

Also it seems better to use word "Plugin" instead of "ExternalEngine" for plugin
API :)


> Example plugin configuration file:
>
> <language CPP>
> plugin_module CPP_engine
> </language>
>
> <plugin_module CPP_engine>
> filename $(this)/cpp_engine
> plugin_config CPP_config
> </plugin_module>
>
> <plugin_config CPP_config>
> path $(this)/cpp
> </plugin_config>
>
> Note that the language tag is ignored at this stage. Only plugin_module
> and plugin_config are read. The dynamic library extension may be
> ommitted, and $(this) expands to the directory of the .conf file.

What if more than one <language CPP> (or <plugin_module CPP_engine>, or any
other section) section is present in config file ?

> Plugins access Firebird databases through the client library read from
> Plugin::getLibraryName method. This method may return different
> filenames depending on the server architecture, and may even return
> NULL. Currently it returns:
> Architecture -> File
> Embedded -> The embedded library
> Windows SS -> fbserver executable [3]
> Windows CS/SC -> fb_inet_server executable [3]
> POSIX CS/SC -> The embedded library
> POSIX SS -> NULL [application should open it through dlopen(NULL)] [3]
> [3] The functions are exported direct in the executable. Not well
> know/used technique, but works in Windows and POSIX.

I.e. plugin must load ISC API functions by itself and never used fbclient for
this, correct ?

> External Engines API:
>
> Entry points are opaque strings to Firebird. They are recognized by
> specific external engines. A external engine is the implementation of a
> language. Languages are declared in config files (possibly in the same
> file as a plugin, like in the config example present here).

I still see no correspondence between natural meaning of word 'language' and
how it is used. I can write plugin which will work with any dll written on any
language and how i must register 'language' of my plugin ?

> When Firebird wants to load an external routine into its metadata cache,
> it gets (if not already done for the database [4]) the external engine
> through the plugin external engine factory and ask it for the routine.
> The plugin used is the one referenced by the attribute plugin_module of
> the routine's language.
> [4] This is in Super-Server. In [Super-]Classic, different attachments
> to one database creates multiple metadata caches and hence multiple
> external engine instances.
>
>
> The C++ (CPP) engine:
>
> Entry points of the C++ engine are defined as following:
> '<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 user defined string passed to the routine and can read there. "!<misc
> info>" may be ommitted.

Why it is better than passing this <misc info> as parameter ?


Regards,
Vlad

PS It seems as a good idea to mention original creator of code in headers. I
mean at least Eugeney Poutilin.

PPS Do you plan to introduce interface for user defined aggregates ?