Click here to Skip to main content
15,895,799 members
Articles / Programming Languages / C

C++ Wrapper Library for Firebird Embedded SQL

,
Rate me:
Please Sign up or sign in to vote.
4.25/5 (9 votes)
23 Sep 2009CPOL8 min read 45.7K   1.1K   24  
This article is devoted to the Embedded Firebird database usage and also development of C++ wrapper of this database.
//*********************************************************************************************
#ifndef __FB_DATA_HELPER_H__
#define __FB_DATA_HELPER_H__
//*********************************************************************************************
#include "fbutils.h"
#include <vector>
#include "util.h"
#include "cmnBufferWriter.h"
//*********************************************************************************************
namespace fb {
//*********************************************************************************************

//*********************************************************************************************
static short    g_nNullValue        = -1;
static int      g_nMaxSegmentLength = 32768;
//*********************************************************************************************

//*********************************************************************************************
//--- ISQLData Interface
//*********************************************************************************************
struct ISQLData
{
	virtual ~ISQLData(){}

	virtual char* get() =0;
	virtual int type() const =0;
	virtual int size() const =0;

    virtual void fill ( XSQLVAR* pXSQLVAR, isc_db_handle* pDatabaseConnection, isc_tr_handle* pCurrentTransaction ) = 0;
    virtual void prepare ( XSQLVAR* pXSQLVAR ) = 0;
    virtual void serialize ( cmn::BufferWriter& cmnBufferWriter, isc_db_handle* pDatabaseConnection, isc_tr_handle* pCurrentTransaction ) = 0;
};

//*********************************************************************************************
//--- CSQLData Implementation ( base types )
//*********************************************************************************************
template<class Type>
class CSQLData: public ISQLData
{
	Type m_data;
	int m_type;
    short m_flag0;
public:
	CSQLData(int type) : m_type(type),m_flag0(0){}
	CSQLData(int type, const Type& data) : m_type(type), m_data(data), m_flag0(0) {}
	char* get() 
    { 
        return (char*)&m_data; 
    }
	int type() const 
    { 
        return m_type; 
    }
    void fill ( XSQLVAR* pXSQLVAR, isc_db_handle* , isc_tr_handle* )
    {
		pXSQLVAR->sqldata = get();
		pXSQLVAR->sqlind  = &m_flag0;
		pXSQLVAR->sqltype = type() + 1;
		pXSQLVAR->sqllen = size();
    }
    void prepare ( XSQLVAR* pXSQLVAR )
    {
		pXSQLVAR->sqldata = get();
		pXSQLVAR->sqlind  = &m_flag0;
    }
    void serialize ( cmn::BufferWriter& cmnBufferWriter, isc_db_handle* , isc_tr_handle* )
    {
        cmnBufferWriter.write_value ( m_data );
    }
	int size() const { return sizeof(Type); }
	void set_size(int size){}
};

//*********************************************************************************************
//--- CSQLData Implementation ( timestmap )
//*********************************************************************************************
template<>
class CSQLData< ISC_TIMESTAMP > : public ISQLData
{
	ISC_TIMESTAMP m_data;
	int m_type;
    short m_flag0;

 public:
    CSQLData(int type) : m_type(type), m_flag0(0)
	{
	}
    CSQLData(int type, double varTime ) : m_type(type), m_flag0(0)
	{
        if ( varTime == 0.0  ) 
            m_flag0 = -1;
        else
        {
            SYSTEMTIME stTime;
            ::VariantTimeToSystemTime ( varTime, &stTime );

            tm tmTime;
            SystemTimeToTmTime ( stTime, &tmTime );

            isc_encode_timestamp ( &tmTime, &m_data );
        }
	}
	char* get() 
    { 
        return (char*)&m_data; 
    }
	int type() const 
    { 
        return m_type; 
    }
    void fill ( XSQLVAR* pXSQLVAR, isc_db_handle* , isc_tr_handle* )
    {
		pXSQLVAR->sqldata = get();
		pXSQLVAR->sqlind  = &m_flag0;
		pXSQLVAR->sqltype = type() + 1;
		pXSQLVAR->sqllen = size();
    }
    void prepare ( XSQLVAR* pXSQLVAR )
    {
		pXSQLVAR->sqldata = get();
		pXSQLVAR->sqlind  = &m_flag0;
    }
    void serialize ( cmn::BufferWriter& cmnBufferWriter, isc_db_handle* , isc_tr_handle* )
    {
        tm tmTime;
        isc_decode_timestamp ( &m_data, &tmTime );

        SYSTEMTIME stTime;
        TmTimeToSystemTime ( tmTime, &stTime );

        double varTime;
        ::SystemTimeToVariantTime ( &stTime, &varTime );

        cmnBufferWriter.write_double ( varTime );
    }
	int size() const { return sizeof(ISC_TIMESTAMP); }
	void set_size(int size){}
};

//*********************************************************************************************
//--- CSQLData Implementation ( string types )
//*********************************************************************************************
typedef enum { ctString = 1, ctWstring, ctVector } CppType;
//*********************************************************************************************
template<>
class CSQLData< char* > : public ISQLData
{
	char* m_data;
	int m_type;
	int m_size;
    short m_flag0;
    CppType m_cppType;

    void init ( int f_nSize, const char* f_pData, bool fUseTypedescriptor = false)
    {
        m_size = f_nSize;
        if ( m_type == SQL_VARYING )
        {
            m_data = new char[m_size+2];
            short int size = (short int)m_size;
            ::memcpy(m_data, &size, 2);
            ::memcpy(m_data+2, f_pData, m_size);
            m_size+=2;
        }
        else
        {
            int addSize = ctWstring ? 2 : 1;

            m_data = new char[ m_size + addSize ];
            ::memcpy(m_data, f_pData, m_size);

            if ( m_cppType == ctWstring )
            {
                wchar_t end[] = L"\0";
                ::memcpy(m_data+m_size, end, addSize);
            }
            else
            {
                char end[] = "\0";
                ::memcpy(m_data+m_size, end, addSize);
            }

            m_size+=addSize;
        }
    }
public:
    CSQLData(int type, CppType cppType ) : m_type(type), m_size(0), m_data(0), m_flag0(0), m_cppType (cppType)
	{
	}
    CSQLData(int type, const std::string& str ) : m_type(type), m_size(0), m_flag0(0), m_data(0), m_cppType(ctString)
	{
        init ( (int)str.size (), str.c_str () );
	}
    CSQLData(int type, const std::wstring& str ) : m_type(type), m_size(0), m_flag0(0), m_data(0), m_cppType(ctWstring)
	{
        init ( (int)str.size () ,  (const char*)utils::wstring2string(str).c_str () );
	}
    CSQLData(int type, const std::vector< char >& str ) : m_type(type), m_size(0), m_flag0(0), m_data(0), m_cppType(ctVector)
    {
        init ( (int)str.size (), utils::GetBeginOf (str), true );
    }
	~CSQLData() 
	{ 
		if(m_data != 0)
			delete []m_data; 
	}
	char* get() 
	{
		return m_data; 
	}
    void prepare ( XSQLVAR* pXSQLVAR )
    {
        int size = pXSQLVAR->sqllen;

		if(m_data == NULL)
		{
            if ( m_type == SQL_VARYING )
				m_size = size + 2;
			m_data = new char[m_size];
			ZeroMemory(m_data, m_size);
		}
		else
            throw std::runtime_error("CSQLData< char* >::Reserve");

		pXSQLVAR->sqldata = get();
		pXSQLVAR->sqlind  = &m_flag0;
	}
    void fill ( XSQLVAR* pXSQLVAR, isc_db_handle* , isc_tr_handle* )
    {
		pXSQLVAR->sqldata = get();  
		pXSQLVAR->sqlind  = &m_flag0;
		pXSQLVAR->sqltype = type() + 1;
		pXSQLVAR->sqllen = size();
    }
    void serialize ( cmn::BufferWriter& cmnBufferWriter, isc_db_handle* , isc_tr_handle* )
    {
        if ( m_cppType == ctWstring )
        {
            if ( m_type == SQL_VARYING )
            {
                std::string str;
                str.assign ( m_data+2, *(short*)m_data );
                std::wstring wRes = utils::string2wstring(str);
                wRes.resize( *(short*)m_data);
                cmnBufferWriter.write_wstring2 ( wRes );
            }
            else
            {
                std::wstring str ( (wchar_t*)m_data );
                cmnBufferWriter.write_wstring2 ( str );
            }
        }
        else if ( m_cppType == ctString )
        {
            if ( m_type == SQL_VARYING )
            {
                std::string str;
                str.assign ( m_data+2, *(short*)m_data );
                cmnBufferWriter.write_string_as_wstring( str );
            }
            else
            {
                std::string str (m_data);
                cmnBufferWriter.write_string_as_wstring ( str );
            }
        }
        else
        {
            if ( m_type == SQL_VARYING )
            {
                cmnBufferWriter.write_buffer ( m_data+2, *(short*)m_data );
            }
            else
            {
                std::string str (m_data);
                cmnBufferWriter.write_buffer ( m_data, str.size () );
            }
        }
    }
	int type() const { return m_type; }
	int size() const 
	{
		return m_size; 
	}
};
	
//*********************************************************************************************
//--- CSQLData Implementation ( blob types )
//*********************************************************************************************
template<>
class CSQLData< std::vector< char > > : public ISQLData
{
	int m_type;
    int m_subtype;
    short m_flag0;
    std::vector< char > m_binary;
    ISC_QUAD m_idBlob;

private:
    void blob_write ( isc_blob_handle* phBlob, std::vector< char >& binary )
    {
        ISC_STATUS_ARRAY status;

        int nBufferSize = (int)binary.size ();
        int nSegmentSize = g_nMaxSegmentLength; 
        int nCurrenPos = 0; 

        while ( nCurrenPos + nSegmentSize < nBufferSize )
        {
            if ( isc_put_segment ( status, phBlob, nSegmentSize, &binary[ nCurrenPos ] ) )
                throw std::runtime_error ( FormatError (status, "isc_put_segment" ) );
            nCurrenPos += nSegmentSize;
        }

        if ( isc_put_segment ( status, phBlob, nBufferSize - nCurrenPos, &binary[ nCurrenPos ] ) )
            throw std::runtime_error ( FormatError (status, "isc_put_segment" ) );
    }
    void blob_read ( isc_blob_handle* phBlob, std::vector< char >& binary )
    {
        ISC_STATUS_ARRAY status;

        char arrItemList[1] = { isc_info_blob_total_length };
        char bufRes [16];
        
        if ( isc_blob_info ( status, phBlob, 
                            sizeof (arrItemList), arrItemList,
                            sizeof (bufRes), bufRes ) )
        {
            throw std::runtime_error ( FormatError ( status, "isc_blob_info" ) );
        }

        long nBlobSize = isc_vax_integer ( &bufRes[3], *(short*)&bufRes[1] );
        if ( nBlobSize == 0 ) 
            return;
        else
            binary.resize ( nBlobSize );

        long nCurrenPos = 0; 
        unsigned short nBytesRead = 0;
        do
        {
            if ( isc_get_segment ( status, phBlob,
                                &nBytesRead,
                                g_nMaxSegmentLength, 
                                &binary[ nCurrenPos ] ))
            {
                throw std::runtime_error ( FormatError ( status, "isc_get_segment" ) );
            }

            nCurrenPos += nBytesRead;
        } while ( nCurrenPos < nBlobSize );
    }
public:
    CSQLData(int type, int subtype ) : m_type(type), m_subtype(subtype), m_binary(), m_flag0(0){}
    CSQLData(int type, int subtype, std::vector< char >& binary ) : m_type(type), m_subtype(subtype), m_binary (), m_flag0(0)
	{
        m_binary.swap (binary);
	}
	~CSQLData() 
	{ 
	}
	char* get() 
	{
        return utils::GetBeginOf ( m_binary ); 
	}
    void fill ( XSQLVAR* pXSQLVAR, isc_db_handle* pDatabaseConnection, isc_tr_handle* pCurrentTransaction )
    {
        ISC_STATUS_ARRAY status;

        isc_blob_handle hBlob = NULL;
        if ( isc_create_blob2 ( status
                                , pDatabaseConnection, pCurrentTransaction
                                , &hBlob, &m_idBlob
                                , 0, 0 ) )
        { 
            throw std::runtime_error ( FormatError (status, "isc_create_blob2" ) );
        }

        CBlobGuard hBlobGuard ( hBlob );

        if ( m_binary.empty () )
            m_flag0 = g_nNullValue;
        else
            blob_write ( &hBlob, m_binary );

		pXSQLVAR->sqldata = (char*)&m_idBlob;
    	pXSQLVAR->sqlind  = &m_flag0;
		pXSQLVAR->sqltype = type() + 1;
		pXSQLVAR->sqllen = sizeof (m_idBlob);
    }
    void prepare ( XSQLVAR* pXSQLVAR )
    {
		pXSQLVAR->sqldata = (char*)&m_idBlob;
		pXSQLVAR->sqlind  = &m_flag0;
    }
    void serialize ( cmn::BufferWriter& cmnBufferWriter, isc_db_handle* pDatabaseConnection, isc_tr_handle* pCurrentTransaction )
    {
        if ( m_flag0 == g_nNullValue )
        {
            if ( m_subtype == 1 )
                cmnBufferWriter.write_wstring ( L"" );
            else
                cmnBufferWriter.write_buffer ( 0, 0 );

            return;
        }

        ISC_STATUS_ARRAY status;

        isc_blob_handle hBlob = NULL;
        if ( isc_open_blob2 ( status,
                            pDatabaseConnection, pCurrentTransaction,
                            &hBlob, &m_idBlob, 
                            0, 0 ) )
        {
            throw std::runtime_error ( FormatError ( status, "isc_open_blob2" ) );
        }

        CBlobGuard hBlobGuard (hBlob);
        blob_read ( &hBlob, m_binary );

        if ( m_subtype == 1 )
        {
            if ( m_binary.empty () )
            {
                cmnBufferWriter.write_wstring2 ( L"" );
            }
            else
            {
                std::wstring wstr;
                wstr.assign ( (wchar_t*)&m_binary[0], m_binary.size () / 2 );
                cmnBufferWriter.write_wstring2 ( wstr );
            }
        }
        else
        {
            if ( m_binary.empty () )
                cmnBufferWriter.write_buffer ( 0, 0 );
            else
                cmnBufferWriter.write_buffer ( &m_binary[0], m_binary.size () );
        }
    }
	int type() const { return m_type; }
	int size() const 
	{
		return (int)m_binary.size (); 
	}
};

//*********************************************************************************************
//--- CDataHelperArray Declaration
//*********************************************************************************************
class CDataHelperArray
{
	std::vector< ISQLData* > m_data;

public:
	~CDataHelperArray();

public:
    void fill( XSQLDA* p_xsqlda, isc_db_handle* pDatabaseConnection, isc_tr_handle* pCurrentTransaction );
	void prepare( XSQLDA* p_xsqlda );
    void serialize ( cmn::BufferWriter& cmnBufferWriter, isc_db_handle* pDatabaseConnection, isc_tr_handle* pCurrentTransaction );

	ISQLData* operator [] (unsigned int index);
	int size();
	void insert ( std::auto_ptr<ISQLData> pData );
};

//*********************************************************************************************
}//namespace fb
//*********************************************************************************************
#endif
//*********************************************************************************************

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Chief Technology Officer Apriorit Inc.
United States United States
ApriorIT is a software research and development company specializing in cybersecurity and data management technology engineering. We work for a broad range of clients from Fortune 500 technology leaders to small innovative startups building unique solutions.

As Apriorit offers integrated research&development services for the software projects in such areas as endpoint security, network security, data security, embedded Systems, and virtualization, we have strong kernel and driver development skills, huge system programming expertise, and are reals fans of research projects.

Our specialty is reverse engineering, we apply it for security testing and security-related projects.

A separate department of Apriorit works on large-scale business SaaS solutions, handling tasks from business analysis, data architecture design, and web development to performance optimization and DevOps.

Official site: https://www.apriorit.com
Clutch profile: https://clutch.co/profile/apriorit
This is a Organisation

33 members

Written By
Software Developer (Senior) ApriorIT
Ukraine Ukraine
Senior Software Developer of Apriorit Inc.
My favorite tasks are multithreading, networking and WDK.

... But in free time : beer, meat, travelling and photo...)

LinkedIn Profile : Wineblat Eugene

Comments and Discussions