8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-22 21:23:04 +01:00

Implemented CORE-5083: Provide method to correcty set offsets in implemented by user IMessageMetadata

This commit is contained in:
alexpeshkoff 2016-01-25 18:34:27 +00:00
parent 8fc0267026
commit ffa487a1bc
5 changed files with 412 additions and 2 deletions

View 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;
}

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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