Subject | Re: [Firebird-Architect] External Engines (and Plugins) |
---|---|
Author | Vlad Khorsun |
Post date | 2008-06-23T09:34:45Z |
Adriano dos Santos Fernandes :
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.
"language" word.
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.
library for this plugin in Delphi and it will work.
another Java connectivity driver) support as original plugin did ?
is not a bad thing. But i have no strong preference here and may live with and
without EXTERNAL keyword in procedure declaration.
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 ?
stayed in metadata cache. Therefore it allows to reload library without restart
of Firebird and without of extra notifications.
via corresponding factory only, am i wrong ?
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 ?
facilities.
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 saves at least trigger call overhead.
I'm not against <misc info> i just want to understand for what it is needed ;)
critical. Therefore i think we need external aggregates not at second order.
a) It must allow to process NULL values. I.e. it must not use NULL value as sign
of EOF. Instead we may introduce builtin system function AGGREGATE_STATE (like
CURRENT_xxx) which will return at least 2 logical values of "accumulating
values" and "produce result".
b) I guess we need obtain input value first and only then accumulate it in
result. We may supply first input value with aggregate functin call but it must
be carefully think on and documented.
c) I not sure that using of SUSPEND keyword is correct here. We can introduce
something more adequate, say WAITROW or GETINPUT, etc.
Taking into account comments above, your example will look as follows:
create aggregate function mult (
n integer
) returns integer
as
declare ret integer = 1;
begin
while (1=1)
do
begin
WAITROW; // or SUSPEND, i don't know what is better
if (AGGREGATE_STATE <> 1) // accumulating values
then leave;
if (n is not null)
then ret = ret * n;
end
return ret;
end
Regards,
Vlad
> Vlad wrote:I not agree here. Language "implemented" by compiler :) I'd said what we have
>>> 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.
>>
> External plugins implements languages.
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.
> Languages are made available toYes, this is how it works and we also have the same semantic problem with
> databases through the configuration file. This is almost how INTL works,
> and allows simultaneous usage of many plugins implementing many
> languages. The admin chooses in the config file what's used from what
> plugin. Also this makes clears features available to databases.
>
> AFAIK, this is also how original implementation works, but instead of
> config the languages/libraries are chosen from a system table.
"language" word.
> Writing external routines directly through the C++ interface is not easyYes, two layers. And this is good design for generic usage i think. But nobody
> 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.
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.
> For example, there is the C++ (engine) plugin. It implements the CPPIt have nothing common with C++ language itself i guess. I can write user
> language. Users will not reimplement it, but write libraries that
> register routines in the C++ plugin.
library for this plugin in Delphi and it will work.
> Firebird talks with the plugin, and the plugin talks with userBTW, do you have Java-plugin implemented ? If yes, does it requires Jaybird (or
> libraries. This is also how Java routines will work, the plugin will
> load user classes.
another Java connectivity driver) support as original plugin did ?
>> Also, you removed EXTERNAL keyword from original implementation. What is theI think ability to distinguish external and native procedures at syntax level
>> reasons for it ?
>>
> I changed EXTERNAL NAME to ENTRY_POINT. It makes things more likely UDFs
> are. Also EXTERNAL NAME seems not good inside an EXTERNAL FUNCTION
> declaration.
is not a bad thing. But i have no strong preference here and may live with and
without EXTERNAL keyword in procedure declaration.
>>> Here are links to latest version of files that worth discuss. PleaseI made all EE API interface methods stdcall to have ability to write plugins in
>>> 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 ?
> 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.
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 ?
>> Why do you use MSVCIt seems we need to think again about it ;)
>> extended syntax ? IIRC plain "stdcall" is supported by every compiler while
>> "__stdcall". is MS extension.
>>
> This is how we define it in public ibase.h.
>> Why values within Values class enumerated starting from 1 but not zero ?Who was pushed on you so hard so you gave up ? :)
>>
> 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.
>>> The plugin library may save the plugin object and call they methodsReference counting allows to release library as soon as no more reference on it
>>> 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.
stayed in metadata cache. Therefore it allows to reload library without restart
of Firebird and without of extra notifications.
>>> Inside the plugin entry point (firebirdPlugin), the plugin may registerI thought Firebird obtained ExternalEngine (or Trace, or INTL, etc) instance
>>> 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.
via corresponding factory only, am i wrong ?
>> It seems for me it is better to introduce enumeration ofI don't understand, explain please.
>> 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.
>> Also it seems better to use word "Plugin" instead of "ExternalEngine" for pluginIt seems you mixed generic PluginAPI (used to enumerate, load\unload and
>> 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.
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 ?
>>> External Engines API:See above. I not convinced term "language" reflects nature of implemented
>>>
>>> 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 ?
>>
> Language is registered in the config file so Firebird knows what plugin
> it should ask for it. Plugins returns ExternalEngines based on language
> parameter of ExternalEngineFactory::createEngine.
facilities.
>>> The C++ (CPP) engine:So, you offer to declare as many REPLICATE triggers as destination datasources
>>>
>>> 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 ?
>>
> 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.
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 saves at least trigger call overhead.
I'm not against <misc info> i just want to understand for what it is needed ;)
>> PS It seems as a good idea to mention original creator of code in headers. IThank you.
>> mean at least Eugeney Poutilin.
>>
> Sorry. Will do it, certainly.
>> PPS Do you plan to introduce interface for user defined aggregates ?Aggregates may be called million times so PSQL's call\execution overhead is
> IMO we need first a generic way for user defined aggregates, i.e., it
> should be possible to write aggregate functions in PSQL.
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)I like it, looks very attractive. Just a few comments:
> 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
a) It must allow to process NULL values. I.e. it must not use NULL value as sign
of EOF. Instead we may introduce builtin system function AGGREGATE_STATE (like
CURRENT_xxx) which will return at least 2 logical values of "accumulating
values" and "produce result".
b) I guess we need obtain input value first and only then accumulate it in
result. We may supply first input value with aggregate functin call but it must
be carefully think on and documented.
c) I not sure that using of SUSPEND keyword is correct here. We can introduce
something more adequate, say WAITROW or GETINPUT, etc.
Taking into account comments above, your example will look as follows:
create aggregate function mult (
n integer
) returns integer
as
declare ret integer = 1;
begin
while (1=1)
do
begin
WAITROW; // or SUSPEND, i don't know what is better
if (AGGREGATE_STATE <> 1) // accumulating values
then leave;
if (n is not null)
then ret = ret * n;
end
return ret;
end
Regards,
Vlad