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
41namespace FIX
42{
43
44const std::string OdbcStoreFactory::DEFAULT_USER = "sa";
45const std::string OdbcStoreFactory::DEFAULT_PASSWORD = "";
46const std::string OdbcStoreFactory::DEFAULT_CONNECTION_STRING
47 = "DATABASE=quickfix;DRIVER={SQL Server};SERVER=(local);";
48
49OdbcStore::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
58OdbcStore::~OdbcStore()
59{
60 delete m_pConnection;
61}
62
63void 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
131MessageStore* 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
141void OdbcStoreFactory::destroy( MessageStore* pStore )
142{
143 delete pStore;
144}
145
146MessageStore* 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
164bool OdbcStore::set( int msgSeqNum, const std::string& msg )
165throw ( 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
199void OdbcStore::get( int begin, int end,
200 std::vector < std::string > & result ) const
201throw ( 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
234int OdbcStore::getNextSenderMsgSeqNum() const throw ( IOException )
235{
236 return m_cache.getNextSenderMsgSeqNum();
237}
238
239int OdbcStore::getNextTargetMsgSeqNum() const throw ( IOException )
240{
241 return m_cache.getNextTargetMsgSeqNum();
242}
243
244void 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
258void 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
274void OdbcStore::incrNextSenderMsgSeqNum() throw ( IOException )
275{
276 m_cache.incrNextSenderMsgSeqNum();
277 setNextSenderMsgSeqNum( m_cache.getNextSenderMsgSeqNum() );
278}
279
280void OdbcStore::incrNextTargetMsgSeqNum() throw ( IOException )
281{
282 m_cache.incrNextTargetMsgSeqNum();
283 setNextTargetMsgSeqNum( m_cache.getNextTargetMsgSeqNum() );
284}
285
286UtcTimeStamp OdbcStore::getCreationTime() const throw ( IOException )
287{
288 return m_cache.getCreationTime();
289}
290
291void 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
330void OdbcStore::refresh() throw ( IOException )
331{
332 m_cache.reset();
333 populateCache();
334}
335
336}
337
338#endif
#define STRING_SPRINTF
Definition Utility.h:222
void string_replace(const std::string &oldValue, const std::string &newValue, std::string &value)
Definition Utility.cpp:40

Generated on Thu Feb 29 2024 22:38:19 for QuickFIX by doxygen 1.9.8 written by Dimitri van Heesch, © 1997-2001