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.
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.
The generated model classes can then be feed into the DBHandler or the PersistenceMgr to perform the database actions.
Right now this is what a tbl file may look like.
The generated C++ code looks like this:
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:
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
Kommentar veröffentlichen