mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-22 18:43:02 +01:00
Implemented CORE-5083: Provide method to correcty set offsets in implemented by user IMessageMetadata
This commit is contained in:
parent
8fc0267026
commit
ffa487a1bc
260
examples/interfaces/05.user_metadata.cpp
Normal file
260
examples/interfaces/05.user_metadata.cpp
Normal file
@ -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 <peshkoff@mail.ru>
|
||||
* 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<MyMetadata, ThrowStatusWrapper>
|
||||
{
|
||||
private:
|
||||
class Callback : public IOffsetsCallbackImpl<Callback, ThrowStatusWrapper>
|
||||
{
|
||||
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 <typename T>
|
||||
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<ISC_SHORT>(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;
|
||||
}
|
@ -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
|
||||
|
@ -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 <typename StatusType> unsigned setOffsets(StatusType* status, IMessageMetadata* metadata, IOffsetsCallback* callback)
|
||||
{
|
||||
StatusType::clearException(status);
|
||||
unsigned ret = static_cast<VTable*>(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 <typename StatusType> void setOffset(StatusType* status, unsigned index, unsigned offset, unsigned nullOffset)
|
||||
{
|
||||
StatusType::clearException(status);
|
||||
static_cast<VTable*>(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<IXpbBuilder*>(0);
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned CLOOP_CARG cloopsetOffsetsDispatcher(IUtil* self, IStatus* status, IMessageMetadata* metadata, IOffsetsCallback* callback) throw()
|
||||
{
|
||||
StatusType status2(status);
|
||||
|
||||
try
|
||||
{
|
||||
return static_cast<Name*>(self)->Name::setOffsets(&status2, metadata, callback);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
StatusType::catchException(&status2);
|
||||
return static_cast<unsigned>(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Name, typename StatusType, typename Base = IVersionedImpl<Name, StatusType, Inherit<IUtil> > >
|
||||
@ -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 <typename Name, typename StatusType, typename Base>
|
||||
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<Name*>(self)->Name::setOffset(&status2, index, offset, nullOffset);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
StatusType::catchException(&status2);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Name, typename StatusType, typename Base = IVersionedImpl<Name, StatusType, Inherit<IOffsetsCallback> > >
|
||||
class IOffsetsCallbackImpl : public IOffsetsCallbackBaseImpl<Name, StatusType, Base>
|
||||
{
|
||||
protected:
|
||||
IOffsetsCallbackImpl(DoNotInherit = DoNotInherit())
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~IOffsetsCallbackImpl()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void setOffset(StatusType* status, unsigned index, unsigned offset, unsigned nullOffset) = 0;
|
||||
};
|
||||
|
||||
template <typename Name, typename StatusType, typename Base>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user