From: Alex P. <pes...@ma...> - 2011-03-23 12:42:55
|
On 03/23/11 02:23, Adriano dos Santos Fernandes wrote: > Hi! > > Current providers code followed the changes I did in trunk for external > engines... > > IAttachment: > virtual ITransaction* FB_CARG startTransaction(Status* status, > unsigned int tpbLength, > const unsigned char* tpb, FB_API_HANDLE api) = 0; > > PProvider: > virtual void FB_CARG attachDatabase(Status* status, IAttachment** > ptr, FB_API_HANDLE api, > const char* fileName, unsigned int dpbLength, const unsigned > char* dpb) = 0; > virtual void FB_CARG createDatabase(Status* status, IAttachment** > ptr, FB_API_HANDLE api, > const char* fileName, unsigned int dpbLength, const unsigned > char* dpb) = 0; > > startTransaction is currently not ok, Aahh - I've forgotten that same trick was needed in it. Sorry. Hope we will find soon better solution. > so look at PProvider methods and > forget about transactions for now (it's the same concepts). The yvalve > calls the provider passing the address of an IAttachment* and a handle > just allocated. > > Once the engine setups an attachment, it puts it address in *ptr. This > is the place which the yvalve uses, so even before attachDatabase > returns, the yvalve can be called and access the database. > > This is not good solution, and now exposes internal ugliness to the API. > The "api" parameters means nothing for user code, and a returned > IAttachment is clean than an IAttachment** parameter. In private discussion Dmitry Kovalenko told me the contrary :-) But personally I agree - API in which new objects are returned, not Object** parameter is passed, looks better. > And this don't solve the problem with autonomous transactions, created > inside the engine and used by external engines. To support this, we need > new approach. It's the engine who needs to ask the yvalve to register an > object and get its handle (yvalve object). > > But this must not be done directly, since providers could be chained to > implement things (say trace provider we discussed it and decided it's bad idea > , log/replay provider which must log only external calls > ). > I think that for current attachment (and transaction) external engines must directly use appropriate engine interfaces. Same for transactions created internally. > I propose IProviderManagement interface. The yvalve will creates an > instance of IProviderManager and will pass it to the factory that > creates a provider. So when an provider is created, it receives an > yvalve IProviderManagement (or something else that indirectly calls the > yvalve object). > > It will have these kind of methods: > - virtual IAttachment* registerAttachment(IAttachment* attachment) = 0; > - virtual void freeAttachment(IAttachment* attachment) = 0; > > registerAttachment receives the engine object and creates and returns an > yvalve object. Remember, ProviderManager instance was created by yvalve. > freeAttachment receives the yvalve object and makes the yvalve object > orphan, that is, all calls on it will not call the engine provider > anymore but will return an error. > > Now how this is going to be used. Say we have: > > ------------ > IAttachment* JrdProvider::attachDatabase(...) > { > JrdAttachment* attachment = new ...; > ... > > attachment->external = providerManager->registerAttachment(attachment); > > ... > // database triggers will call external code passing an > attachment->external > runDatabaseTriggers(attachment); > > return attachment; > } > > Attachment::~Attachment() > { > providerManager->freeAttachment(this->external); > } > ------------ > > Then we solve three problems: > 1) API ugliness: unused (by user code) parameters and refcount > 2) External access to objects created internally > 3) Management of freed objects > > Actually, we can solve another problem. Note the IStatement method > below. The problem is that a dsql_req (IStatement) cannot be reprepared > internally, so a new object must be created. > > // FixMe - prepare must return void, not new statement handle > virtual IStatement* FB_CARG prepare(Status* status, ITransaction* tra, > unsigned int stmtLength, const char* sqlStmt, unsigned int dialect, > unsigned int itemLength, const unsigned char* items, > unsigned int bufferLength, unsigned char* buffer) = 0; > > We can do something like: > > dsql_req::prepare(...) > { > dsql_req* newStatement = ...; > providerManager->replaceStatement(this->external, newStatement); > } > Adriano, are you serious that all described here is simpler than proxy objects in engine? :-) |