Skip to content

Commit

Permalink
Add query transformation callback feature
Browse files Browse the repository at this point in the history
Implementation of query transformation requested in ticket #66:
* Add query_transformation.h with internal types
* Add method session::set_query_transformation<T>() - assigns
user-defined function or functor
* Add session::get_query() - returns string with transformed query.
* Replace use of session::get_query_stream() with get_query()
* Keep session::get_query_stream() to access original query accumulated
in current session statement
* Add test_query_transformation to common tests.
Tested using VS2012 on Windows 8 against DB2, ODBC (PostgreSQL, SQL
Server), MySQL, Oracle, SQLite3.
  • Loading branch information
mloskot committed Feb 26, 2013
1 parent 4aff358 commit 9fffdf6
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 6 deletions.
49 changes: 49 additions & 0 deletions src/core/query_transformation.h
@@ -0,0 +1,49 @@
//
// Copyright (C) 2013 Mateusz Loskot <mateusz@loskot.net>
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef SOCI_QUERY_TRANSFORMATION_H_INCLUDED
#define SOCI_QUERY_TRANSFORMATION_H_INCLUDED

#include "soci-config.h"
#include <functional>
#include <string>

namespace soci
{

namespace details
{

// TODO: use std::unary_function<const std::string&, std::string>
class query_transformation_function : public std::unary_function<std::string, std::string>
{
public:
virtual result_type operator()(argument_type a) const = 0;
};

template <typename T>
class query_transformation : public query_transformation_function
{
public:
query_transformation(T callback)
: callback_(callback)
{}

result_type operator()(argument_type query) const
{
return callback_(query);
}

private:
T callback_;
};

} // namespace details

} // namespace soci

#endif // SOCI_QUERY_TRANSFORMATION_H_INCLUDED
2 changes: 1 addition & 1 deletion src/core/ref-counted-prepare-info.cpp
Expand Up @@ -42,5 +42,5 @@ void ref_counted_prepare_info::final_action()

std::string ref_counted_prepare_info::get_query() const
{
return session_.get_query_stream().str();
return session_.get_query();
}
2 changes: 1 addition & 1 deletion src/core/ref-counted-statement.cpp
Expand Up @@ -23,7 +23,7 @@ void ref_counted_statement::final_action()
try
{
st_.alloc();
st_.prepare(session_.get_query_stream().str(), st_one_time_query);
st_.prepare(session_.get_query(), st_one_time_query);
st_.define_and_bind();

const bool gotData = st_.execute(true);
Expand Down
24 changes: 20 additions & 4 deletions src/core/session.cpp
Expand Up @@ -10,6 +10,7 @@
#include "connection-pool.h"
#include "soci-backend.h"
#include "backend-loader.h"
#include "query_transformation.h"

#ifdef _MSC_VER
#pragma warning(disable:4355)
Expand Down Expand Up @@ -48,15 +49,15 @@ void ensureConnected(session_backend * backEnd)
} // namespace anonymous

session::session()
: once(this), prepare(this), logStream_(NULL),
: once(this), prepare(this), query_transformation_(0), logStream_(NULL),
lastFactory_(NULL), uppercaseColumnNames_(false), backEnd_(NULL),
isFromPool_(false), pool_(NULL)
{
}

session::session(backend_factory const & factory,
std::string const & connectString)
: once(this), prepare(this), logStream_(NULL),
: once(this), prepare(this), query_transformation_(0), logStream_(NULL),
lastFactory_(&factory), lastConnectString_(connectString),
uppercaseColumnNames_(false),
isFromPool_(false), pool_(NULL)
Expand All @@ -66,7 +67,7 @@ session::session(backend_factory const & factory,

session::session(std::string const & backendName,
std::string const & connectString)
: once(this), prepare(this), logStream_(NULL),
: once(this), prepare(this), query_transformation_(0), logStream_(NULL),
uppercaseColumnNames_(false),
isFromPool_(false), pool_(NULL)
{
Expand All @@ -79,7 +80,7 @@ session::session(std::string const & backendName,
}

session::session(std::string const & connectString)
: once(this), prepare(this), logStream_(NULL),
: once(this), prepare(this), query_transformation_(0), logStream_(NULL),
uppercaseColumnNames_(false),
isFromPool_(false), pool_(NULL)
{
Expand Down Expand Up @@ -115,6 +116,7 @@ session::~session()
}
else
{
delete query_transformation_;
delete backEnd_;
}
}
Expand Down Expand Up @@ -260,6 +262,20 @@ std::ostringstream & session::get_query_stream()
}
}

std::string session::get_query() const
{
// preserve logical constness of get_query,
// stream used as read-only here,
session* pthis = const_cast<session*>(this);

// sole place where any user-defined query transformation is applied
if (query_transformation_)
{
return (*query_transformation_)(pthis->get_query_stream().str());
}
return pthis->get_query_stream().str();
}

void session::set_log_stream(std::ostream * s)
{
if (isFromPool_)
Expand Down
11 changes: 11 additions & 0 deletions src/core/session.h
Expand Up @@ -9,6 +9,8 @@
#define SOCI_SESSION_H_INCLUDED

#include "once-temp-type.h"
#include "query_transformation.h"

// std
#include <cstddef>
#include <ostream>
Expand Down Expand Up @@ -62,6 +64,14 @@ class SOCI_DECL session
details::once_temp_type operator<<(T const & t) { return once << t; }

std::ostringstream & get_query_stream();
std::string get_query() const;

template <typename T>
void set_query_transformation(T callback)
{
delete query_transformation_;
query_transformation_= new details::query_transformation<T>(callback);
}

// support for basic logging
void set_log_stream(std::ostream * s);
Expand Down Expand Up @@ -107,6 +117,7 @@ class SOCI_DECL session
session& operator=(session const &);

std::ostringstream query_stream_;
details::query_transformation_function* query_transformation_;

std::ostream * logStream_;
std::string lastQuery_;
Expand Down
47 changes: 47 additions & 0 deletions src/core/test/common-tests.h
Expand Up @@ -307,6 +307,7 @@ class common_tests
test30();
test31();
test_get_affected_rows();
test_query_transformation();
test_pull5();
test_issue67();
}
Expand Down Expand Up @@ -3564,6 +3565,52 @@ void test31()
std::cout << "test 31 passed\n";
}

// Issue 66 - test query transformation callback feature
static std::string lower_than_g(std::string query)
{
return query + " WHERE c < 'g'";
}

struct greater_than_g_lower_than_j : std::unary_function<std::string, std::string>
{
result_type operator()(argument_type query) const
{
return query + " WHERE c > 'g' AND c < 'j'";
}
};

void test_query_transformation()
{
session sql(backEndFactory_, connectString_);
{
// create and populate the test table
auto_table_creator tableCreator(tc_.table_creator_1(sql));

char c;
for (c = 'a'; c <= 'z'; ++c)
{
sql << "insert into soci_test(c) values(\'" << c << "\')";
}

char const* query = "select count(*) from soci_test";
{
sql.set_query_transformation(lower_than_g);
int count;
sql << query, into(count);

assert(count == 'g' - 'a');
}
{
sql.set_query_transformation(greater_than_g_lower_than_j());
int count;
sql << query, into(count);

assert(count == 'j' - 'h');
}
}
std::cout << "test test_query_transformation passed" << std::endl;
}

// Originally, submitted to SQLite3 backend and later moved to common test.
// Test commit b394d039530f124802d06c3b1a969c3117683152
// Author: Mika Fischer <mika.fischer@zoopnet.de>
Expand Down

0 comments on commit 9fffdf6

Please sign in to comment.