2 * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * @author Bartosz Janiak (b.janiak@samsung.com)
20 * @brief DPL-ORM: Object-relational mapping for sqlite database, written on top of DPL.
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>
44 //TODO move to type utils
45 #define DPL_CHECK_TYPE_INSTANTIABILITY(type) \
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;
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
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);
75 virtual ~Expression() {}
76 virtual std::string GetString() const = 0;
77 virtual ArgumentIndex BindTo(DataCommand *command, ArgumentIndex index) = 0;
80 typedef DPL::SharedPtr<Expression> ExpressionPtr;
82 template<const char* Operator, typename LeftExpression, typename RightExpression>
83 class BinaryExpression : public Expression {
85 LeftExpression m_leftExpression;
86 RightExpression m_rightExpression;
87 bool m_outerParenthesis;
89 BinaryExpression(const LeftExpression& leftExpression, const RightExpression& rightExpression, bool outerParenthesis = true) :
90 m_leftExpression(leftExpression),
91 m_rightExpression(rightExpression),
92 m_outerParenthesis(outerParenthesis)
95 virtual std::string GetString() const
97 return (m_outerParenthesis ? "( " : " " ) +
98 m_leftExpression.GetString() + " " + Operator + " " + m_rightExpression.GetString() +
99 (m_outerParenthesis ? " )" : " " ) ;
102 virtual ArgumentIndex BindTo(DataCommand *command, ArgumentIndex index)
104 index = m_leftExpression.BindTo(command, index);
105 return m_rightExpression.BindTo(command, index);
108 template<typename TableDefinition>
109 struct ValidForTable {
110 typedef std::pair<typename LeftExpression ::template ValidForTable<TableDefinition>::Yes ,
111 typename RightExpression::template ValidForTable<TableDefinition>::Yes >
116 template<typename LeftExpression, typename RightExpression>
117 BinaryExpression<RelationTypes::And, LeftExpression, RightExpression>
118 And(const LeftExpression& leftExpression, const RightExpression& rightExpression)
120 return BinaryExpression<RelationTypes::And, LeftExpression, RightExpression>
121 (leftExpression, rightExpression);
124 template<typename ArgumentType>
125 class ExpressionWithArgument : public Expression {
127 ArgumentType argument;
130 explicit ExpressionWithArgument(const ArgumentType& _argument) : argument(_argument) {}
132 virtual ArgumentIndex BindTo(DataCommand *command, ArgumentIndex index)
134 DataCommandUtils::BindArgument(command, index, argument);
140 template<typename ColumnData, const char* Relation>
141 class Compare : public ExpressionWithArgument<typename ColumnData::ColumnType> {
143 explicit Compare(typename ColumnData::ColumnType column) :
144 ExpressionWithArgument<typename ColumnData::ColumnType>(column)
147 virtual std::string GetString() const
149 std::string statement;
150 statement += ColumnData::GetColumnName();
152 statement += Relation;
157 template<typename TableDefinition>
158 struct ValidForTable {
159 typedef typename TableDefinition::ColumnList::template Contains<ColumnData> Yes;
162 #define ORM_DEFINE_COMPARE_EXPRESSION(name, relationType) \
163 template<typename ColumnData> \
164 class name : public Compare<ColumnData, RelationTypes::relationType> { \
166 name(typename ColumnData::ColumnType column) : \
167 Compare<ColumnData, RelationTypes::relationType>(column) \
171 ORM_DEFINE_COMPARE_EXPRESSION(Equals, Equal)
172 ORM_DEFINE_COMPARE_EXPRESSION(Is, Is)
174 template<typename ColumnType>
175 ColumnType GetColumnFromCommand(ColumnIndex columnIndex, DataCommand *command);
177 template<typename ColumnList, typename Row>
180 static void FillRow(Row& row, ColumnIndex columnIndex, DataCommand *command)
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);
189 template<typename Row>
190 class FillRowUtil<DPL::TypeListGuard, Row> {
192 static void FillRow(Row&, ColumnIndex, DataCommand *)
193 { /* do nothing, we're past the last element of column list */ }
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)
204 template<typename TableDefinition>
208 explicit Query(IOrmInterface* interface) :
209 m_interface(interface),
216 if (m_command == NULL)
219 TableDefinition::FreeTableDataCommand(m_command, m_interface);
222 IOrmInterface* m_interface;
223 DataCommand *m_command;
224 std::string m_commandString;
225 ArgumentIndex m_bindArgumentIndex;
228 template<typename TableDefinition>
229 class QueryWithWhereClause : public Query<TableDefinition>
232 ExpressionPtr m_whereExpression;
236 if ( !!m_whereExpression )
238 this->m_commandString += " WHERE ";
239 this->m_commandString += m_whereExpression->GetString();
245 if ( !!m_whereExpression )
247 this->m_bindArgumentIndex = m_whereExpression->BindTo(
248 this->m_command, this->m_bindArgumentIndex);
253 explicit QueryWithWhereClause(IOrmInterface* interface) :
254 Query<TableDefinition>(interface)
258 template<typename Expression>
259 void Where(const Expression& expression)
261 DPL_CHECK_TYPE_INSTANTIABILITY(typename Expression::template ValidForTable<TableDefinition>::Yes);
262 if ( !!m_whereExpression && ( typeid(Expression) != typeid(*m_whereExpression) ) )
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";
269 str << this->m_commandString;
270 ThrowMsg(Exception::SelectReuseWithDifferentQuerySignature,
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));
279 template<typename TableDefinition>
280 class Delete : public QueryWithWhereClause<TableDefinition>
285 if ( !this->m_command)
287 this->m_commandString = "DELETE FROM ";
288 this->m_commandString += TableDefinition::GetName();
290 QueryWithWhereClause<TableDefinition>::Prepare();
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);
301 this->m_bindArgumentIndex = 1;
302 QueryWithWhereClause<TableDefinition>::Bind();
306 explicit Delete(IOrmInterface *interface = NULL) :
307 QueryWithWhereClause<TableDefinition>(interface)
315 this->m_command->Step();
316 this->m_command->Reset();
320 template<typename TableDefinition>
321 class Insert : public Query<TableDefinition>
324 typedef typename TableDefinition::Row Row;
325 typedef DPL::DB::SqlConnection::RowID RowID;
328 DPL::Optional<std::string> m_orClause;
331 class PrepareVisitor {
333 std::string m_columnNames;
334 std::string m_values;
336 template<typename ColumnType>
337 void Visit(const char* name, const ColumnType&, bool isSet)
341 if ( !m_columnNames.empty() )
343 m_columnNames += ", ";
346 m_columnNames += name;
354 if ( !this->m_command )
356 this->m_commandString = "INSERT ";
359 this->m_commandString += " OR " + *m_orClause + " ";
361 this->m_commandString += "INTO ";
362 this->m_commandString += TableDefinition::GetName();
364 PrepareVisitor visitor;
365 m_row.VisitColumns(visitor);
367 this->m_commandString += " ( " + visitor.m_columnNames + " ) ";
368 this->m_commandString += "VALUES ( " + visitor.m_values + " )";
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);
379 DataCommand *m_command;
380 ArgumentIndex m_bindArgumentIndex;
382 BindVisitor(DataCommand *command) :
384 m_bindArgumentIndex(1)
387 template<typename ColumnType>
388 void Visit(const char*, const ColumnType& value, bool isSet)
392 DataCommandUtils::BindArgument(m_command, m_bindArgumentIndex, value);
393 m_bindArgumentIndex++;
400 BindVisitor visitor(this->m_command);
401 m_row.VisitColumns(visitor);
406 IOrmInterface* interface = NULL,
407 const DPL::Optional<std::string>& orClause = DPL::Optional<std::string>::Null) :
408 Query<TableDefinition>(interface),
413 void Values(const Row& row)
415 if ( this->m_command )
417 if ( !row.IsSignatureMatching(m_row) )
419 ThrowMsg(Exception::SelectReuseWithDifferentQuerySignature,
420 "Current ORM implementation doesn't allow to reuse Insert instance "
421 "with different query signature.");
431 this->m_command->Step();
433 RowID result = TableDefinition::GetLastInsertRowID(
434 Query<TableDefinition>::m_interface);
436 this->m_command->Reset();
441 template<typename TableDefinition>
442 class Select : public QueryWithWhereClause<TableDefinition>
445 typedef typename TableDefinition::ColumnList ColumnList;
446 typedef typename TableDefinition::Row Row;
448 typedef std::list<Row> RowList;
450 DPL::Optional<std::string> m_orderBy;
451 bool m_distinctResults;
453 void Prepare(const char* selectColumnName)
455 if ( !this->m_command )
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();
464 QueryWithWhereClause<TableDefinition>::Prepare();
466 if ( !m_orderBy.IsNull() )
468 this->m_commandString += " ORDER BY " + *m_orderBy;
471 this->m_command = TableDefinition::AllocTableDataCommand(
472 this->m_commandString.c_str(),
473 Query<TableDefinition>::m_interface);
475 LogPedantic("Prepared SQL command " << this->m_commandString);
481 this->m_bindArgumentIndex = 1;
482 QueryWithWhereClause<TableDefinition>::Bind();
485 template<typename ColumnType>
486 ColumnType GetColumn(ColumnIndex columnIndex)
488 return GetColumnFromCommand<ColumnType>(columnIndex, this->m_command);
494 FillRowUtil<ColumnList, Row>::FillRow(row, 0, this->m_command);
500 explicit Select(IOrmInterface *interface = NULL) :
501 QueryWithWhereClause<TableDefinition>(interface),
502 m_distinctResults(false)
508 m_distinctResults = true;
511 void OrderBy(const std::string& orderBy)
516 template<typename ColumnData>
517 typename ColumnData::ColumnType GetSingleValue()
519 Prepare(ColumnData::GetColumnName());
521 this->m_command->Step();
523 typename ColumnData::ColumnType result =
524 GetColumn<typename ColumnData::ColumnType>(0);
526 this->m_command->Reset();
530 //TODO return range - pair of custom iterators
531 template<typename ColumnData>
532 std::list<typename ColumnData::ColumnType> GetValueList()
534 Prepare(ColumnData::GetColumnName());
537 std::list<typename ColumnData::ColumnType> resultList;
539 while (this->m_command->Step())
540 resultList.push_back(GetColumn<typename ColumnData::ColumnType>(0));
542 this->m_command->Reset();
550 this->m_command->Step();
552 Row result = GetRow();
554 this->m_command->Reset();
558 //TODO return range - pair of custom iterators
566 while (this->m_command->Step())
567 resultList.push_back(GetRow());
569 this->m_command->Reset();
574 template<typename TableDefinition>
575 class Update : public QueryWithWhereClause<TableDefinition> {
577 typedef typename TableDefinition::Row Row;
580 DPL::Optional<std::string> m_orClause;
583 class PrepareVisitor {
585 std::string m_setExpressions;
587 template<typename ColumnType>
588 void Visit(const char* name, const ColumnType&, bool isSet)
592 if ( !m_setExpressions.empty() )
594 m_setExpressions += ", ";
596 m_setExpressions += name;
597 m_setExpressions += " = ";
598 m_setExpressions += "?";
605 if ( !this->m_command )
607 this->m_commandString = "UPDATE ";
610 this->m_commandString += " OR " + *m_orClause + " ";
612 this->m_commandString += TableDefinition::GetName();
613 this->m_commandString += " SET ";
615 // got through row columns and values
616 PrepareVisitor visitor;
617 m_row.VisitColumns(visitor);
619 if(visitor.m_setExpressions.empty())
621 ThrowMsg(Exception::EmptyUpdateStatement, "No SET expressions in update statement");
624 this->m_commandString += visitor.m_setExpressions;
627 QueryWithWhereClause<TableDefinition>::Prepare();
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);
638 DataCommand *m_command;
641 ArgumentIndex m_bindArgumentIndex;
643 BindVisitor(DataCommand *command) :
645 m_bindArgumentIndex(1)
648 template<typename ColumnType>
649 void Visit(const char*, const ColumnType& value, bool isSet)
653 DataCommandUtils::BindArgument(m_command, m_bindArgumentIndex, value);
654 m_bindArgumentIndex++;
661 BindVisitor visitor(this->m_command);
662 m_row.VisitColumns(visitor);
664 this->m_bindArgumentIndex = visitor.m_bindArgumentIndex;
665 QueryWithWhereClause<TableDefinition>::Bind();
670 explicit Update(IOrmInterface *interface = NULL,
671 const DPL::Optional<std::string>& orClause = DPL::Optional<std::string>::Null) :
672 QueryWithWhereClause<TableDefinition>(interface),
677 void Values(const Row& row)
679 if ( this->m_command )
681 if ( !row.IsSignatureMatching(m_row) )
683 ThrowMsg(Exception::SelectReuseWithDifferentQuerySignature,
684 "Current ORM implementation doesn't allow to reuse Update instance "
685 "with different query signature.");
695 this->m_command->Step();
696 this->m_command->Reset();