#pragma once
#include <Typelist.h>
#include <DataGenerators.h>
#include <boost/shared_ptr.hpp>
#include "ForwardDeclares.h"
#include "SQLValue.h"
#include "DBException.h"
#include "QueryNode.h"
#include "QuerySQLGenVisitor.h"
#include "PersistentObject.h"
#include <vector>
#include <iterator>
#include <string>
#include <sstream>
#include "afxdb.h"
namespace TemplateDB
{
class TableDefInterface
{
protected:
TableDefInterface() {}
public:
virtual ~TableDefInterface() {}
virtual bool IsBOF() const = 0;
virtual bool IsEOF() const = 0;
virtual void First() = 0;
virtual void Last() = 0;
virtual void Next() = 0;
virtual void Prev() = 0;
virtual void Requery() = 0;
virtual const SQLValue &GetFieldValue( unsigned int index ) const = 0;
virtual const ColumnDefInterface &GetColumnDef( unsigned int index ) const = 0;
};
// DBDEF is the name of the database class
// TABLENAME is the name of the table
// COLDEFS is a typelist of column definitions
template < class DBDEF, char *TABLENAME, class COLDEFS >
class TableDef : public TableDefInterface
{
public:
typedef typename boost::shared_ptr< TableDef< DBDEF, TABLENAME, COLDEFS > > _ptr;
static _ptr GetTableDef();
static _ptr GetTableDef( const WhereClause &where );
protected:
TableDef();
TableDef( const WhereClause &where );
public:
virtual ~TableDef();
// Navigation functions
virtual bool IsBOF() const;
virtual bool IsEOF() const;
virtual void First();
virtual void Last();
virtual void Next();
virtual void Prev();
// Force a requery of the dataset
virtual void Requery();
// Access the data
virtual const SQLValue &GetFieldValue( unsigned int index ) const;
virtual const ColumnDefInterface &GetColumnDef( unsigned int index ) const;
// Return the number of columns in this class
static unsigned int GetNumberOfColumns();
// Return the first column def with the primary key set
static ColumnDefInterface *GetPrimaryKey();
static unsigned int GetPrimaryKeyColumnIndex();
public:
// API for create persistent objects on this table
typedef typename PersistentObject<DBDEF, TABLENAME, COLDEFS>::_ptr persistentObjectPtr;
persistentObjectPtr NewRow();
persistentObjectPtr NewRow( const PersistentObjectPtr &dependentEntry );
persistentObjectPtr AccessRow();
protected:
// Get the SQL string to open the record set
std::string GetOpenSQL();
// Assert that the record set is open
void OpenRecordSet();
// Create the columns ready to receive data
void CreateColumns();
// Ensure that data from the record set is copied down into this object
void SyncData();
private:
CRecordset m_recordSet;
std::vector<ColumnDefInterface*> m_columnDefs;
WhereClause m_whereClause;
template <typename T>
struct Create
{
T *operator()()
{
return T::Create();
}
};
template <class TList>
struct FindPrimaryKey
{
typedef typename TList::Head Head;
typedef typename TList::Tail Tail;
private:
// Main template IfThenElse handles the default - ie true
template<bool C, class T, class F>
struct IfThenElse
{
// True variant
typedef typename T Result;
};
// Partial specialisation for the alternate case - false
template<class T, class F>
struct IfThenElse<false, T, F>
{
// False variant
typedef typename F Result;
};
// Main template returns the Head if it is the PrimaryKey, or recurses onto the Tail
template<class TList1>
struct In
{
typedef typename TList1::Head Head;
typedef typename TList1::Tail Tail;
typedef typename IfThenElse<Head::IsPrimaryKey, Head, typename In<Tail>::Result>::Result Result;
};
// Partial specialisation to end recursion - returns the Head of the list by default
template<>
struct In< ::Loki::NullType >
{
typedef typename Head Result;
};
public:
typedef typename In<TList>::Result Result;
};
};
template < class DBDEF, char *TABLENAME, class COLDEFS >
typename TableDef<DBDEF, TABLENAME, COLDEFS>::_ptr
TableDef<DBDEF, TABLENAME, COLDEFS>::GetTableDef()
{
return _ptr( new TableDef<DBDEF, TABLENAME, COLDEFS>() );
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
typename TableDef<DBDEF, TABLENAME, COLDEFS>::_ptr
TableDef<DBDEF, TABLENAME, COLDEFS>::GetTableDef( const WhereClause &where )
{
return _ptr( new TableDef<DBDEF, TABLENAME, COLDEFS>( where ) );
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
TableDef<DBDEF, TABLENAME, COLDEFS>::TableDef() :
m_recordSet( DBDEF::MySingleton::Instance().GetDatabase() ),
m_columnDefs( Loki::TL::Length< COLDEFS >::value ),
m_whereClause()
{
CreateColumns();
First();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
TableDef<DBDEF, TABLENAME, COLDEFS>::TableDef( const WhereClause &where ) :
m_recordSet( DBDEF::MySingleton::Instance().GetDatabase() ),
m_columnDefs( Loki::TL::Length< COLDEFS >::value ),
m_whereClause( where )
{
CreateColumns();
First();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
TableDef<DBDEF, TABLENAME, COLDEFS>::~TableDef()
{
if ( m_recordSet.IsOpen() )
m_recordSet.Close();
// Empty the column list
std::vector<ColumnDefInterface*>::iterator iter = m_columnDefs.begin();
for ( ; iter != m_columnDefs.end(); ++iter )
if ( (*iter) )
delete (*iter);
m_columnDefs.clear();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
bool
TableDef<DBDEF, TABLENAME, COLDEFS>::IsBOF() const
{
if ( !m_recordSet.IsOpen() )
throw DBException( "RecordSet is not yet open" );
return m_recordSet.IsBOF() ? true : false;
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
bool
TableDef<DBDEF, TABLENAME, COLDEFS>::IsEOF() const
{
if ( !m_recordSet.IsOpen() )
throw DBException( "RecordSet is not yet open" );
return m_recordSet.IsEOF() ? true : false;
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
void
TableDef<DBDEF, TABLENAME, COLDEFS>::First()
{
OpenRecordSet();
if ( IsBOF() && IsEOF() )
return;
m_recordSet.MoveFirst();
SyncData();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
void
TableDef<DBDEF, TABLENAME, COLDEFS>::Last()
{
OpenRecordSet();
if ( IsBOF() && IsEOF() )
return;
m_recordSet.MoveLast();
SyncData();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
void
TableDef<DBDEF, TABLENAME, COLDEFS>::Next()
{
if ( !m_recordSet.IsOpen() )
throw DBException( "RecordSet is not yet open" );
m_recordSet.MoveNext();
SyncData();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
void
TableDef<DBDEF, TABLENAME, COLDEFS>::Prev()
{
if ( !m_recordSet.IsOpen() )
throw DBException( "RecordSet is not yet open" );
m_recordSet.MovePrev();
SyncData();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
void
TableDef<DBDEF, TABLENAME, COLDEFS>::Requery()
{
if ( !m_recordSet.IsOpen() )
throw DBException( "RecordSet is not yet open" );
m_recordSet.Requery();
First();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
const SQLValue &
TableDef<DBDEF, TABLENAME, COLDEFS>::GetFieldValue( unsigned int index ) const
{
if ( !m_recordSet.IsOpen() )
throw DBException( "RecordSet is not yet open" );
if ( index >= m_columnDefs.size() )
throw DBException( "Attempt to access field out of range" );
return m_columnDefs[ index ]->GetValue();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
const ColumnDefInterface &
TableDef<DBDEF, TABLENAME, COLDEFS>::GetColumnDef( unsigned int index ) const
{
if ( !m_recordSet.IsOpen() )
throw DBException( "RecordSet is not yet open" );
if ( index >= m_columnDefs.size() )
throw DBException( "Attempt to access field out of range" );
return *m_columnDefs[ index ];
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
unsigned int
TableDef<DBDEF, TABLENAME, COLDEFS>::GetNumberOfColumns()
{
return Loki::TL::Length< COLDEFS >::value;
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
ColumnDefInterface *
TableDef<DBDEF, TABLENAME, COLDEFS>::GetPrimaryKey()
{
return FindPrimaryKey<COLDEFS>::Result::Create();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
unsigned int
TableDef<DBDEF, TABLENAME, COLDEFS>::GetPrimaryKeyColumnIndex()
{
return Loki::TL::IndexOf<COLDEFS, FindPrimaryKey<COLDEFS>::Result>::value;
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
typename TableDef<DBDEF, TABLENAME, COLDEFS>::persistentObjectPtr
TableDef<DBDEF, TABLENAME, COLDEFS>::NewRow()
{
PersistentObject<DBDEF, TABLENAME, COLDEFS>::_ptr row = PersistentObject<DBDEF, TABLENAME, COLDEFS>::GetPersistentObject();
row->PersistentObjectInterface::Create();
return row;
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
typename TableDef<DBDEF, TABLENAME, COLDEFS>::persistentObjectPtr
TableDef<DBDEF, TABLENAME, COLDEFS>::NewRow( const PersistentObjectPtr &dependentEntry )
{
PersistentObject<DBDEF, TABLENAME, COLDEFS>::_ptr row = PersistentObject<DBDEF, TABLENAME, COLDEFS>::GetPersistentObject();
row->PersistentObjectInterface::Create( dependentEntry );
return row;
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
typename TableDef<DBDEF, TABLENAME, COLDEFS>::persistentObjectPtr
TableDef<DBDEF, TABLENAME, COLDEFS>::AccessRow()
{
PersistentObject<DBDEF, TABLENAME, COLDEFS>::_ptr row = PersistentObject<DBDEF, TABLENAME, COLDEFS>::GetPersistentObject();
row->CopyInColumns( m_columnDefs );
return row;
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
std::string
TableDef<DBDEF, TABLENAME, COLDEFS>::GetOpenSQL()
{
std::ostringstream str;
str << "SELECT ";
std::vector<ColumnDefInterface*>::const_iterator iter = m_columnDefs.begin();
if ( iter == m_columnDefs.end() )
str << "* ";
else
{
// Special case for first to ensure commas work
str << (*iter)->GetColumnName();
for ( ++iter; iter != m_columnDefs.end(); ++iter )
str << ", " << (*iter)->GetColumnName();
}
str << " FROM ";
str << std::string( TABLENAME );
str << " WHERE ";
QuerySQLGenVisitor visitor;
m_whereClause.Accept( visitor );
str << visitor.GetSQL();
return str.str();
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
void
TableDef<DBDEF, TABLENAME, COLDEFS>::OpenRecordSet()
{
if ( m_recordSet.IsOpen() )
return;
// Open the recordset
m_recordSet.m_nFields = 0;
m_recordSet.Open( CRecordset::snapshot, GetOpenSQL().c_str(), CRecordset::readOnly );
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
void
TableDef<DBDEF, TABLENAME, COLDEFS>::CreateColumns()
{
// Empty the old list
std::vector<ColumnDefInterface*>::iterator iter = m_columnDefs.begin();
for ( ; iter != m_columnDefs.end(); ++iter )
if ( (*iter) )
delete (*iter );
m_columnDefs.clear();
// Iterate over the typelist, creating ColumnDef records from the data in the recordset
Loki::TL::iterate_types< COLDEFS, Create, std::back_insert_iterator< std::vector<ColumnDefInterface*> > >( std::back_inserter( m_columnDefs ) );
}
template < class DBDEF, char *TABLENAME, class COLDEFS >
void
TableDef<DBDEF, TABLENAME, COLDEFS>::SyncData()
{
if ( !m_recordSet.IsOpen() )
throw DBException( "RecordSet is not yet open" );
// Cannot sync data if we are at BOF or EOF
if ( IsEOF() || IsBOF() )
return;
// Now populate the data with actual values
std::vector<ColumnDefInterface*>::iterator iter = m_columnDefs.begin();
short ii = 0;
for ( ; iter != m_columnDefs.end(); ++iter, ++ii )
{
CDBVariant value;
m_recordSet.GetFieldValue( ii, value );
(*iter)->AcquireValue( value );
}
}
} // namespace TemplateDB