|
// This may look like C code, but it's really -*- C++ -*-
|
|
/*
|
|
* Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
|
|
*
|
|
* See the LICENSE file for terms of use.
|
|
*/
|
|
#ifndef WT_DBO_QUERY_IMPL_H_
|
|
#define WT_DBO_QUERY_IMPL_H_
|
|
|
|
#include <stdexcept>
|
|
#include <boost/tuple/tuple.hpp>
|
|
|
|
#include <Wt/Dbo/Exception>
|
|
#include <Wt/Dbo/Field>
|
|
#include <Wt/Dbo/SqlStatement>
|
|
|
|
#include <Wt/Dbo/Field_impl.h>
|
|
|
|
#ifndef DOXYGEN_ONLY
|
|
|
|
namespace Wt {
|
|
namespace Dbo {
|
|
namespace Impl {
|
|
|
|
extern std::string WTDBO_API
|
|
completeQuerySelectSql(const std::string& sql,
|
|
const std::string& where,
|
|
const std::string& groupBy,
|
|
const std::string& orderBy,
|
|
int limit, int offset,
|
|
const std::vector<FieldInfo>& fields);
|
|
|
|
extern std::string WTDBO_API
|
|
createQuerySelectSql(const std::string& from,
|
|
const std::string& where,
|
|
const std::string& groupBy,
|
|
const std::string& orderBy,
|
|
int limit, int offset,
|
|
const std::vector<FieldInfo>& fields);
|
|
|
|
extern std::string WTDBO_API
|
|
createWrappedQueryCountSql(const std::string& query);
|
|
|
|
extern std::string WTDBO_API
|
|
createQueryCountSql(const std::string& query,
|
|
const std::string& from,
|
|
const std::string& where,
|
|
const std::string& groupBy,
|
|
const std::string& orderBy,
|
|
int limit, int offset);
|
|
|
|
extern void WTDBO_API
|
|
substituteFields(const SelectFieldList& list,
|
|
const std::vector<FieldInfo>& fs,
|
|
std::string& sql,
|
|
int offset);
|
|
|
|
extern void WTDBO_API
|
|
parseSql(const std::string& sql, SelectFieldLists& fieldLists,
|
|
bool& simpleSelectCount);
|
|
|
|
template <class Result>
|
|
QueryBase<Result>::QueryBase()
|
|
: session_(0)
|
|
{ }
|
|
|
|
template <class Result>
|
|
QueryBase<Result>::QueryBase(Session& session, const std::string& sql)
|
|
: session_(&session),
|
|
sql_(sql)
|
|
{
|
|
parseSql(sql_, selectFieldLists_, simpleCount_);
|
|
}
|
|
|
|
template <class Result>
|
|
QueryBase<Result>::QueryBase(Session& session, const std::string& table,
|
|
const std::string& where)
|
|
: session_(&session)
|
|
{
|
|
sql_ = "from " + table + ' ' + where;
|
|
|
|
simpleCount_ = true;
|
|
}
|
|
|
|
template <class Result>
|
|
QueryBase<Result>& QueryBase<Result>::operator=(const QueryBase<Result>& other)
|
|
{
|
|
session_ = other.session_;
|
|
sql_ = other.sql_;
|
|
selectFieldLists_ = other.selectFieldLists_;
|
|
simpleCount_ = other.simpleCount_;
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Result>
|
|
std::vector<FieldInfo> QueryBase<Result>::fields() const
|
|
{
|
|
std::vector<FieldInfo> result;
|
|
|
|
if (selectFieldLists_.empty())
|
|
query_result_traits<Result>::getFields(*session_, 0, result);
|
|
else {
|
|
/*
|
|
* We'll build only the aliases from the first selection list
|
|
* (this matters only for compound selects
|
|
*/
|
|
fieldsForSelect(selectFieldLists_[0], result);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
template <class Result>
|
|
std::pair<SqlStatement *, SqlStatement *>
|
|
QueryBase<Result>::statements(const std::string& where,
|
|
const std::string& groupBy,
|
|
const std::string& orderBy,
|
|
int limit, int offset) const
|
|
{
|
|
SqlStatement *statement, *countStatement;
|
|
|
|
if (selectFieldLists_.empty()) {
|
|
/*
|
|
* sql_ is "from ..."
|
|
*/
|
|
std::string sql;
|
|
|
|
std::vector<FieldInfo> fs = this->fields();
|
|
sql = Impl::createQuerySelectSql(sql_, where, groupBy, orderBy,
|
|
limit, offset, fs);
|
|
statement = this->session_->getOrPrepareStatement(sql);
|
|
|
|
if (simpleCount_)
|
|
sql = Impl::createQueryCountSql(sql, sql_, where, groupBy, orderBy,
|
|
limit, offset);
|
|
else
|
|
sql = Impl::createWrappedQueryCountSql(sql);
|
|
|
|
countStatement = this->session_->getOrPrepareStatement(sql);
|
|
} else {
|
|
/*
|
|
* sql_ is complete "[with ...] select ..."
|
|
*/
|
|
std::string sql = sql_;
|
|
int sql_offset = 0;
|
|
|
|
std::vector<FieldInfo> fs;
|
|
for (unsigned i = 0; i < selectFieldLists_.size(); ++i) {
|
|
const SelectFieldList& list = selectFieldLists_[i];
|
|
|
|
fs.clear();
|
|
this->fieldsForSelect(list, fs);
|
|
|
|
Impl::substituteFields(list, fs, sql, sql_offset);
|
|
}
|
|
|
|
sql = Impl::completeQuerySelectSql(sql, where, groupBy, orderBy,
|
|
limit, offset, fs);
|
|
|
|
statement = this->session_->getOrPrepareStatement(sql);
|
|
|
|
if (simpleCount_) {
|
|
std::string from = sql_.substr(selectFieldLists_.front().back().end);
|
|
sql = Impl::createQueryCountSql(sql, from, where, groupBy, orderBy,
|
|
limit, offset);
|
|
} else
|
|
sql = Impl::createWrappedQueryCountSql(sql);
|
|
|
|
countStatement = this->session_->getOrPrepareStatement(sql);
|
|
}
|
|
|
|
return std::make_pair(statement, countStatement);
|
|
}
|
|
|
|
template <class Result>
|
|
void QueryBase<Result>::fieldsForSelect(const SelectFieldList& list,
|
|
std::vector<FieldInfo>& result) const
|
|
{
|
|
std::vector<std::string> aliases;
|
|
for (unsigned i = 0; i < list.size(); ++i) {
|
|
const SelectField& field = list[i];
|
|
aliases.push_back(sql_.substr(field.begin, field.end - field.begin));
|
|
}
|
|
|
|
query_result_traits<Result>::getFields(*session_, &aliases, result);
|
|
if (!aliases.empty())
|
|
throw std::logic_error("Session::query(): too many aliases for result");
|
|
}
|
|
|
|
template <class Result>
|
|
Session& QueryBase<Result>::session() const
|
|
{
|
|
return *session_;
|
|
}
|
|
|
|
template <class Result>
|
|
Result QueryBase<Result>::singleResult(const collection<Result>& results) const
|
|
{
|
|
typename collection<Result>::const_iterator i = results.begin();
|
|
if (i == results.end())
|
|
return Result();
|
|
else {
|
|
Result result = *i;
|
|
++i;
|
|
if (i != results.end())
|
|
throw NoUniqueResultException();
|
|
return result;
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DirectBinding>::Query()
|
|
: statement_(0),
|
|
countStatement_(0)
|
|
{ }
|
|
|
|
template <class Result>
|
|
Query<Result, DirectBinding>::Query(Session& session, const std::string& sql)
|
|
: Impl::QueryBase<Result>(session, sql),
|
|
statement_(0),
|
|
countStatement_(0)
|
|
{
|
|
prepareStatements();
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DirectBinding>::Query(Session& session, const std::string& table,
|
|
const std::string& where)
|
|
: Impl::QueryBase<Result>(session, table, where),
|
|
statement_(0),
|
|
countStatement_(0)
|
|
{
|
|
prepareStatements();
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DirectBinding>::~Query()
|
|
{
|
|
if (statement_)
|
|
statement_->done();
|
|
if (countStatement_)
|
|
countStatement_->done();
|
|
}
|
|
|
|
template <class Result>
|
|
template <typename T>
|
|
Query<Result, DirectBinding>&
|
|
Query<Result, DirectBinding>::bind(const T& value)
|
|
{
|
|
sql_value_traits<T>::bind(value, this->statement_, column_, -1);
|
|
sql_value_traits<T>::bind(value, this->countStatement_, column_, -1);
|
|
|
|
++column_;
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Result>
|
|
Result Query<Result, DirectBinding>::resultValue() const
|
|
{
|
|
return this->singleResult(resultList());
|
|
}
|
|
|
|
template <class Result>
|
|
collection<Result> Query<Result, DirectBinding>::resultList() const
|
|
{
|
|
if (!this->session_)
|
|
return collection<Result>();
|
|
|
|
if (!statement_)
|
|
throw std::logic_error("Query<Result, DirectBinding>::resultList() "
|
|
"may be called only once");
|
|
|
|
SqlStatement *s = this->statement_, *cs = this->countStatement_;
|
|
this->statement_ = this->countStatement_ = 0;
|
|
|
|
return collection<Result>(this->session_, s, cs);
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DirectBinding>::operator Result () const
|
|
{
|
|
return resultValue();
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DirectBinding>::operator collection<Result> () const
|
|
{
|
|
return resultList();
|
|
}
|
|
|
|
template <class Result>
|
|
void Query<Result, DirectBinding>::prepareStatements() const
|
|
{
|
|
if (!this->session_)
|
|
return;
|
|
|
|
this->session_->flush();
|
|
|
|
boost::tie(this->statement_, this->countStatement_)
|
|
= this->statements(std::string(), std::string(), std::string(), -1, -1);
|
|
|
|
column_ = 0;
|
|
}
|
|
|
|
namespace Impl {
|
|
template <typename T>
|
|
void Parameter<T>::bind(SaveBaseAction& binder)
|
|
{
|
|
field(binder, v_, "parameter");
|
|
}
|
|
|
|
template <typename T>
|
|
Parameter<T> *Parameter<T>::clone() const
|
|
{
|
|
return new Parameter<T>(v_);
|
|
}
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>::Query()
|
|
: limit_(-1),
|
|
offset_(-1)
|
|
{ }
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>::Query(Session& session, const std::string& sql)
|
|
: Impl::QueryBase<Result>(session, sql),
|
|
limit_(-1),
|
|
offset_(-1)
|
|
{ }
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>::Query(Session& session,
|
|
const std::string& table,
|
|
const std::string& where)
|
|
: Impl::QueryBase<Result>(session, table, where),
|
|
limit_(-1),
|
|
offset_(-1)
|
|
{ }
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>
|
|
::Query(const Query<Result, DynamicBinding>& other)
|
|
: Impl::QueryBase<Result>(other),
|
|
where_(other.where_),
|
|
groupBy_(other.groupBy_),
|
|
orderBy_(other.orderBy_),
|
|
limit_(other.limit_),
|
|
offset_(other.offset_)
|
|
{
|
|
for (unsigned i = 0; i < other.parameters_.size(); ++i)
|
|
parameters_.push_back(other.parameters_[i]->clone());
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>&
|
|
Query<Result, DynamicBinding>::operator=
|
|
(const Query<Result, DynamicBinding>& other)
|
|
{
|
|
Impl::QueryBase<Result>::operator=(other);
|
|
where_ = other.where_;
|
|
groupBy_ = other.groupBy_;
|
|
orderBy_ = other.orderBy_;
|
|
limit_ = other.limit_;
|
|
offset_ = other.offset_;
|
|
|
|
reset();
|
|
|
|
for (unsigned i = 0; i < other.parameters_.size(); ++i)
|
|
parameters_.push_back(other.parameters_[i]->clone());
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>::~Query()
|
|
{
|
|
reset();
|
|
}
|
|
|
|
template <class Result>
|
|
template <typename T>
|
|
Query<Result, DynamicBinding>&
|
|
Query<Result, DynamicBinding>::bind(const T& value)
|
|
{
|
|
parameters_.push_back(new Impl::Parameter<T>(value));
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>&
|
|
Query<Result, DynamicBinding>::where(const std::string& where)
|
|
{
|
|
if (!where_.empty())
|
|
where_ += " and ";
|
|
|
|
where_ += "(" + where + ")";
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>&
|
|
Query<Result, DynamicBinding>::orderBy(const std::string& orderBy)
|
|
{
|
|
orderBy_ = orderBy;
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>&
|
|
Query<Result, DynamicBinding>::groupBy(const std::string& groupBy)
|
|
{
|
|
groupBy_ = groupBy;
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>&
|
|
Query<Result, DynamicBinding>::offset(int offset)
|
|
{
|
|
offset_ = offset;
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Result>
|
|
int Query<Result, DynamicBinding>::offset() const
|
|
{
|
|
return offset_;
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>&
|
|
Query<Result, DynamicBinding>::limit(int limit)
|
|
{
|
|
limit_ = limit;
|
|
|
|
return *this;
|
|
}
|
|
|
|
template <class Result>
|
|
int Query<Result, DynamicBinding>::limit() const
|
|
{
|
|
return limit_;
|
|
}
|
|
|
|
template <class Result>
|
|
Result Query<Result, DynamicBinding>::resultValue() const
|
|
{
|
|
return this->singleResult(resultList());
|
|
}
|
|
|
|
template <class Result>
|
|
collection<Result> Query<Result, DynamicBinding>::resultList() const
|
|
{
|
|
if (!this->session_)
|
|
return collection<Result>();
|
|
|
|
this->session_->flush();
|
|
|
|
SqlStatement *statement, *countStatement;
|
|
|
|
boost::tie(statement, countStatement)
|
|
= this->statements(where_, groupBy_, orderBy_, offset_ + 1, limit_ + offset_ + 1);
|
|
|
|
bindParameters(statement);
|
|
bindParameters(countStatement);
|
|
|
|
return collection<Result>(this->session_, statement, countStatement);
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>::operator Result () const
|
|
{
|
|
return resultValue();
|
|
}
|
|
|
|
template <class Result>
|
|
Query<Result, DynamicBinding>::operator collection<Result> () const
|
|
{
|
|
return resultList();
|
|
}
|
|
|
|
template <class Result>
|
|
void Query<Result, DynamicBinding>::bindParameters(SqlStatement *statement)
|
|
const
|
|
{
|
|
SaveBaseAction binder(statement, 0);
|
|
|
|
for (unsigned i = 0; i < parameters_.size(); ++i)
|
|
parameters_[i]->bind(binder);
|
|
|
|
if (offset_ != -1) {
|
|
int v = offset_ + 1;
|
|
field(binder, v, "limit");
|
|
}
|
|
|
|
if (limit_ != -1) {
|
|
int v = limit_ + offset_ + 1;
|
|
field(binder, v, "offset");
|
|
}
|
|
}
|
|
|
|
template <class Result>
|
|
void Query<Result, DynamicBinding>::reset()
|
|
{
|
|
for (unsigned i = 0; i < parameters_.size(); ++i)
|
|
delete parameters_[i];
|
|
|
|
parameters_.clear();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
#endif // DOXYGEN_ONLY
|
|
|
|
#endif // WT_DBO_QUERY_IMPL_H_
|