diff --git a/examples/interfaces/05.user_metadata.cpp b/examples/interfaces/05.user_metadata.cpp new file mode 100644 index 0000000000..76dd6d4c98 --- /dev/null +++ b/examples/interfaces/05.user_metadata.cpp @@ -0,0 +1,260 @@ +/* + * PROGRAM: Object oriented API samples. + * MODULE: 05.user_metadata.cpp + * DESCRIPTION: A sample of user-implemented IMessageMetadata. + * Prints firebird user name (SYSDBA by default). + * + * Example for the following interfaces: + * + * IOffsetsCallback - callback for IUtil::setOffsets() + * IMessageMetadata - how to implement it yourself + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alexander Peshkoff + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2016 Alexander Peshkoff + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "ifaceExamples.h" + +static IMaster* master = fb_get_master_interface(); + +/* + * Trivial sample of IMessageMetadata implementation. + * Metadata is created for a fixed outptu format with single char field. + * Therefore index parameter in all functions is ignored. + * Because the only possible error is index out of bounds status parameter is ignored too. + * Atomic operation is not used in IReferenceCounted cause we do not plan MT support. + * Non-array vars used to represent offset and nullOffset of that single field. + */ + +class MyMetadata : public IMessageMetadataImpl +{ +private: + class Callback : public IOffsetsCallbackImpl + { + private: + MyMetadata* metadata; + + public: + Callback(MyMetadata* pmeta) + : metadata(pmeta) + { } + + //IOffsetsCallback implementation + void setOffset(ThrowStatusWrapper* status, unsigned /*index*/, unsigned offset, unsigned nullOffset) + { + // Typically setOffset() function should save passed offsets + // in your implementation of message metadata. + metadata->offset = offset; + metadata->nullOffset = nullOffset; + } + }; + +public: + unsigned offset, nullOffset, length; + + MyMetadata() + { + IUtil* utl = master->getUtilInterface(); + ThrowStatusWrapper s(master->getStatus()); + try + { + Callback cb(this); + length = utl->setOffsets(&s, this, &cb); + } + catch(...) + { + s.dispose(); + throw; + } + s.dispose(); + } + + // Dummy IReferenceCounted implementation + // JUST a SAMPLE - interface will be placed on stack, ignore reference counts !!! + // In real life please use your favorite atomic incr/decr here + // and create reference counted interfaces on heap. + void addRef() + { } + + int release() + { + return 1; + } + + // IMessageMetadata implementation + unsigned getCount(ThrowStatusWrapper* /*status*/) + { + return 1; + } + + const char* getField(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return NULL; + } + + const char* getRelation(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return NULL; + } + + const char* getOwner(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return NULL; + } + + const char* getAlias(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return NULL; + } + + unsigned getType(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return SQL_VARYING; + } + + FB_BOOLEAN isNullable(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return false; + } + + int getSubType(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return 0; + } + + unsigned getLength(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return 20; // Want to make it fit + } + + int getScale(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return 0; + } + + unsigned getCharSet(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return 0; + } + + unsigned getOffset(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return offset; + } + + unsigned getNullOffset(ThrowStatusWrapper* /*status*/, unsigned /*index*/) + { + return nullOffset; + } + + IMetadataBuilder* getBuilder(ThrowStatusWrapper* status) + { + ISC_STATUS err[] = {isc_arg_gds, isc_wish_list, isc_arg_end}; + status->setErrors(err); + return NULL; + } + + unsigned getMessageLength(ThrowStatusWrapper* /*status*/) + { + return length; + } +}; + +template +T to(const unsigned char* b, unsigned o) +{ + return *((T*)(b + o)); +} + +int main() +{ + int rc = 0; + unsigned char* buffer = NULL; + + // set default password if none specified in environment + setenv("ISC_USER", "sysdba", 0); + setenv("ISC_PASSWORD", "masterkey", 0); + + // status vector and main dispatcher + ThrowStatusWrapper status(master->getStatus()); + IProvider* prov = master->getDispatcher(); + + // declare pointers to required interfaces + IAttachment* att = NULL; + ITransaction* tra = NULL; + IResultSet* curs = NULL; + + // Instance of our metadata + MyMetadata meta; + + // allocate output buffer + buffer = new unsigned char[meta.length]; + + try + { + // attach employee db + att = prov->attachDatabase(&status, "employee", 0, NULL); + + // start default transaction + tra = att->startTransaction(&status, 0, NULL); + + // open cursor + curs = att->openCursor(&status, tra, 0, "select current_user from rdb$database", 3, NULL, NULL, &meta, NULL, 0); + + // fetch record from cursor and print it + curs->fetchNext(&status, buffer); + ISC_SHORT l = to(buffer, meta.offset); + printf("<%*.*s>\n", l, l, buffer + meta.offset + sizeof(ISC_SHORT)); + + // close interfaces + curs->close(&status); + curs = NULL; + + tra->commit(&status); + tra = NULL; + + att->detach(&status); + att = NULL; + } + catch (const FbException& error) + { + // handle error + rc = 1; + + char buf[256]; + master->getUtilInterface()->formatStatus(buf, sizeof(buf), error.getStatus()); + fprintf(stderr, "%s\n", buf); + } + + // release interfaces after error caught + if (curs) + curs->release(); + if (tra) + tra->release(); + if (att) + att->release(); + + prov->release(); + status.dispose(); + + delete buffer; + + return rc; +} diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 532dd02ed4..a319cfef0e 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -911,8 +911,7 @@ interface VersionCallback : Versioned interface Util : Versioned { - void getFbVersion(Status status, Attachment att, - VersionCallback callback); + void getFbVersion(Status status, Attachment att, VersionCallback callback); void loadBlob(Status status, ISC_QUAD* blobId, Attachment att, Transaction tra, const string file, boolean txt); void dumpBlob(Status status, ISC_QUAD* blobId, @@ -929,6 +928,12 @@ interface Util : Versioned uint formatStatus(string buffer, uint bufferSize, Status status); uint getClientVersion(); // Returns major * 256 + minor XpbBuilder getXpbBuilder(Status status, uint kind, const uchar* buf, uint len); + uint setOffsets(Status status, MessageMetadata metadata, OffsetsCallback callback); +} + +interface OffsetsCallback : Versioned +{ + void setOffset(Status status, uint index, uint offset, uint nullOffset); } interface XpbBuilder : Disposable diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index 4b64aedd61..77e7137c68 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -84,6 +84,7 @@ namespace Firebird class ITimerControl; class IVersionCallback; class IUtil; + class IOffsetsCallback; class IXpbBuilder; class ITraceConnection; class ITraceDatabaseConnection; @@ -3446,6 +3447,7 @@ namespace Firebird unsigned (CLOOP_CARG *formatStatus)(IUtil* self, char* buffer, unsigned bufferSize, IStatus* status) throw(); unsigned (CLOOP_CARG *getClientVersion)(IUtil* self) throw(); IXpbBuilder* (CLOOP_CARG *getXpbBuilder)(IUtil* self, IStatus* status, unsigned kind, const unsigned char* buf, unsigned len) throw(); + unsigned (CLOOP_CARG *setOffsets)(IUtil* self, IStatus* status, IMessageMetadata* metadata, IOffsetsCallback* callback) throw(); }; protected: @@ -3538,6 +3540,43 @@ namespace Firebird StatusType::checkException(status); return ret; } + + template unsigned setOffsets(StatusType* status, IMessageMetadata* metadata, IOffsetsCallback* callback) + { + StatusType::clearException(status); + unsigned ret = static_cast(this->cloopVTable)->setOffsets(this, status, metadata, callback); + StatusType::checkException(status); + return ret; + } + }; + + class IOffsetsCallback : public IVersioned + { + public: + struct VTable : public IVersioned::VTable + { + void (CLOOP_CARG *setOffset)(IOffsetsCallback* self, IStatus* status, unsigned index, unsigned offset, unsigned nullOffset) throw(); + }; + + protected: + IOffsetsCallback(DoNotInherit) + : IVersioned(DoNotInherit()) + { + } + + ~IOffsetsCallback() + { + } + + public: + static const unsigned VERSION = 2; + + template void setOffset(StatusType* status, unsigned index, unsigned offset, unsigned nullOffset) + { + StatusType::clearException(status); + static_cast(this->cloopVTable)->setOffset(this, status, index, offset, nullOffset); + StatusType::checkException(status); + } }; class IXpbBuilder : public IDisposable @@ -12257,6 +12296,7 @@ namespace Firebird this->formatStatus = &Name::cloopformatStatusDispatcher; this->getClientVersion = &Name::cloopgetClientVersionDispatcher; this->getXpbBuilder = &Name::cloopgetXpbBuilderDispatcher; + this->setOffsets = &Name::cloopsetOffsetsDispatcher; } } vTable; @@ -12424,6 +12464,21 @@ namespace Firebird return static_cast(0); } } + + static unsigned CLOOP_CARG cloopsetOffsetsDispatcher(IUtil* self, IStatus* status, IMessageMetadata* metadata, IOffsetsCallback* callback) throw() + { + StatusType status2(status); + + try + { + return static_cast(self)->Name::setOffsets(&status2, metadata, callback); + } + catch (...) + { + StatusType::catchException(&status2); + return static_cast(0); + } + } }; template > > @@ -12451,6 +12506,58 @@ namespace Firebird virtual unsigned formatStatus(char* buffer, unsigned bufferSize, IStatus* status) = 0; virtual unsigned getClientVersion() = 0; virtual IXpbBuilder* getXpbBuilder(StatusType* status, unsigned kind, const unsigned char* buf, unsigned len) = 0; + virtual unsigned setOffsets(StatusType* status, IMessageMetadata* metadata, IOffsetsCallback* callback) = 0; + }; + + template + class IOffsetsCallbackBaseImpl : public Base + { + public: + typedef IOffsetsCallback Declaration; + + IOffsetsCallbackBaseImpl(DoNotInherit = DoNotInherit()) + { + static struct VTableImpl : Base::VTable + { + VTableImpl() + { + this->version = Base::VERSION; + this->setOffset = &Name::cloopsetOffsetDispatcher; + } + } vTable; + + this->cloopVTable = &vTable; + } + + static void CLOOP_CARG cloopsetOffsetDispatcher(IOffsetsCallback* self, IStatus* status, unsigned index, unsigned offset, unsigned nullOffset) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::setOffset(&status2, index, offset, nullOffset); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + }; + + template > > + class IOffsetsCallbackImpl : public IOffsetsCallbackBaseImpl + { + protected: + IOffsetsCallbackImpl(DoNotInherit = DoNotInherit()) + { + } + + public: + virtual ~IOffsetsCallbackImpl() + { + } + + virtual void setOffset(StatusType* status, unsigned index, unsigned offset, unsigned nullOffset) = 0; }; template diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index e0af347fd2..78b568eef3 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -567,6 +567,8 @@ public: unsigned getClientVersion(); Firebird::IXpbBuilder* getXpbBuilder(Firebird::CheckStatusWrapper* status, unsigned kind, const unsigned char* buf, unsigned len); + unsigned setOffsets(Firebird::CheckStatusWrapper* status, Firebird::IMessageMetadata* metadata, + Firebird::IOffsetsCallback* callback); }; } // namespace Why diff --git a/src/yvalve/utl.cpp b/src/yvalve/utl.cpp index b967e2b309..10188590b8 100644 --- a/src/yvalve/utl.cpp +++ b/src/yvalve/utl.cpp @@ -1054,6 +1054,42 @@ IXpbBuilder* UtilInterface::getXpbBuilder(CheckStatusWrapper* status, } } +unsigned UtilInterface::setOffsets(CheckStatusWrapper* status, IMessageMetadata* metadata, + IOffsetsCallback* callback) +{ + try + { + unsigned count = metadata->getCount(status); + check(status); + + unsigned length = 0; + + for (unsigned n = 0; n < count; ++n) + { + unsigned type = metadata->getType(status, n); + check(status); + unsigned len = metadata->getLength(status, n); + check(status); + + unsigned offset, nullOffset; + length = fb_utils::sqlTypeToDsc(length, type, len, + NULL /*dtype*/, NULL /*length*/, &offset, &nullOffset); + + callback->setOffset(status, n, offset, nullOffset); + check(status); + } + + return length; + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + + return 0; +} + + } // namespace Why