OdbcStore.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 ** Copyright (c) 2001-2014
3 **
4 ** This file is part of the QuickFIX FIX Engine
5 **
6 ** This file may be distributed under the terms of the quickfixengine.org
7 ** license as defined by quickfixengine.org and appearing in the file
8 ** LICENSE included in the packaging of this file.
9 **
10 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
11 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12 **
13 ** See http://www.quickfixengine.org/LICENSE for licensing information.
14 **
15 ** Contact ask@quickfixengine.org if any conditions of this licensing are
16 ** not clear to you.
17 **
18 ****************************************************************************/
19 
20 #ifdef _MSC_VER
21 #include "stdafx.h"
22 #else
23 #include "config.h"
24 #endif
25 
26 #ifdef HAVE_ODBC
27 
28 #ifndef SQLLEN
29 #define SQLLEN SQLINTEGER
30 #endif
31 
32 #include "OdbcStore.h"
33 #include "SessionID.h"
34 #include "SessionSettings.h"
35 #include "FieldConvertors.h"
36 #include "Parser.h"
37 #include "Utility.h"
38 #include "strptime.h"
39 #include <fstream>
40 
41 namespace FIX
42 {
43 
44 const std::string OdbcStoreFactory::DEFAULT_USER = "sa";
45 const std::string OdbcStoreFactory::DEFAULT_PASSWORD = "";
46 const std::string OdbcStoreFactory::DEFAULT_CONNECTION_STRING
47  = "DATABASE=quickfix;DRIVER={SQL Server};SERVER=(local);";
48 
49 OdbcStore::OdbcStore
50 ( const SessionID& s, const std::string& user, const std::string& password,
51  const std::string& connectionString )
52  : m_sessionID( s )
53 {
54  m_pConnection = new OdbcConnection( user, password, connectionString );
55  populateCache();
56 }
57 
58 OdbcStore::~OdbcStore()
59 {
60  delete m_pConnection;
61 }
62 
63 void OdbcStore::populateCache()
64 {
65  std::stringstream queryString;
66 
67  queryString << "SELECT creation_time, incoming_seqnum, outgoing_seqnum FROM sessions WHERE "
68  << "beginstring=" << "'" << m_sessionID.getBeginString().getValue() << "' and "
69  << "sendercompid=" << "'" << m_sessionID.getSenderCompID().getValue() << "' and "
70  << "targetcompid=" << "'" << m_sessionID.getTargetCompID().getValue() << "' and "
71  << "session_qualifier=" << "'" << m_sessionID.getSessionQualifier() << "'";
72 
73  OdbcQuery query( queryString.str() );
74 
75  if( !m_pConnection->execute(query) )
76  throw ConfigError( "Unable to connect to database" );
77 
78  int rows = 0;
79  while( query.fetch() )
80  {
81  rows++;
82  if( rows > 1 )
83  throw ConfigError( "Multiple entries found for session in database" );
84 
85  SQL_TIMESTAMP_STRUCT creationTime;
86  SQLLEN creationTimeLength;
87  SQLGetData( query.statement(), 1, SQL_C_TYPE_TIMESTAMP, &creationTime, 0, &creationTimeLength );
88  SQLLEN incomingSeqNum;
89  SQLLEN incomingSeqNumLength;
90  SQLGetData( query.statement(), 2, SQL_C_SLONG, &incomingSeqNum, 0, &incomingSeqNumLength );
91 
92  SQLLEN outgoingSeqNum;
93  SQLLEN outgoingSeqNumLength;
94  SQLGetData( query.statement(), 3, SQL_C_SLONG, &outgoingSeqNum, 0, &outgoingSeqNumLength );
95 
96  UtcTimeStamp time;
97  time.setYMD( creationTime.year, creationTime.month, creationTime.day );
98  time.setHMS( creationTime.hour, creationTime.minute, creationTime.second, creationTime.fraction );
99  m_cache.setCreationTime( time );
100  m_cache.setNextTargetMsgSeqNum( incomingSeqNum );
101  m_cache.setNextSenderMsgSeqNum( outgoingSeqNum );
102  }
103  query.close();
104 
105  if( rows == 0 )
106  {
107  UtcTimeStamp time = m_cache.getCreationTime();
108  char sqlTime[ 20 ];
109  int year, month, day, hour, minute, second, millis;
110  time.getYMD (year, month, day);
111  time.getHMS (hour, minute, second, millis);
112  STRING_SPRINTF (sqlTime, "%d-%02d-%02d %02d:%02d:%02d",
113  year, month, day, hour, minute, second);
114  std::stringstream queryString2;
115  queryString2 << "INSERT INTO sessions (beginstring, sendercompid, targetcompid, session_qualifier,"
116  << "creation_time, incoming_seqnum, outgoing_seqnum) VALUES("
117  << "'" << m_sessionID.getBeginString().getValue() << "',"
118  << "'" << m_sessionID.getSenderCompID().getValue() << "',"
119  << "'" << m_sessionID.getTargetCompID().getValue() << "',"
120  << "'" << m_sessionID.getSessionQualifier() << "',"
121  << "{ts '" << sqlTime << "'},"
122  << m_cache.getNextTargetMsgSeqNum() << ","
123  << m_cache.getNextSenderMsgSeqNum() << ")";
124 
125  OdbcQuery query2( queryString2.str() );
126  if( !m_pConnection->execute(query2) )
127  throw ConfigError( "Unable to create session in database" );
128  }
129 }
130 
131 MessageStore* OdbcStoreFactory::create( const SessionID& s )
132 {
133  if( m_useSettings )
134  return create( s, m_settings.get(s) );
135  else if( m_useDictionary )
136  return create( s, m_dictionary );
137  else
138  return new OdbcStore( s, m_user, m_password, m_connectionString );
139 }
140 
141 void OdbcStoreFactory::destroy( MessageStore* pStore )
142 {
143  delete pStore;
144 }
145 
146 MessageStore* OdbcStoreFactory::create( const SessionID& s, const Dictionary& settings )
147 {
148  std::string user = DEFAULT_USER;
149  std::string password = DEFAULT_PASSWORD;
150  std::string connectionString = DEFAULT_CONNECTION_STRING;
151 
152  try { user = settings.getString( ODBC_STORE_USER ); }
153  catch( ConfigError& ) {}
154 
155  try { password = settings.getString( ODBC_STORE_PASSWORD ); }
156  catch( ConfigError& ) {}
157 
158  try { connectionString = settings.getString( ODBC_STORE_CONNECTION_STRING ); }
159  catch( ConfigError& ) {}
160 
161  return new OdbcStore( s, user, password, connectionString );
162 }
163 
164 bool OdbcStore::set( int msgSeqNum, const std::string& msg )
165 throw ( IOException )
166 {
167  std::string msgCopy = msg;
168  string_replace( "'", "''", msgCopy );
169 
170  std::stringstream queryString;
171  queryString << "INSERT INTO messages "
172  << "(beginstring, sendercompid, targetcompid, session_qualifier, msgseqnum, message) "
173  << "VALUES ("
174  << "'" << m_sessionID.getBeginString().getValue() << "',"
175  << "'" << m_sessionID.getSenderCompID().getValue() << "',"
176  << "'" << m_sessionID.getTargetCompID().getValue() << "',"
177  << "'" << m_sessionID.getSessionQualifier() << "',"
178  << msgSeqNum << ","
179  << "'" << msgCopy << "')";
180 
181  OdbcQuery query( queryString.str() );
182  if( !m_pConnection->execute(query) )
183  {
184  query.close();
185  std::stringstream queryString2;
186  queryString2 << "UPDATE messages SET message='" << msgCopy << "' WHERE "
187  << "beginstring=" << "'" << m_sessionID.getBeginString().getValue() << "' and "
188  << "sendercompid=" << "'" << m_sessionID.getSenderCompID().getValue() << "' and "
189  << "targetcompid=" << "'" << m_sessionID.getTargetCompID().getValue() << "' and "
190  << "session_qualifier=" << "'" << m_sessionID.getSessionQualifier() << "' and "
191  << "msgseqnum=" << msgSeqNum;
192  OdbcQuery query2( queryString2.str() );
193  if( !m_pConnection->execute(query2) )
194  query2.throwException();
195  }
196  return true;
197 }
198 
199 void OdbcStore::get( int begin, int end,
200  std::vector < std::string > & result ) const
201 throw ( IOException )
202 {
203  result.clear();
204  std::stringstream queryString;
205  queryString << "SELECT message FROM messages WHERE "
206  << "beginstring=" << "'" << m_sessionID.getBeginString().getValue() << "' and "
207  << "sendercompid=" << "'" << m_sessionID.getSenderCompID().getValue() << "' and "
208  << "targetcompid=" << "'" << m_sessionID.getTargetCompID().getValue() << "' and "
209  << "session_qualifier=" << "'" << m_sessionID.getSessionQualifier() << "' and "
210  << "msgseqnum>=" << begin << " and " << "msgseqnum<=" << end << " "
211  << "ORDER BY msgseqnum";
212 
213  OdbcQuery query( queryString.str() );
214 
215  if( !m_pConnection->execute(query) )
216  query.throwException();
217 
218  while( query.fetch() )
219  {
220  std::string message;
221  SQLVARCHAR messageBuffer[4096];
222  SQLLEN messageLength;
223 
224  while( odbcSuccess(SQLGetData( query.statement(), 1, SQL_C_CHAR, &messageBuffer, 4095, &messageLength)) )
225  {
226  messageBuffer[messageLength] = 0;
227  message += (char*)messageBuffer;
228  }
229 
230  result.push_back( message );
231  }
232 }
233 
234 int OdbcStore::getNextSenderMsgSeqNum() const throw ( IOException )
235 {
236  return m_cache.getNextSenderMsgSeqNum();
237 }
238 
239 int OdbcStore::getNextTargetMsgSeqNum() const throw ( IOException )
240 {
241  return m_cache.getNextTargetMsgSeqNum();
242 }
243 
244 void OdbcStore::setNextSenderMsgSeqNum( int value ) throw ( IOException )
245 {
246  std::stringstream queryString;
247  queryString << "UPDATE sessions SET outgoing_seqnum=" << value << " WHERE "
248  << "beginstring=" << "'" << m_sessionID.getBeginString().getValue() << "' and "
249  << "sendercompid=" << "'" << m_sessionID.getSenderCompID().getValue() << "' and "
250  << "targetcompid=" << "'" << m_sessionID.getTargetCompID().getValue() << "' and "
251  << "session_qualifier=" << "'" << m_sessionID.getSessionQualifier() << "'";
252  OdbcQuery query( queryString.str() );
253  if( !m_pConnection->execute(query) )
254  query.throwException();
255  m_cache.setNextSenderMsgSeqNum( value );
256 }
257 
258 void OdbcStore::setNextTargetMsgSeqNum( int value ) throw ( IOException )
259 {
260  std::stringstream queryString;
261  queryString << "UPDATE sessions SET incoming_seqnum=" << value << " WHERE "
262  << "beginstring=" << "'" << m_sessionID.getBeginString().getValue() << "' and "
263  << "sendercompid=" << "'" << m_sessionID.getSenderCompID().getValue() << "' and "
264  << "targetcompid=" << "'" << m_sessionID.getTargetCompID().getValue() << "' and "
265  << "session_qualifier=" << "'" << m_sessionID.getSessionQualifier() << "'";
266 
267  OdbcQuery query( queryString.str() );
268  if( !m_pConnection->execute(query) )
269  query.throwException();
270 
271  m_cache.setNextTargetMsgSeqNum( value );
272 }
273 
274 void OdbcStore::incrNextSenderMsgSeqNum() throw ( IOException )
275 {
276  m_cache.incrNextSenderMsgSeqNum();
277  setNextSenderMsgSeqNum( m_cache.getNextSenderMsgSeqNum() );
278 }
279 
280 void OdbcStore::incrNextTargetMsgSeqNum() throw ( IOException )
281 {
282  m_cache.incrNextTargetMsgSeqNum();
283  setNextTargetMsgSeqNum( m_cache.getNextTargetMsgSeqNum() );
284 }
285 
286 UtcTimeStamp OdbcStore::getCreationTime() const throw ( IOException )
287 {
288  return m_cache.getCreationTime();
289 }
290 
291 void OdbcStore::reset() throw ( IOException )
292 {
293  std::stringstream queryString;
294  queryString << "DELETE FROM messages WHERE "
295  << "beginstring=" << "'" << m_sessionID.getBeginString().getValue() << "' and "
296  << "sendercompid=" << "'" << m_sessionID.getSenderCompID().getValue() << "' and "
297  << "targetcompid=" << "'" << m_sessionID.getTargetCompID().getValue() << "' and "
298  << "session_qualifier=" << "'" << m_sessionID.getSessionQualifier() << "'";
299 
300  OdbcQuery query( queryString.str() );
301  if( !m_pConnection->execute(query) )
302  query.throwException();
303  query.close();
304 
305  m_cache.reset();
306  UtcTimeStamp time = m_cache.getCreationTime();
307 
308  int year, month, day, hour, minute, second, millis;
309  time.getYMD( year, month, day );
310  time.getHMS( hour, minute, second, millis );
311 
312  char sqlTime[ 20 ];
313  STRING_SPRINTF( sqlTime, "%d-%02d-%02d %02d:%02d:%02d",
314  year, month, day, hour, minute, second );
315 
316  std::stringstream queryString2;
317  queryString2 << "UPDATE sessions SET creation_time={ts '" << sqlTime << "'}, "
318  << "incoming_seqnum=" << m_cache.getNextTargetMsgSeqNum() << ", "
319  << "outgoing_seqnum=" << m_cache.getNextSenderMsgSeqNum() << " WHERE "
320  << "beginstring=" << "'" << m_sessionID.getBeginString().getValue() << "' and "
321  << "sendercompid=" << "'" << m_sessionID.getSenderCompID().getValue() << "' and "
322  << "targetcompid=" << "'" << m_sessionID.getTargetCompID().getValue() << "' and "
323  << "session_qualifier=" << "'" << m_sessionID.getSessionQualifier() << "'";
324 
325  OdbcQuery query2( queryString2.str() );
326  if( !m_pConnection->execute(query2) )
327  query2.throwException();
328 }
329 
330 void OdbcStore::refresh() throw ( IOException )
331 {
332  m_cache.reset();
333  populateCache();
334 }
335 
336 }
337 
338 #endif
SessionID.h
OdbcStore.h
SessionSettings.h
FIX::ODBC_STORE_PASSWORD
const char ODBC_STORE_PASSWORD[]
Definition: SessionSettings.h:111
FIX::ODBC_STORE_CONNECTION_STRING
const char ODBC_STORE_CONNECTION_STRING[]
Definition: SessionSettings.h:112
FIX::TYPE::UtcTimeStamp
@ UtcTimeStamp
Definition: FieldTypes.h:940
FieldConvertors.h
FIX::string_replace
void string_replace(const std::string &oldValue, const std::string &newValue, std::string &value)
Definition: Utility.cpp:57
Parser.h
STRING_SPRINTF
#define STRING_SPRINTF
Definition: Utility.h:222
FIX
Definition: Acceptor.cpp:34
Utility.h
FIX::ODBC_STORE_USER
const char ODBC_STORE_USER[]
Definition: SessionSettings.h:110

Generated on Wed Apr 29 2020 19:41:30 for QuickFIX by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2001