Code generation for my database layer

I'm using postgresql as my database and have written yet another code generator that makes my life easier. You'll find the code here at github.

What does this tool do for me?

It generates classes derived from persistence::Model with static meta data and the corresponding persistence::DBCondition classes for the table fields.

Theoretically I can support more than postgres as database. But right now this is not a goal for me. The models are generated with memory consumption in mind. The base class itself is just 32 bytes.

If you take a look at the code, you should note that the DBHandler is just the "low" level part of the persistence system. There is another component which I called the PersistenceMgr. This class is able to collect dirty models from player handler components and performs a mass update every game tick for dirty stuff with prepared statements.

But back to what the generator does. Each module can provide tbl files to a CMake macro to generate C++ code from those tbl-files.

1:  generate_db_models(yourmodule tables.tbl YourModuleModels.h)  

The generated model classes can then be feed into the DBHandler or the PersistenceMgr to perform the database actions.

How does this look like?


1:  table event {  
2:       namespace eventmgr  
3:       field id {  
4:            type long  
5:            operator set  
6:       }  
7:       field nameid {  
8:            type string  
9:            notnull  
10:           operator set  
11:       }  
12:       field startdate {  
13:            type timestamp  
14:            notnull  
15:            operator set  
16:       }  
17:       field enddate {  
18:            type timestamp  
19:            notnull  
20:            operator set  
21:       }  
22:       field finished {  
23:            type boolean  
24:            notnull  
25:            default false  
26:            operator set  
27:       }  
28:       constraints {  
29:            id primarykey  
30:            id autoincrement  
31:       }  
32:  }  
33:  table event_point {  
34:       namespace eventmgr  
35:       field eventid {  
36:            type long  
37:            notnull  
38:            operator set  
39:       }  
40:       field userid {  
41:            type long  
42:            operator set  
43:            notnull  
44:       }  
45:       field key {  
46:            type string  
47:            notnull  
48:            operator set  
49:       }  
50:       field points {  
51:            type long  
52:            notnull  
53:            operator add  
54:       }  
55:       constraints {  
56:            (userid, eventid, key) unique  
57:       }  
58:  }  

Right now this is what a tbl file may look like.

The generated C++ code looks like this:

1:  /**  
2:   * @file  
3:   */  
4:  #pragma once  
5:  #include "persistence/Model.h"  
6:  #include "persistence/DBCondition.h"  
7:  #include "core/String.h"  
8:  #include "core/Common.h"  
9:  #include <memory>  
10:  #include <vector>  
11:  #include <array>  
12:  #include <string>  
13:  namespace eventmgr {  
14:  namespace db {  
15:  /**  
16:   * @brief Model class for table 'public.event'  
17:   * @note Work with this class in combination with the persistence::DBHandler  
18:   * @ingroup Persistence  
19:   */  
20:  class EventModel : public persistence::Model {  
21:  private:  
22:       using Super = persistence::Model;  
23:       friend class persistence::DBHandler;  
24:  protected:  
25:       struct Members {  
26:            /**  
27:             * @brief Member for table column 'enddate'  
28:             */  
29:            persistence::Timestamp _enddate;  
30:            /**  
31:             * @brief Member for table column 'finished'  
32:             */  
33:            bool _finished = false;  
34:            /**  
35:             * @brief Member for table column 'id'  
36:             */  
37:            int64_t _id = 0l;  
38:            /**  
39:             * @brief Member for table column 'nameid'  
40:             */  
41:            std::string _nameid;  
42:            /**  
43:             * @brief Member for table column 'startdate'  
44:             */  
45:            persistence::Timestamp _startdate;  
46:            /**  
47:             * @brief Is there a valid value set?  
48:             * @c true if a value is set and the field should be taken into account for e.g. update statements, @c false if not  
49:             */  
50:            bool _isValid_enddate = false;  
51:            /**  
52:             * @brief Is there a valid value set?  
53:             * @c true if a value is set and the field should be taken into account for e.g. update statements, @c false if not  
54:             */  
55:            bool _isValid_finished = false;  
56:            /**  
57:             * @brief Is there a valid value set?  
58:             * @c true if a value is set and the field should be taken into account for e.g. update statements, @c false if not  
59:             */  
60:            bool _isValid_id = false;  
61:            /**  
62:             * @brief Is there a valid value set?  
63:             * @c true if a value is set and the field should be taken into account for e.g. update statements, @c false if not  
64:             */  
65:            bool _isValid_nameid = false;  
66:            /**  
67:             * @brief Is there a valid value set?  
68:             * @c true if a value is set and the field should be taken into account for e.g. update statements, @c false if not  
69:             */  
70:            bool _isValid_startdate = false;  
71:       };  
72:       Members _m;  
73:       struct MetaPriv : public Meta {  
74:            MetaPriv() {  
75:                 _schema = "public";  
76:                 _tableName = "event";  
77:                 _primaryKeyFields = 1;  
78:                 _autoIncrementStart = 1;  
79:                 _fields.reserve(5);  
80:                 _fields.emplace_back(persistence::Field{"enddate", persistence::FieldType::TIMESTAMP, persistence::Operator::SET, 8, "", 0, offsetof(Members, _enddate), -1, offsetof(Members, _isValid_enddate)});  
81:                 _fields.emplace_back(persistence::Field{"finished", persistence::FieldType::BOOLEAN, persistence::Operator::SET, 8, "false", 0, offsetof(Members, _finished), -1, offsetof(Members, _isValid_finished)});  
82:                 _fields.emplace_back(persistence::Field{"id", persistence::FieldType::LONG, persistence::Operator::SET, 6, "", 0, offsetof(Members, _id), -1, offsetof(Members, _isValid_id)});  
83:                 _fields.emplace_back(persistence::Field{"nameid", persistence::FieldType::STRING, persistence::Operator::SET, 8, "", 0, offsetof(Members, _nameid), -1, offsetof(Members, _isValid_nameid)});  
84:                 _fields.emplace_back(persistence::Field{"startdate", persistence::FieldType::TIMESTAMP, persistence::Operator::SET, 8, "", 0, offsetof(Members, _startdate), -1, offsetof(Members, _isValid_startdate)});  
85:                 _constraints.reserve(5);  
86:                 _constraints.insert(std::make_pair("id", persistence::Constraint{{"id"}, 6}));  
87:                 _constraints.insert(std::make_pair("finished", persistence::Constraint{{"finished"}, 8}));  
88:                 _constraints.insert(std::make_pair("enddate", persistence::Constraint{{"enddate"}, 8}));  
89:                 _constraints.insert(std::make_pair("nameid", persistence::Constraint{{"nameid"}, 8}));  
90:                 _constraints.insert(std::make_pair("startdate", persistence::Constraint{{"startdate"}, 8}));  
91:                 _primaryKeys.reserve(1);  
92:                 _primaryKeys.emplace_back("id");  
93:                 _autoIncrementField = "id";  
94:            }  
95:       };  
96:       static inline const Meta* meta() {  
97:            static MetaPriv _meta;  
98:            return &_meta;  
99:       }  
100:  public:  
101:       EventModel() : Super(meta()) {  
102:            _membersPointer = (uint8_t*)&_m;  
103:       }  
104:       EventModel(EventModel&& source) : Super(meta()) {  
105:            _m = std::move(source._m);  
106:            _membersPointer = (uint8_t*)&_m;  
107:       }  
108:       EventModel(const EventModel& source) : Super(meta()) {  
109:            _m = source._m;  
110:            _membersPointer = (uint8_t*)&_m;  
111:       }  
112:       EventModel& operator=(EventModel&& source) {  
113:            _m = std::move(source._m);  
114:            _membersPointer = (uint8_t*)&_m;  
115:            return *this;  
116:       }  
117:       EventModel& operator=(const EventModel& source) {  
118:            _m = source._m;  
119:            _membersPointer = (uint8_t*)&_m;  
120:            return *this;  
121:       }  
122:       /**  
123:        * @brief Access the value for 'public.event.enddate' after the model was loaded  
124:        * @note The value is in seconds  
125:        * @note May not be null  
126:        * @note Will set the value in the conflict case (Operator::SET)  
127:        */  
128:       inline const persistence::Timestamp& enddate() const {  
129:            return _m._enddate;  
130:       }  
131:       /**  
132:        * @brief Set the value for 'public.event.enddate' for updates, inserts and where clauses  
133:        * @note The value is in seconds  
134:        * @note May not be null  
135:        * @note Will set the value in the conflict case (Operator::SET)  
136:        */  
137:       inline void setEnddate(const persistence::Timestamp& enddate) {  
138:            _m._enddate = enddate;  
139:            _m._isValid_enddate = true;  
140:       }  
141:       /**  
142:        * @brief Access the value for 'public.event.finished' after the model was loaded  
143:        * @note May not be null  
144:        * @note Will set the value in the conflict case (Operator::SET)  
145:        */  
146:       inline bool finished() const {  
147:            return _m._finished;  
148:       }  
149:       /**  
150:        * @brief Set the value for 'public.event.finished' for updates, inserts and where clauses  
151:        * @note May not be null  
152:        * @note Will set the value in the conflict case (Operator::SET)  
153:        */  
154:       inline void setFinished(bool finished) {  
155:            _m._finished = finished;  
156:            _m._isValid_finished = true;  
157:       }  
158:       /**  
159:        * @brief Access the value for 'public.event.id' after the model was loaded  
160:        * @note Auto increment  
161:        * @note Primary key  
162:        * @note Will set the value in the conflict case (Operator::SET)  
163:        */  
164:       inline int64_t id() const {  
165:            return _m._id;  
166:       }  
167:       /**  
168:        * @brief Set the value for 'public.event.id' for updates, inserts and where clauses  
169:        * @note Auto increment  
170:        * @note Primary key  
171:        * @note Will set the value in the conflict case (Operator::SET)  
172:        */  
173:       inline void setId(int64_t id) {  
174:            _m._id = id;  
175:            _m._isValid_id = true;  
176:       }  
177:       /**  
178:        * @brief Access the value for 'public.event.nameid' after the model was loaded  
179:        * @note May not be null  
180:        * @note Will set the value in the conflict case (Operator::SET)  
181:        */  
182:       inline const std::string& nameid() const {  
183:            return _m._nameid;  
184:       }  
185:       /**  
186:        * @brief Set the value for 'public.event.nameid' for updates, inserts and where clauses  
187:        * @note May not be null  
188:        * @note Will set the value in the conflict case (Operator::SET)  
189:        */  
190:       inline void setNameid(const std::string& nameid) {  
191:            _m._nameid = nameid;  
192:            _m._isValid_nameid = true;  
193:       }  
194:       /**  
195:        * @brief Access the value for 'public.event.startdate' after the model was loaded  
196:        * @note The value is in seconds  
197:        * @note May not be null  
198:        * @note Will set the value in the conflict case (Operator::SET)  
199:        */  
200:       inline const persistence::Timestamp& startdate() const {  
201:            return _m._startdate;  
202:       }  
203:       /**  
204:        * @brief Set the value for 'public.event.startdate' for updates, inserts and where clauses  
205:        * @note The value is in seconds  
206:        * @note May not be null  
207:        * @note Will set the value in the conflict case (Operator::SET)  
208:        */  
209:       inline void setStartdate(const persistence::Timestamp& startdate) {  
210:            _m._startdate = startdate;  
211:            _m._isValid_startdate = true;  
212:       }  
213:       /**  
214:        * @brief The column name for 'enddate'  
215:        */  
216:       static constexpr const char* f_enddate() {  
217:            return "enddate";  
218:       }  
219:       /**  
220:        * @brief The column name for 'finished'  
221:        */  
222:       static constexpr const char* f_finished() {  
223:            return "finished";  
224:       }  
225:       /**  
226:        * @brief The column name for 'id'  
227:        */  
228:       static constexpr const char* f_id() {  
229:            return "id";  
230:       }  
231:       /**  
232:        * @brief The column name for 'nameid'  
233:        */  
234:       static constexpr const char* f_nameid() {  
235:            return "nameid";  
236:       }  
237:       /**  
238:        * @brief The column name for 'startdate'  
239:        */  
240:       static constexpr const char* f_startdate() {  
241:            return "startdate";  
242:       }  
243:  }; // class EventModel  
244:  /**  
245:   * @brief Condition for 'public.event.enddate'.  
246:   */  
247:  class DBConditionEventModelEnddate : public persistence::DBCondition {  
248:  private:  
249:       using Super = persistence::DBCondition;  
250:  public:  
251:       /**  
252:        * @brief Condition for enddate  
253:        * @param[in] value UTC timestamp in seconds  
254:        * @param[in] comp @c persistence::Comparator  
255:        */  
256:       DBConditionEventModelEnddate(const persistence::Timestamp& value, persistence::Comparator comp = persistence::Comparator::Equal) :  
257:            Super(EventModel::f_enddate(), persistence::FieldType::TIMESTAMP, std::to_string(value.seconds()), comp) {  
258:       }  
259:  }; // class DBConditionEventModelEnddate  
260:  /**  
261:   * @brief Condition for 'public.event.finished'.  
262:   */  
263:  class DBConditionEventModelFinished : public persistence::DBCondition {  
264:  private:  
265:       using Super = persistence::DBCondition;  
266:  public:  
267:       /**  
268:        * @brief Condition for finished  
269:        * @param[in] value  
270:        * @param[in] comp @c persistence::Comparator  
271:        */  
272:       DBConditionEventModelFinished(bool value, persistence::Comparator comp = persistence::Comparator::Equal) :  
273:            Super(EventModel::f_finished(), persistence::FieldType::BOOLEAN, std::to_string(value), comp) {  
274:       }  
275:  }; // class DBConditionEventModelFinished  
276:  /**  
277:   * @brief Condition for 'public.event.id'.  
278:   */  
279:  class DBConditionEventModelId : public persistence::DBCondition {  
280:  private:  
281:       using Super = persistence::DBCondition;  
282:  public:  
283:       /**  
284:        * @brief Condition for id  
285:        * @param[in] value  
286:        * @param[in] comp @c persistence::Comparator  
287:        */  
288:       DBConditionEventModelId(int64_t value, persistence::Comparator comp = persistence::Comparator::Equal) :  
289:            Super(EventModel::f_id(), persistence::FieldType::LONG, std::to_string(value), comp) {  
290:       }  
291:  }; // class DBConditionEventModelId  
292:  /**  
293:   * @brief Condition for 'public.event.nameid'.  
294:   */  
295:  class DBConditionEventModelNameid : public persistence::DBCondition {  
296:  private:  
297:       using Super = persistence::DBCondition;  
298:  public:  
299:       /**  
300:        * @brief Condition for nameid  
301:        * @param[in] value  
302:        * @param[in] comp @c persistence::Comparator  
303:        */  
304:       constexpr DBConditionEventModelNameid(const char * value, persistence::Comparator comp = persistence::Comparator::Equal) :  
305:            Super(EventModel::f_nameid(), persistence::FieldType::STRING, value, comp) {  
306:       }  
307:       DBConditionEventModelNameid(const std::string& value, persistence::Comparator comp = persistence::Comparator::Equal) :  
308:            Super(EventModel::f_nameid(), persistence::FieldType::STRING, value, comp) {  
309:       }  
310:  }; // class DBConditionEventModelNameid  
311:  /**  
312:   * @brief Condition for 'public.event.startdate'.  
313:   */  
314:  class DBConditionEventModelStartdate : public persistence::DBCondition {  
315:  private:  
316:       using Super = persistence::DBCondition;  
317:  public:  
318:       /**  
319:        * @brief Condition for startdate  
320:        * @param[in] value UTC timestamp in seconds  
321:        * @param[in] comp @c persistence::Comparator  
322:        */  
323:       DBConditionEventModelStartdate(const persistence::Timestamp& value, persistence::Comparator comp = persistence::Comparator::Equal) :  
324:            Super(EventModel::f_startdate(), persistence::FieldType::TIMESTAMP, std::to_string(value.seconds()), comp) {  
325:       }  
326:  }; // class DBConditionEventModelStartdate  
327:  typedef std::shared_ptr<EventModel> EventModelPtr;  
328:  } // namespace db  
329:  } // namespace eventmgr  

Note that the MetaPriv struct is static and does not waste memory per model instance.

You can now use the DBHandler to create and automatically update the database tables whenever you change the tbl file like this:

1:  _dbHandler->createTable(db::EventModel());  

Kommentare

Beliebte Posts