f4df084c6cc6f17e569ec4e41322253250246466
[framework/web/wrt-commons.git] / modules / db / include / dpl / db / orm.h
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file        orm.h
18  * @author      Bartosz Janiak (b.janiak@samsung.com)
19  * @version     1.0
20  * @brief       DPL-ORM: Object-relational mapping for sqlite database, written on top of DPL.
21  */
22
23 #include <cstdlib>
24 #include <cstdio>
25 #include <string>
26 #include <typeinfo>
27 #include <utility>
28
29 #include <dpl/db/sql_connection.h>
30 #include <dpl/db/orm_interface.h>
31 #include <dpl/string.h>
32 #include <dpl/optional.h>
33 #include <dpl/shared_ptr.h>
34 #include <dpl/type_list.h>
35 #include <dpl/assert.h>
36
37 #ifndef DPL_ORM_H
38 #define DPL_ORM_H
39
40 namespace DPL {
41 namespace DB {
42 namespace ORM {
43
44 //TODO move to type utils
45 #define DPL_CHECK_TYPE_INSTANTIABILITY(type) \
46     { \
47         type _ignored_; \
48         (void)_ignored_; \
49     }
50
51 typedef size_t ColumnIndex;
52 typedef size_t ArgumentIndex;
53 typedef DPL::Optional<DPL::String> OptionalString;
54 typedef DPL::Optional<int> OptionalInteger;
55 typedef DPL::DB::SqlConnection::DataCommand DataCommand;
56
57 namespace RelationTypes {
58     extern const char Equal[];
59     extern const char LessThan[];
60     extern const char And[];
61     extern const char Or[];
62     extern const char Is[];
63     //TODO define more relation types
64 }
65
66 namespace DataCommandUtils {
67     //TODO move to DPL::DataCommand?
68     void BindArgument(DataCommand *command, ArgumentIndex index, int argument);
69     void BindArgument(DataCommand *command, ArgumentIndex index, const OptionalInteger& argument);
70     void BindArgument(DataCommand *command, ArgumentIndex index, const DPL::String& argument);
71     void BindArgument(DataCommand *command, ArgumentIndex index, const OptionalString& argument);
72 }
73 class Expression {
74 public:
75     virtual ~Expression() {}
76     virtual std::string GetString() const = 0;
77     virtual ArgumentIndex BindTo(DataCommand *command, ArgumentIndex index) = 0;
78 };
79
80 typedef DPL::SharedPtr<Expression> ExpressionPtr;
81
82 template<const char* Operator, typename LeftExpression, typename RightExpression>
83 class BinaryExpression : public Expression {
84 protected:
85     LeftExpression  m_leftExpression;
86     RightExpression m_rightExpression;
87     bool            m_outerParenthesis;
88 public:
89     BinaryExpression(const LeftExpression& leftExpression, const RightExpression& rightExpression, bool outerParenthesis = true) :
90         m_leftExpression(leftExpression),
91         m_rightExpression(rightExpression),
92         m_outerParenthesis(outerParenthesis)
93     {}
94
95     virtual std::string GetString() const
96     {
97         return  (m_outerParenthesis ? "( " : " " ) +
98                  m_leftExpression.GetString() + " " + Operator + " " + m_rightExpression.GetString() +
99                 (m_outerParenthesis ? " )" : " " ) ;
100     }
101
102     virtual ArgumentIndex BindTo(DataCommand *command, ArgumentIndex index)
103     {
104         index = m_leftExpression.BindTo(command, index);
105         return  m_rightExpression.BindTo(command, index);
106     }
107
108     template<typename TableDefinition>
109     struct ValidForTable {
110         typedef std::pair<typename LeftExpression ::template ValidForTable<TableDefinition>::Yes ,
111                           typename RightExpression::template ValidForTable<TableDefinition>::Yes >
112                 Yes;
113     };
114 };
115
116 template<typename LeftExpression, typename RightExpression>
117 BinaryExpression<RelationTypes::And, LeftExpression, RightExpression>
118     And(const LeftExpression& leftExpression, const RightExpression& rightExpression)
119 {
120     return BinaryExpression<RelationTypes::And, LeftExpression, RightExpression>
121             (leftExpression, rightExpression);
122 }
123
124 template<typename ArgumentType>
125 class ExpressionWithArgument : public Expression {
126 protected:
127     ArgumentType argument;
128
129 public:
130     explicit ExpressionWithArgument(const ArgumentType& _argument) : argument(_argument) {}
131
132     virtual ArgumentIndex BindTo(DataCommand *command, ArgumentIndex index)
133     {
134         DataCommandUtils::BindArgument(command, index, argument);
135         return index + 1;
136     }
137 };
138
139
140 template<typename ColumnData, const char* Relation>
141 class Compare : public ExpressionWithArgument<typename ColumnData::ColumnType> {
142 public:
143     explicit Compare(typename ColumnData::ColumnType column) :
144         ExpressionWithArgument<typename ColumnData::ColumnType>(column)
145     {}
146
147     virtual std::string GetString() const
148     {
149         std::string statement;
150         statement += ColumnData::GetColumnName();
151         statement += " ";
152         statement += Relation;
153         statement += " ?";
154         return statement;
155     }
156
157     template<typename TableDefinition>
158     struct ValidForTable {
159         typedef typename TableDefinition::ColumnList::template Contains<ColumnData> Yes;
160     };
161 };
162 #define ORM_DEFINE_COMPARE_EXPRESSION(name, relationType)                      \
163     template<typename ColumnData>                                              \
164     class name : public Compare<ColumnData, RelationTypes::relationType> {     \
165     public:                                                                    \
166         name(typename ColumnData::ColumnType column) :                         \
167             Compare<ColumnData, RelationTypes::relationType>(column)           \
168         {}                                                                     \
169     };
170
171 ORM_DEFINE_COMPARE_EXPRESSION(Equals, Equal)
172 ORM_DEFINE_COMPARE_EXPRESSION(Is, Is)
173
174 template<typename ColumnType>
175 ColumnType GetColumnFromCommand(ColumnIndex columnIndex, DataCommand *command);
176
177 template<typename ColumnList, typename Row>
178 class FillRowUtil {
179 public:
180     static void FillRow(Row& row, ColumnIndex columnIndex, DataCommand *command)
181     {
182         typename ColumnList::Head::ColumnType rowField;
183         rowField = GetColumnFromCommand<typename ColumnList::Head::ColumnType>(columnIndex, command);
184         ColumnList::Head::SetRowField(row, rowField);
185         FillRowUtil<typename ColumnList::Tail, Row>::FillRow(row, columnIndex + 1, command);
186     }
187 };
188
189 template<typename Row>
190 class FillRowUtil<DPL::TypeListGuard, Row> {
191 public:
192     static void FillRow(Row&, ColumnIndex, DataCommand *)
193     { /* do nothing, we're past the last element of column list */ }
194 };
195
196 class Exception {
197 public:
198     DECLARE_EXCEPTION_TYPE(DPL::Exception, Base)
199     DECLARE_EXCEPTION_TYPE(Base, SelectReuseWithDifferentQuerySignature)
200     DECLARE_EXCEPTION_TYPE(Base, RowFieldNotInitialized)
201     DECLARE_EXCEPTION_TYPE(Base, EmptyUpdateStatement)
202 };
203
204 template<typename TableDefinition>
205 class Query
206 {
207 protected:
208     explicit Query(IOrmInterface* interface) :
209         m_interface(interface),
210         m_command(NULL)
211     {
212     }
213
214     virtual ~Query()
215     {
216         if (m_command == NULL)
217             return;
218
219         TableDefinition::FreeTableDataCommand(m_command, m_interface);
220     }
221
222     IOrmInterface* m_interface;
223     DataCommand *m_command;
224     std::string m_commandString;
225     ArgumentIndex m_bindArgumentIndex;
226 };
227
228 template<typename TableDefinition>
229 class QueryWithWhereClause : public Query<TableDefinition>
230 {
231 protected:
232     ExpressionPtr m_whereExpression;
233
234     void Prepare()
235     {
236         if ( !!m_whereExpression )
237         {
238             this->m_commandString += " WHERE ";
239             this->m_commandString += m_whereExpression->GetString();
240         }
241     }
242
243     void Bind()
244     {
245         if ( !!m_whereExpression )
246         {
247             this->m_bindArgumentIndex = m_whereExpression->BindTo(
248                 this->m_command, this->m_bindArgumentIndex);
249         }
250     }
251
252 public:
253     explicit QueryWithWhereClause(IOrmInterface* interface) :
254         Query<TableDefinition>(interface)
255     {
256     }
257
258     template<typename Expression>
259     void Where(const Expression& expression)
260     {
261         DPL_CHECK_TYPE_INSTANTIABILITY(typename Expression::template ValidForTable<TableDefinition>::Yes);
262         if ( !!m_whereExpression && ( typeid(Expression) != typeid(*m_whereExpression) ) )
263         {
264             std::ostringstream str;
265             str << "Current ORM implementation doesn't allow to reuse Select"
266                     " instance with different query signature (particularly "
267                     "WHERE on different column).\n";
268             str << "Query: ";
269             str << this->m_commandString;
270             ThrowMsg(Exception::SelectReuseWithDifferentQuerySignature,
271                 str.str());
272         }
273         //TODO maybe don't make a copy here but just generate the string part of the query.
274         m_whereExpression.Reset(new Expression(expression));
275     }
276
277 };
278
279 template<typename TableDefinition>
280 class Delete : public QueryWithWhereClause<TableDefinition>
281 {
282 protected:
283     void Prepare()
284     {
285         if ( !this->m_command)
286         {
287             this->m_commandString  = "DELETE FROM ";
288             this->m_commandString += TableDefinition::GetName();
289
290             QueryWithWhereClause<TableDefinition>::Prepare();
291
292             this->m_command = TableDefinition::AllocTableDataCommand(
293                     this->m_commandString.c_str(),
294                     Query<TableDefinition>::m_interface);
295             LogPedantic("Prepared SQL command " << this->m_commandString);
296         }
297     }
298
299     void Bind()
300     {
301         this->m_bindArgumentIndex = 1;
302         QueryWithWhereClause<TableDefinition>::Bind();
303     }
304
305 public:
306     explicit Delete(IOrmInterface *interface = NULL) :
307         QueryWithWhereClause<TableDefinition>(interface)
308     {
309     }
310
311     void Execute()
312     {
313         Prepare();
314         Bind();
315         this->m_command->Step();
316         this->m_command->Reset();
317     }
318 };
319
320 template<typename TableDefinition>
321 class Insert : public Query<TableDefinition>
322 {
323 public:
324     typedef typename TableDefinition::Row Row;
325     typedef DPL::DB::SqlConnection::RowID RowID;
326
327 protected:
328     DPL::Optional<std::string> m_orClause;
329     Row m_row;
330
331     class PrepareVisitor {
332     public:
333         std::string m_columnNames;
334         std::string m_values;
335
336         template<typename ColumnType>
337         void Visit(const char* name, const ColumnType&, bool isSet)
338         {
339             if ( isSet )
340             {
341                 if ( !m_columnNames.empty() )
342                 {
343                     m_columnNames += ", ";
344                     m_values += ", ";
345                 }
346                 m_columnNames += name;
347                 m_values += "?";
348             }
349         }
350     };
351
352     void Prepare()
353     {
354         if ( !this->m_command )
355         {
356             this->m_commandString = "INSERT ";
357             if ( !!m_orClause )
358             {
359                 this->m_commandString += " OR " + *m_orClause + " ";
360             }
361             this->m_commandString += "INTO ";
362             this->m_commandString += TableDefinition::GetName();
363
364             PrepareVisitor visitor;
365             m_row.VisitColumns(visitor);
366
367             this->m_commandString += " ( " + visitor.m_columnNames + " ) ";
368             this->m_commandString += "VALUES ( " + visitor.m_values + " )";
369
370             LogPedantic("Prepared SQL command " << this->m_commandString);
371             this->m_command = TableDefinition::AllocTableDataCommand(
372                 this->m_commandString.c_str(),
373                 Query<TableDefinition>::m_interface);
374         }
375     }
376
377     class BindVisitor {
378     private:
379         DataCommand *m_command;
380         ArgumentIndex m_bindArgumentIndex;
381     public:
382         BindVisitor(DataCommand *command) :
383             m_command(command),
384             m_bindArgumentIndex(1)
385         {}
386
387         template<typename ColumnType>
388         void Visit(const char*, const ColumnType& value, bool isSet)
389         {
390             if ( isSet )
391             {
392                 DataCommandUtils::BindArgument(m_command, m_bindArgumentIndex, value);
393                 m_bindArgumentIndex++;
394             }
395         }
396     };
397
398     void Bind()
399     {
400         BindVisitor visitor(this->m_command);
401         m_row.VisitColumns(visitor);
402     }
403
404 public:
405     explicit Insert(
406             IOrmInterface* interface = NULL,
407             const DPL::Optional<std::string>& orClause = DPL::Optional<std::string>::Null) :
408         Query<TableDefinition>(interface),
409         m_orClause(orClause)
410     {
411     }
412
413     void Values(const Row& row)
414     {
415         if ( this->m_command )
416         {
417             if ( !row.IsSignatureMatching(m_row) )
418             {
419                 ThrowMsg(Exception::SelectReuseWithDifferentQuerySignature,
420                     "Current ORM implementation doesn't allow to reuse Insert instance "
421                     "with different query signature.");
422             }
423         }
424         m_row = row;
425     }
426
427     RowID Execute()
428     {
429         Prepare();
430         Bind();
431         this->m_command->Step();
432
433         RowID result = TableDefinition::GetLastInsertRowID(
434             Query<TableDefinition>::m_interface);
435
436         this->m_command->Reset();
437         return result;
438     }
439 };
440
441 template<typename TableDefinition>
442 class Select : public QueryWithWhereClause<TableDefinition>
443 {
444 public:
445     typedef typename TableDefinition::ColumnList       ColumnList;
446     typedef typename TableDefinition::Row              Row;
447
448     typedef std::list<Row>                             RowList;
449 protected:
450     DPL::Optional<std::string> m_orderBy;
451     bool                       m_distinctResults;
452
453     void Prepare(const char* selectColumnName)
454     {
455         if ( !this->m_command )
456         {
457             this->m_commandString  = "SELECT ";
458             if (m_distinctResults)
459                 this->m_commandString += "DISTINCT ";
460             this->m_commandString += selectColumnName;
461             this->m_commandString += " FROM ";
462             this->m_commandString += TableDefinition::GetName();
463
464             QueryWithWhereClause<TableDefinition>::Prepare();
465
466             if ( !m_orderBy.IsNull() )
467             {
468                 this->m_commandString += " ORDER BY " + *m_orderBy;
469             }
470
471             this->m_command = TableDefinition::AllocTableDataCommand(
472                 this->m_commandString.c_str(),
473                 Query<TableDefinition>::m_interface);
474
475             LogPedantic("Prepared SQL command " << this->m_commandString);
476         }
477     }
478
479     void Bind()
480     {
481         this->m_bindArgumentIndex = 1;
482         QueryWithWhereClause<TableDefinition>::Bind();
483     }
484
485     template<typename ColumnType>
486     ColumnType GetColumn(ColumnIndex columnIndex)
487     {
488         return GetColumnFromCommand<ColumnType>(columnIndex, this->m_command);
489     }
490
491     Row GetRow()
492     {
493         Row row;
494         FillRowUtil<ColumnList, Row>::FillRow(row, 0, this->m_command);
495         return row;
496     }
497
498 public:
499
500     explicit Select(IOrmInterface *interface = NULL) :
501         QueryWithWhereClause<TableDefinition>(interface),
502         m_distinctResults(false)
503     {
504     }
505
506     void Distinct()
507     {
508         m_distinctResults = true;
509     }
510
511     void OrderBy(const std::string& orderBy)
512     {
513         m_orderBy = orderBy;
514     }
515
516     template<typename ColumnData>
517     typename ColumnData::ColumnType GetSingleValue()
518     {
519         Prepare(ColumnData::GetColumnName());
520         Bind();
521         this->m_command->Step();
522
523         typename ColumnData::ColumnType result =
524             GetColumn<typename ColumnData::ColumnType>(0);
525
526         this->m_command->Reset();
527         return result;
528     }
529
530     //TODO return range - pair of custom iterators
531     template<typename ColumnData>
532     std::list<typename ColumnData::ColumnType> GetValueList()
533     {
534         Prepare(ColumnData::GetColumnName());
535         Bind();
536
537         std::list<typename ColumnData::ColumnType> resultList;
538
539         while (this->m_command->Step())
540             resultList.push_back(GetColumn<typename ColumnData::ColumnType>(0));
541
542         this->m_command->Reset();
543         return resultList;
544     }
545
546     Row GetSingleRow()
547     {
548         Prepare("*");
549         Bind();
550         this->m_command->Step();
551
552         Row result = GetRow();
553
554         this->m_command->Reset();
555         return result;
556     }
557
558     //TODO return range - pair of custom iterators
559     RowList GetRowList()
560     {
561         Prepare("*");
562         Bind();
563
564         RowList resultList;
565
566         while (this->m_command->Step())
567             resultList.push_back(GetRow());
568
569         this->m_command->Reset();
570         return resultList;
571     }
572 };
573
574 template<typename TableDefinition>
575 class Update : public QueryWithWhereClause<TableDefinition> {
576 public:
577     typedef typename TableDefinition::Row Row;
578
579 protected:
580     DPL::Optional<std::string> m_orClause;
581     Row m_row;
582
583     class PrepareVisitor {
584     public:
585         std::string m_setExpressions;
586
587         template<typename ColumnType>
588         void Visit(const char* name, const ColumnType&, bool isSet)
589         {
590             if ( isSet )
591             {
592                 if ( !m_setExpressions.empty() )
593                 {
594                     m_setExpressions += ", ";
595                 }
596                 m_setExpressions += name;
597                 m_setExpressions += " = ";
598                 m_setExpressions += "?";
599             }
600         }
601     };
602
603     void Prepare()
604     {
605         if ( !this->m_command )
606         {
607             this->m_commandString = "UPDATE ";
608             if ( !!m_orClause )
609             {
610                 this->m_commandString += " OR " + *m_orClause + " ";
611             }
612             this->m_commandString += TableDefinition::GetName();
613             this->m_commandString += " SET ";
614
615             // got through row columns and values
616             PrepareVisitor visitor;
617             m_row.VisitColumns(visitor);
618
619             if(visitor.m_setExpressions.empty())
620             {
621                 ThrowMsg(Exception::EmptyUpdateStatement, "No SET expressions in update statement");
622             }
623
624             this->m_commandString += visitor.m_setExpressions;
625
626             // where
627             QueryWithWhereClause<TableDefinition>::Prepare();
628
629             this->m_command = TableDefinition::AllocTableDataCommand(
630                     this->m_commandString.c_str(),
631                     Query<TableDefinition>::m_interface);
632             LogPedantic("Prepared SQL command " << this->m_commandString);
633         }
634     }
635
636     class BindVisitor {
637     private:
638         DataCommand *m_command;
639
640     public:
641         ArgumentIndex m_bindArgumentIndex;
642
643         BindVisitor(DataCommand *command) :
644             m_command(command),
645             m_bindArgumentIndex(1)
646         {}
647
648         template<typename ColumnType>
649         void Visit(const char*, const ColumnType& value, bool isSet)
650         {
651             if ( isSet )
652             {
653                 DataCommandUtils::BindArgument(m_command, m_bindArgumentIndex, value);
654                 m_bindArgumentIndex++;
655             }
656         }
657     };
658
659     void Bind()
660     {
661         BindVisitor visitor(this->m_command);
662         m_row.VisitColumns(visitor);
663
664         this->m_bindArgumentIndex = visitor.m_bindArgumentIndex;
665         QueryWithWhereClause<TableDefinition>::Bind();
666     }
667
668
669 public:
670     explicit Update(IOrmInterface *interface = NULL,
671                     const DPL::Optional<std::string>& orClause = DPL::Optional<std::string>::Null) :
672         QueryWithWhereClause<TableDefinition>(interface),
673         m_orClause(orClause)
674     {
675     }
676
677     void Values(const Row& row)
678     {
679         if ( this->m_command )
680         {
681             if ( !row.IsSignatureMatching(m_row) )
682             {
683                 ThrowMsg(Exception::SelectReuseWithDifferentQuerySignature,
684                     "Current ORM implementation doesn't allow to reuse Update instance "
685                     "with different query signature.");
686             }
687         }
688         m_row = row;
689     }
690
691     void Execute()
692     {
693         Prepare();
694         Bind();
695         this->m_command->Step();
696         this->m_command->Reset();
697     }
698 };
699
700 } //namespace ORM
701 } //namespace DB
702 } //namespace DPL
703
704 #endif // DPL_ORM_H