-
Notifications
You must be signed in to change notification settings - Fork 169
/
Copy pathSessionImpl.h
511 lines (403 loc) · 16.2 KB
/
SessionImpl.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
/************************************************************************
* Copyright(c) 2011, One Unified. All rights reserved. *
* email: [email protected] *
* *
* This file is provided as is WITHOUT ANY WARRANTY *
* without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* *
* This software may not be used nor distributed without proper license *
* agreement. *
* *
* See the file LICENSE.txt for redistribution information. *
************************************************************************/
#pragma once
// generic header for session management
// header is designed so only one type of database backend can be linked in
// will need to work with namespace stuff in order to allow multiple number of database backends
// 2011-01-30
// need to work on:
// getting key back when inserting new record with auto-increment
// adding transactions with rollback and commit
// OUSqlite Library
// was going to be pimpl, but couldn't, as templates are in use, no 'export' keyword
// includes are a bit messy, a compendium of ousql/session, ousql/sessionimpl, and this session file
// maybe using Spirit::Phoenix tuplies will get me out of this mess?
// 2011/04/25
// need to add 'constraint unique ... ' clause during table creation (needs revaming of constraint symbology)
// need to add ''create index ... ' clause during table creation
// need to add transaction/commit/rollback capabilities
// 2012/10/13
// QueryFields has a problem. Things have to be re-written so that a statement can be prepared
// independently of a supplied structure. A new structure may be required on each execution of the statement.
// Currently, the same physical structure needs to be re-used. Structure is provided during statement construction,
// not necessarily a good thing all the time.
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <typeinfo>
#include <stdexcept>
#include <boost/noncopyable.hpp>
#include <boost/intrusive_ptr.hpp>
#include "Constants.h"
#include "Actions.h"
namespace ou {
namespace db {
// dummy place holder for when no fields to bind
// will need to figure out a better way to handle this
struct NoBind {
template<class A>
void Fields( A& a ) { };
};
// CQuery contains:
// * SS: statement state structures dedicated to the specific database
// * F: struct containing Fields function with ou::db::Field calls
// 2011-01-30 need to modify the structures so they can handle instance when there is no F.
class QueryBase { // used as base representation for stowage in vectors and such
protected:
enum enumClause { EClauseNone, EClauseQuery, EClauseWhere, EClauseOrderBy, EClauseGroupBy, EClauseBind, EClauseNoExecute };
public:
using pQueryBase_t = boost::intrusive_ptr<QueryBase>;
QueryBase(): m_clause( EClauseNone ), m_bHasFields( false ), m_cntRef( 0 ), m_bPrepared( false ) {};
virtual ~QueryBase() {};
void SetHasFields() { m_bHasFields = true; };
inline bool HasFields() const { return m_bHasFields; };
void SetPrepared() { m_bPrepared = true; };
inline bool IsPrepared() const { return m_bPrepared; };
// instrusive reference counting
size_t RefCnt() const { return m_cntRef; };
void Ref() { ++m_cntRef; };
size_t UnRef() { --m_cntRef; return m_cntRef; };
std::string& UpdateQueryText() {
// assert( EClauseQuery >= m_clause );
// m_clause = EClauseQuery;
return m_sQueryText;
};
const std::string& QueryText() const { return m_sQueryText; };
protected:
enumClause m_clause;
std::string m_sQueryText; // 'compose' results end up here
private:
bool m_bHasFields;
size_t m_cntRef;
bool m_bPrepared; // used by session.execute
};
// =====
template<class F> // used for returning structures to queriers
class QueryFields:
public QueryBase
{
public:
using pQueryFields_t = boost::intrusive_ptr<QueryFields<F> >;
explicit QueryFields( F& f ): QueryBase(), var( f ) {};
virtual ~QueryFields() {};
F& var; // 2011/03/07 I want to make this a reference to a constant var at some point
// will require mods to Bind, and will need Fields( A& a ) const, hopefully will work with the Actions passed in
protected:
private:
};
// =====
template<class F>
class Query: public QueryFields<F> { // rename to Query once SessionImpl Query has been renamed successfully
public:
explicit Query( F& f )
: m_bExecuteOneTime( false ), QueryFields<F>( f ) {
};
~Query() {};
Query& Where( const std::string& sWhere ) { // todo: ensure sub clause ordering
assert( QueryBase::EClauseWhere > QueryBase::m_clause );
QueryBase::m_sQueryText += " WHERE " + sWhere;
QueryBase::m_clause = QueryBase::EClauseWhere;
return *this;
};
Query& OrderBy( const std::string& sOrderBy ) { // todo: ensure sub clause ordering
assert( QueryBase::EClauseOrderBy > QueryBase::m_clause );
QueryBase::m_sQueryText += " ORDER BY " + sOrderBy;
QueryBase::m_clause = QueryBase::EClauseOrderBy;
return *this;
}
Query& GroupBy( const std::string& sGroupBy ) {
assert( QueryBase::EClauseGroupBy > QueryBase::m_clause );
QueryBase::m_sQueryText += " GROUP BY " + sGroupBy;
QueryBase::m_clause = QueryBase::EClauseGroupBy;
return *this;
}
Query& NoExecute() {
assert( QueryBase::EClauseNoExecute > QueryBase::m_clause );
m_bExecuteOneTime = false; // don't execute on the conversion
QueryBase::m_clause = QueryBase::EClauseNoExecute;
return *this;
}
// operator QueryFields<F>() {
// return dynamic_cast<QueryFields<F> >( *this );
// }
// conversion operator: upon conversion from QueryState to QueryFields (upon assignment),
// execute the bind and execute may need to add an auto-reset before the bind:
// therefore need m_bBound member variable
/*
operator QueryFields<F>*() {
if ( m_bExecuteOneTime ) {
ProcessInQueryState();
m_bExecuteOneTime = false;
}
return dynamic_cast<QueryFields<F>* >( this );
}
*/
/*
operator QueryFields<F>&() {
if ( m_bExecuteOneTime ) {
ProcessInQueryState();
m_bExecuteOneTime = false;
}
return dynamic_cast<QueryFields<F>&>( *this );
}
*/
// reference:
// https://en.cppreference.com/w/cpp/language/cast_operator
// conversion operator
operator typename QueryFields<F>::pQueryFields_t() {
if ( m_bExecuteOneTime ) {
ProcessInQueryState();
m_bExecuteOneTime = false;
}
typename QueryFields<F>::pQueryFields_t p( this );
return p;
}
void SetExecuteOneTime() { m_bExecuteOneTime = true; };
protected:
virtual void ProcessInQueryState() {};
bool m_bExecuteOneTime;
private:
};
// functions for intrusive ptr of the query structures
template<class Q> // Q = Query
void intrusive_ptr_add_ref( Q* pq ) {
pq->Ref();
}
template<class Q>
void intrusive_ptr_release( Q* pq ) {
if ( 0 == pq->UnRef() ) {
delete pq;
}
}
// =====
template<class SS, class F, class S> // SS statement state, F fields, S session
class QueryState:
public Query<F>,
public SS // statement state
{
// friend S; // ** g++ does not like this, need to provide specific function?
public:
QueryState( S& session, F& f ): Query<F>( f ), m_session( session ) {};
virtual ~QueryState() {
m_session.Release( *this );
};
protected:
void ProcessInQueryState() {
if ( QueryBase::HasFields() ) { // who calls this, there fore had to virtual the destructor
m_session.Bind( *this );
}
m_session.Execute( *this );
}
private:
S& m_session;
};
//
// SessionImpl
template<class IDatabase> // IDatabase is a the specific handler type: sqlite3 or pg or ...
class SessionImpl: boost::noncopyable {
public:
using session_t = SessionImpl<IDatabase>;
using pSession_t = std::shared_ptr<session_t>;
using pQueryBase_t = QueryBase::pQueryBase_t;
SessionImpl();
virtual ~SessionImpl();
void ImplOpen( const std::string& sDbFileName, enumOpenFlags flags = EOpenFlagsZero );
void ImplClose();
void CreateTables();
int64_t GetLastRowId() { return m_db.GetLastRowId(); }; // call after an auto-increment insertion
template<class F>
void Bind( QueryFields<F>& qf ) {
typename IDatabase::structStatementState& StatementState
= dynamic_cast<typename IDatabase::structStatementState&>( qf );
if ( !qf.IsPrepared() ) {
m_db.PrepareStatement( StatementState, qf.UpdateQueryText() );
qf.SetPrepared();
}
typename IDatabase::Action_Bind_Values action( StatementState );
qf.var.Fields( action );
}
template<class F>
void Bind( typename QueryFields<F>::pQueryFields_t pQuery ) {
Bind( *pQuery.get() );
}
void Bind( QueryFields<NoBind>& qf ) {
}
bool Execute( QueryBase& qb ) {
typename IDatabase::structStatementState& StatementState
= dynamic_cast<typename IDatabase::structStatementState&>( qb );
if ( !qb.IsPrepared() ) {
m_db.PrepareStatement( StatementState, qb.UpdateQueryText() );
qb.SetPrepared();
}
return m_db.ExecuteStatement( StatementState );
}
bool Execute( pQueryBase_t pQuery ) {
return Execute( *pQuery.get() );
}
template<class F, class C>
void Columns( typename QueryFields<F>::pQueryFields_t pQuery, C& columns ) {
typename IDatabase::structStatementState& StatementState
= *dynamic_cast<typename IDatabase::structStatementState*>( pQuery.get() );
typename IDatabase::Action_Extract_Columns action( StatementState );
columns.Fields( action );
}
void Reset( pQueryBase_t pQuery ) {
typename IDatabase::structStatementState& StatementState
= *dynamic_cast<typename IDatabase::structStatementState*>( pQuery.get() );
m_db.ResetStatement( StatementState );
}
template<class F> // T: Table Class with TableDef member function
QueryState<typename IDatabase::structStatementState, F, session_t>& RegisterTable( const std::string& sTableName ) {
MapRowDefToTableName<F>( sTableName );
mapTableDefs_iter_t iter = m_mapTableDefs.find( sTableName );
if ( m_mapTableDefs.end() != iter ) {
throw std::runtime_error( "table name already has definition" );
}
// test template getting at type without instantiating variable: complains about static call to non static function
// use full specialization or partial specialization
typedef QueryState<typename IDatabase::structStatementState, F, session_t> query_t;
F f; // warning, this variable goes out of scope before the query is destroyed
query_t* pQuery = new query_t( *this, f );
typename IDatabase::Action_Assemble_TableDef action( sTableName );
f.Fields( action );
if ( 0 < action.FieldCount() ) pQuery->SetHasFields();
action.ComposeCreateStatement( pQuery->UpdateQueryText() );
iter = m_mapTableDefs.insert(
m_mapTableDefs.begin(),
mapTableDefs_pair_t( sTableName, pQuery) );
return *pQuery;
}
template<class F> // do reset, auto bind when doing execute
QueryState<typename IDatabase::structStatementState, F, session_t>& Insert( F& f ) {
return ComposeSql<F, typename IDatabase::Action_Compose_Insert>( f );
}
template<class F> // do reset, auto bind when doing execute
QueryState<typename IDatabase::structStatementState, F, session_t>& Update( F& f ) {
return ComposeSql<F, typename IDatabase::Action_Compose_Update>( f );
}
template<class F> // do reset, auto bind when doing execute
QueryState<typename IDatabase::structStatementState, F, session_t>& Delete( F& f ) {
return ComposeSql<F, typename IDatabase::Action_Compose_Delete>( f );
}
// also need non-F specialization as there may be no fields involved in some queries
// todo: need to do field processing, so can get field count, so need a processing action
template<class F> // do reset, auto bind if variables exist
QueryState<typename IDatabase::structStatementState, F, session_t>& SQL( const std::string& sSqlQuery, F& f ) {
typedef QueryState<typename IDatabase::structStatementState, F, session_t> query_t;
query_t* pQuery = new query_t( *this, f );
Action_Compose action;
f.Fields( action );
if ( 0 < action.FieldCount() ) pQuery->SetHasFields();
pQuery->UpdateQueryText() = sSqlQuery;
pQuery->SetExecuteOneTime();
return *pQuery;
}
// query with no parameters
template<class F>
QueryState<typename IDatabase::structStatementState, F, session_t>& SQL( const std::string& sSqlQuery ) {
F f; // warning, this variable goes out of scope before the query is destroyed
return SQL( sSqlQuery, f );
}
void Release( typename IDatabase::structStatementState& statement ) {
m_db.CloseStatement( statement );
}
template<class F>
void MapRowDefToTableName( const std::string& sTableName ) {
std::string sF( typeid( F ).name() );
mapFieldsToTable_iter_t iter = m_mapFieldsToTable.find( sF );
if ( m_mapFieldsToTable.end() == iter ) {
m_mapFieldsToTable[ sF ] = sTableName;
}
else {
if ( iter->second != sTableName ) {
throw std::runtime_error( "type already defined" );
}
}
}
protected:
template<class F>
const std::string& GetTableName() {
std::string t( typeid( F ).name() );
mapFieldsToTable_iter_t iter = m_mapFieldsToTable.find( t );
if ( m_mapFieldsToTable.end() == iter ) {
throw std::runtime_error( "type not found" );
}
return iter->second;
}
template<class F, class Action> // do reset, auto bind when doing execute
QueryState<typename IDatabase::structStatementState, F, session_t>& ComposeSql( F& f ) {
using query_t = QueryState<typename IDatabase::structStatementState, F, session_t>;
query_t* pQuery = new query_t( *this, f );
Action action( GetTableName<F>() );
f.Fields( action );
if ( 0 < action.FieldCount() ) pQuery->SetHasFields();
action.ComposeStatement( pQuery->UpdateQueryText() );
pQuery->SetExecuteOneTime();
return *pQuery;
}
private:
bool m_bOpened;
IDatabase m_db;
typedef std::map<std::string, pQueryBase_t> mapTableDefs_t; // map table name to table definition
typedef typename mapTableDefs_t::iterator mapTableDefs_iter_t;
typedef std::pair<std::string, pQueryBase_t> mapTableDefs_pair_t;
mapTableDefs_t m_mapTableDefs;
typedef std::map<std::string, std::string> mapFieldsToTable_t;
typedef mapFieldsToTable_t::iterator mapFieldsToTable_iter_t;
typedef std::pair<std::string, std::string> mapFieldsToTable_pair_t;
mapFieldsToTable_t m_mapFieldsToTable;
};
// Constructor
template<class IDatabase>
SessionImpl<IDatabase>::SessionImpl(): m_bOpened( false ) {
}
// Destructor
template<class IDatabase>
SessionImpl<IDatabase>::~SessionImpl() {
ImplClose();
}
// Open
template<class IDatabase>
void SessionImpl<IDatabase>::ImplOpen( const std::string& sDbFileName, enumOpenFlags flags ) {
if ( m_bOpened ) {
std::string sErr( "Session already opened" );
throw std::runtime_error( sErr );
}
else {
m_db.SessionOpen( sDbFileName, flags );
m_bOpened = true;
}
}
// Close
template<class IDatabase>
void SessionImpl<IDatabase>::ImplClose() {
if ( m_bOpened ) {
m_bOpened = false;
m_db.SessionClose();
// 2013/08/26 process memory doesn't appear to be relaimed after this
// trying again with addition of reset();
}
}
// CreateTables
template<class IDatabase>
void SessionImpl<IDatabase>::CreateTables() {
// todo: need to add a transaction around this set of instructions
for ( mapTableDefs_iter_t iter = m_mapTableDefs.begin(); m_mapTableDefs.end() != iter; ++iter ) {
Execute( iter->second );
}
m_mapTableDefs.clear();
}
} // db
} // ou