Subject Re: [Firebird-Architect] External Engines (and Plugins)
Author Adriano dos Santos Fernandes
Vlad Khorsun escreveu:
>> External plugins implements languages.
>>
>
> I not agree here. Language "implemented" by compiler :)
Ok. :-)

> I'd said what we have as ExternalEngine is a set of rules of how to write loadable library which this
> EE plugin is able to load, obtain entrypoints from library and execute its
> functions passing parameters and returning results. Also plugin may provide some
> kind of run-time support for user library. But i see no relation with language,
> sorry.
>
But for the database (accordingly to the standard) there are languages.
If you do things in other way (and don't have one to suggest) we're
going to complicate things.

>> Writing external routines directly through the C++ interface is not easy
>> as write an UDF. And this is more difficult for others (non C++)
>> languages. So the things are break into two layers: plugins and user
>> libraries.
>>
>
> Yes, two layers. And this is good design for generic usage i think. But nobody
> prevents users to write single External Engine plugin with application code
> builtin. It can be usable for complex application layer executed on database
> server side.
>
Yes. But I don't have a good suggestion on how to not name this as a
language.

>
>> For example, there is the C++ (engine) plugin. It implements the CPP
>> language. Users will not reimplement it, but write libraries that
>> register routines in the C++ plugin.
>>
>
> It have nothing common with C++ language itself i guess. I can write user
> library for this plugin in Delphi and it will work.
>
Maybe you can, but is not the design goals. You will need to deal with
Delphi->C++ exceptions conversions, for example. In the end you will
write a Delphi plugin embedded in the library modules mixing layers.

>
>> Firebird talks with the plugin, and the plugin talks with user
>> libraries. This is also how Java routines will work, the plugin will
>> load user classes.
>>
>
> BTW, do you have Java-plugin implemented ? If yes, does it requires Jaybird (or
> another Java connectivity driver) support as original plugin did ?
>
Well, since original implementors doesn't showed the Java code :-), I
had need to start things. And I started yesterday. I never wrote JNI
code before, but I'm already able to load JVM, pass control to Java for
functions, and access others (not the "current" attachment/transaction)
databases using Jaybird with very small changes. Things are very basic yet.

Yes, it requires Jaybird and don't think we should do other thing. I
started things on firebird2 tree but I'll move (offline for now) to
client-java tree. Do things on firebird2 tree looks attractive due to
C++ class library and build things direct. But it will anyway requires
Jaybird, and it will requires some changes on it. Also not many things
of our class library will be needed on the Java plugin.

>>> Why FB_CALL is defined as __stdcall for WIN32 only ?
>>>
>> Native calling convention from Windows is STDCALL. Also, the default
>> calling convention for C++ functions in MSVC is THISCALL, that is
>> STDCALL. Certainly, we may define FB_CALL as CDECL, but since COM used
>> STDCALL, I think we should use it.
>>
>> And finally, the ISC API uses it too.
>>
>
> I made all EE API interface methods stdcall to have ability to write plugins in
> Delphi. IIRC, i described this decision here. I.e. it was done nor because of
> ISC API used it nor because of Windows\COM\etc.
>
> I asked you not why "stdcall" but why "stdcall" is for Windows only ?
>
Because it's Windows-only native (from API POV) calling convention.

>>> Why values within Values class enumerated starting from 1 but not zero ?
>>>
>>>
>> Do you talk about "index" parameter? This is how almost all SQL
>> libraries works. I prefer indexes starting from 0, but I give up on this.
>>
>
> Who was pushed on you so hard so you gave up ? :)
>
The world seems to like counters starting from 1. They are incorrect, we
know. :-) But there are many people to convince.

>
>
>>>> 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.
>>>
>>>
>> If we implement plugin reload, the plugin will be need to be notified by
>> some way and can/will reload its state. I don't see any need for
>> reference counting here.
>>
>
> Reference counting allows to release library as soon as no more reference on it
> stayed in metadata cache. Therefore it allows to reload library without restart
> of Firebird and without of extra notifications.
>
If unload/reload is done, entrypoint will be called again. So I guess
you're not talking about the Plugin object received by the library, but
about internal implementation of PluginManager/ExtEngineManager.

Anyway, I don't think unload is good. For Java plugin, for example, it
will cause unload/reload of JVM.

>
>
>>>> 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 ?
>>>
>> Yes. All API set are versioned so they can be extended and used
>> appropriated. But not all plugins will register through factories. Some
>> may need to call simple Plugin:::setXXX methods.
>>
>
> I thought Firebird obtained ExternalEngine (or Trace, or INTL, etc) instance
> via corresponding factory only, am i wrong ?
>
For ExternaEngine and for future INTL plugin, yes. For Trace, I don't
know exactly how it works. But imagine a plugin that wants to receive
these events:
- engineInitialized
- engineShutingDown

There is no factory for it. It should be:
class EngineListener
{
virtual void engineInitialized() = 0;
virtual void engineShutingDown() = 0;
};

void Plugin::setEngineListener(EngineListener*);

>
>>> 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).
>>>
>>>
>> This will cause a plugin to extend a whole set of methods to just do one
>> thing.
>>
>
> I don't understand, explain please.
>
class PluginFactoryImpl : public PluginFactory
{
virtual createExternalEngine...
virtual createCharacterSet... // this is EE plugin, but need to
override all PluginFactory
};

And if you want to extend PluginFactory for each PluginKind, it doesn't
differ much from current design.

>
>>> Also it seems better to use word "Plugin" instead of "ExternalEngine" for plugin
>>> API :)
>>>
>>>
>> I do not like "external engine" or ESP words from original
>> implementations. :-) But Plugin is generic. We may have the TracePlugin,
>> for example. I think "Language Plugin" is a more appropriate term.
>>
>
> It seems you mixed generic PluginAPI (used to enumerate, load\unload and
> configure loadable plugin libraries) with concrete API, implemented by plugin
> (ExternalEngine API, Trace API, etc). PluginAPI must be isolated from all other
> APIs, isn't is ?
>
I mean, TracePluginApi.h. The only thing that Plugin knows about
ExternalEngine is that ExternalEngine is a class and that it (Plugin)
have a interface factory to create that class.

>> Because some things is better encoded in the metadata, and for triggers
>> there is no parameter. For example, see my REPLICATE trigger example.
>> The database designer chooses the datasource, and the trigger will read
>> properties of that datasource from a table.
>>
>
> So, you offer to declare as many REPLICATE triggers as destination datasources
> and mark each of them by corresponding datasource name in <misc info> ? I don't
> think its OK. Instead i would put all destinations names into separate table and
> read this table in one REPLICATE trigger.
It will have another table to be read. In some cases no table may be
needed, just the info.

> It saves at least trigger call overhead.
>
I don't understand.

>>> PPS Do you plan to introduce interface for user defined aggregates ?
>>>
>> IMO we need first a generic way for user defined aggregates, i.e., it
>> should be possible to write aggregate functions in PSQL.
>>
>
> Aggregates may be called million times so PSQL's call\execution overhead is
> critical. Therefore i think we need external aggregates not at second order.
>
...
>
>> A way to do it may be through normal functions (but marked as aggregate)
>> that SUSPEND asks for new rows, instead of produce rows. Something like:
>>
>> create aggregate function mult (
>> n integer
>> ) returns integer
>> as
>> declare ret integer = 1;
>> begin
>> while (n is not null)
>> do
>> begin
>> ret = ret * n;
>> suspend;
>> end
>>
>> return ret;
>> end
>>
>
> I like it, looks very attractive. Just a few comments:
So I think we must discuss in another thread all things needed for it.
External interface may be then created it.


Adriano