From: Adriano d. S. F. <adr...@gm...> - 2011-03-22 23:23:37
|
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, 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. 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, log/replay provider). 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 |