Merge branch '2.2' into develop

This commit is contained in:
Johan Wikman
2018-01-02 10:13:57 +02:00
444 changed files with 15589 additions and 6893 deletions

View File

@ -1,4 +1,5 @@
include_directories(..)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../test)
add_library(cachetester
tester.cc
@ -27,6 +28,21 @@ target_link_libraries(testrawstorage cachetester cache maxscale-common)
add_executable(testlrustorage testlrustorage.cc)
target_link_libraries(testlrustorage cachetester cache maxscale-common)
add_executable(test_cacheoptions
test_cacheoptions.cc
../../test/filtermodule.cc
../../test/mock.cc
../../test/mock_backend.cc
../../test/mock_client.cc
../../test/mock_dcb.cc
../../test/mock_routersession.cc
../../test/mock_session.cc
../../test/module.cc
../../test/queryclassifiermodule.cc
)
target_link_libraries(test_cacheoptions maxscale-common)
add_test(TestCache_rules testrules)
add_test(TestCache_inmemory_keygeneration testkeygeneration storage_inmemory ${CMAKE_CURRENT_SOURCE_DIR}/input.test)
@ -39,3 +55,5 @@ add_test(TestCache_storage_inmemory testrawstorage storage_inmemory 0 10 1000 10
#usage: testlrustorage storage-module [threads [time [items [min-size [max-size]]]]]\n"
add_test(TestCache_lru_inmemory testlrustorage storage_inmemory 0 10 1000 1024 1024000)
#add_test(TestCache_lru_rocksdb testlrustorage storage_rocksdb 0 10 1000 1024 1024000)
add_test(TestCache_options test_cacheoptions)

View File

@ -0,0 +1,453 @@
/*
* Copyright (c) 2016 MariaDB Corporation Ab
*
* Use of this software is governed by the Business Source License included
* in the LICENSE.TXT file and at www.mariadb.com/bsl11.
*
* Change Date: 2020-01-01
*
* On the date above, in accordance with the Business Source License, use
* of this software will be governed by version 2 or later of the General
* Public License.
*/
#include <iostream>
#include <sstream>
#include <maxscale/filtermodule.hh>
#include <maxscale/mock/backend.hh>
#include <maxscale/mock/client.hh>
#include <maxscale/mock/routersession.hh>
#include <maxscale/mock/session.hh>
#include "../cachefilter.h"
using namespace std;
using maxscale::FilterModule;
namespace mock = maxscale::mock;
namespace
{
struct CONFIG
{
bool stop_at_first_error;
} config =
{
true, // stop_at_first_error
};
// See https://github.com/mariadb-corporation/MaxScale/blob/2.2/Documentation/Filters/Cache.md#cache_inside_transactions
struct TEST_CASE
{
cache_in_trxs_t cit; /*< How to cache in transactions. */
mxs_session_trx_state_t trx_state; /*< The transaction state. */
bool should_use; /*< Whether the cache should be returned from the cache. */
} TEST_CASES[] =
{
{
CACHE_IN_TRXS_NEVER,
SESSION_TRX_INACTIVE,
true // should_use
},
{
CACHE_IN_TRXS_NEVER,
SESSION_TRX_ACTIVE,
false // should_use
},
{
CACHE_IN_TRXS_NEVER,
SESSION_TRX_READ_ONLY,
false // should_use
},
{
CACHE_IN_TRXS_READ_ONLY,
SESSION_TRX_INACTIVE,
true // should_use
},
{
CACHE_IN_TRXS_READ_ONLY,
SESSION_TRX_ACTIVE,
false // should_use
},
{
CACHE_IN_TRXS_READ_ONLY,
SESSION_TRX_READ_ONLY,
true // should_use
},
{
CACHE_IN_TRXS_ALL,
SESSION_TRX_INACTIVE,
true // should_use
},
{
CACHE_IN_TRXS_ALL,
SESSION_TRX_ACTIVE,
true // should_use
},
{
CACHE_IN_TRXS_ALL,
SESSION_TRX_READ_ONLY,
true // should_use
},
};
const size_t N_TEST_CASES = sizeof(TEST_CASES) / sizeof(TEST_CASES[0]);
const char* to_string(cache_in_trxs_t x)
{
switch (x)
{
case CACHE_IN_TRXS_NEVER:
return "never";
case CACHE_IN_TRXS_READ_ONLY:
return "read_only_transactions";
case CACHE_IN_TRXS_ALL:
return "all_transactions";
default:
ss_dassert(!true);
return NULL;
}
}
ostream& operator << (ostream& out, cache_in_trxs_t x)
{
out << to_string(x);
return out;
}
ostream& operator << (ostream& out, mxs_session_trx_state_t trx_state)
{
out << session_trx_state_to_string(trx_state);
return out;
}
}
namespace
{
static int counter = 0;
string create_unique_select()
{
stringstream ss;
ss << "SELECT col" << ++counter << " FROM tbl";
return ss.str();
}
int test(mock::Session& session,
FilterModule::Session& filter_session,
mock::RouterSession& router_session,
const TEST_CASE& tc)
{
int rv = 0;
mock::Client& client = session.client();
// Let's check that there's nothing pending.
ss_dassert(client.n_responses() == 0);
ss_dassert(router_session.idle());
session.set_trx_state(tc.trx_state);
session.set_autocommit(tc.trx_state == SESSION_TRX_INACTIVE);
string select(create_unique_select());
GWBUF* pStatement;
pStatement = mock::create_com_query(select);
cout << "Performing select: \"" << select << "\"" << flush;
filter_session.routeQuery(pStatement);
if (!router_session.idle())
{
cout << ", reached backend." << endl;
// Let's cause the backend to respond.
router_session.respond();
// And let's verify that the backend is now empty...
ss_dassert(router_session.idle());
// ...and that we have received a response.
ss_dassert(client.n_responses() == 1);
// Let's do the select again.
pStatement = mock::create_com_query(select);
cout << "Performing same select: \"" << select << "\"" << flush;
filter_session.routeQuery(pStatement);
if (tc.should_use)
{
if (!router_session.idle())
{
cout << "\nERROR: Select reached backend and was not provided from cache." << endl;
router_session.respond();
++rv;
}
else
{
cout << ", cache was used." << endl;
// Let's check we did receive a response.
ss_dassert(client.n_responses() == 2);
}
}
else
{
if (router_session.idle())
{
cout << "\nERROR: Select was provided from cache and did not reach backend." << endl;
++rv;
}
else
{
cout << ", reached backend." << endl;
router_session.respond();
}
}
if ((tc.trx_state != SESSION_TRX_INACTIVE) && (tc.trx_state != SESSION_TRX_READ_ONLY))
{
// A transaction, but not a read-only one.
string update("UPDATE tbl SET a=1;");
pStatement = mock::create_com_query("UPDATE tbl SET a=1;");
cout << "Performing update: \"" << update << "\"" << flush;
filter_session.routeQuery(pStatement);
if (router_session.idle())
{
cout << "\n"
<< "ERROR: Did not reach backend." << endl;
++rv;
}
else
{
cout << ", reached backend." << endl;
router_session.respond();
// Let's make the select again.
pStatement = mock::create_com_query(select);
cout << "Performing select: \"" << select << "\"" << flush;
filter_session.routeQuery(pStatement);
if (router_session.idle())
{
cout << "\nERROR: Did not reach backend." << endl;
++rv;
}
else
{
// The select reached the backend, i.e. the cache was not used after
// a non-SELECT.
cout << ", reached backend." << endl;
router_session.respond();
}
}
}
// Irrespective of what was going on above, the cache should now contain the
// original select. So, let's do a select with no transaction.
cout << "Setting transaction state to SESSION_TRX_INACTIVE" << endl;
session.set_trx_state(SESSION_TRX_INACTIVE);
session.set_autocommit(true);
pStatement = mock::create_com_query(select);
cout << "Performing select: \"" << select << "\"" << flush;
filter_session.routeQuery(pStatement);
if (router_session.idle())
{
cout << ", cache was used." << endl;
}
else
{
cout << "\nERROR: cache was not used." << endl;
router_session.respond();
++rv;
}
}
else
{
cout << "\nERROR: Did not reach backend." << endl;
++rv;
}
return rv;
}
int test(FilterModule::Instance& filter_instance, const TEST_CASE& tc)
{
int rv = 0;
mock::ResultSetBackend backend;
mock::RouterSession router_session(&backend);
mock::Client client("bob", "127.0.0.1");
mock::Session session(&client);
auto_ptr<FilterModule::Session> sFilter_session = filter_instance.newSession(&session);
if (sFilter_session.get())
{
router_session.set_as_downstream_on(sFilter_session.get());
client.set_as_upstream_on(*sFilter_session.get());
rv += test(session, *sFilter_session.get(), router_session, tc);
}
else
{
++rv;
}
return rv;
}
int test(FilterModule& filter_module, const TEST_CASE& tc)
{
int rv = 1;
auto_ptr<FilterModule::ConfigParameters> sParameters = filter_module.create_default_parameters();
sParameters->set_value("cache_in_transactions", to_string(tc.cit));
sParameters->set_value("debug", "31");
auto_ptr<FilterModule::Instance> sInstance = filter_module.createInstance("test", NULL, sParameters);
if (sInstance.get())
{
rv = test(*sInstance, tc);
}
return rv;
}
}
namespace
{
int run()
{
int rv = 1;
auto_ptr<FilterModule> sModule = FilterModule::load("cache");
if (sModule.get())
{
if (maxscale::Module::process_init())
{
if (maxscale::Module::thread_init())
{
rv = 0;
for (size_t i = 0; i < N_TEST_CASES; ++i)
{
const TEST_CASE& tc = TEST_CASES[i];
cout << "CIT: " << tc.cit
<< ", TRX_STATE: " << tc.trx_state
<< ", should use: " << tc.should_use
<< endl;
rv += test(*sModule.get(), tc);
cout << endl;
if ((rv != 0) && config.stop_at_first_error)
{
break;
}
}
maxscale::Module::thread_finish();
}
else
{
cerr << "error: Could not perform thread initialization." << endl;
}
maxscale::Module::process_finish();
}
else
{
cerr << "error: Could not perform process initialization." << endl;
}
}
else
{
cerr << "error: Could not load filter module." << endl;
}
return rv;
}
}
namespace
{
char USAGE[] =
"usage: test_dbfwfilter [-d]\n"
"\n"
"-d don't stop at first error\n";
}
int main(int argc, char* argv[])
{
int rv = 0;
int c;
while ((c = getopt(argc, argv, "d")) != -1)
{
switch (c)
{
case 'd':
config.stop_at_first_error = false;
break;
default:
rv = 1;
}
}
if (rv == 0)
{
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
{
if (qc_setup("qc_sqlite", QC_SQL_MODE_DEFAULT, NULL))
{
if (qc_process_init(QC_INIT_SELF))
{
rv = run();
cout << rv << " failures." << endl;
qc_process_end(QC_INIT_SELF);
}
else
{
cerr << "error: Could not initialize query classifier." << endl;
}
}
else
{
cerr << "error: Could not setup query classifier." << endl;
}
mxs_log_finish();
}
}
else
{
cout << USAGE << endl;
}
return rv;
}

View File

@ -15,6 +15,8 @@
#include <iostream>
#include <fstream>
#include <tr1/unordered_map>
#include <maxscale/alloc.h>
#include <maxscale/paths.h>
#include <maxscale/query_classifier.h>
#include <maxscale/log_manager.h>
#include "storagefactory.hh"
@ -126,11 +128,16 @@ int main(int argc, char* argv[])
if ((argc == 2) || (argc == 3))
{
char* libdir = MXS_STRDUP("../../../../../query_classifier/qc_sqlite/");
set_libdir(libdir);
if (mxs_log_init(NULL, ".", MXS_LOG_TARGET_DEFAULT))
{
if (qc_setup(NULL, QC_SQL_MODE_DEFAULT, NULL) && qc_process_init(QC_INIT_BOTH))
{
const char* zModule = argv[1];
libdir = MXS_STRDUP("../storage/storage_inmemory/");
set_libdir(libdir);
StorageFactory* pFactory = StorageFactory::Open(zModule);
@ -172,6 +179,9 @@ int main(int argc, char* argv[])
{
cerr << "error: Could not initialize log." << endl;
}
// TODO: Remove this once globally allocated memory is freed
MXS_FREE(libdir);
}
else
{

View File

@ -13,6 +13,8 @@
#include <maxscale/cppdefs.hh>
#include <iostream>
#include <maxscale/alloc.h>
#include <maxscale/paths.h>
#include "teststorage.hh"
#include "testerlrustorage.hh"
@ -46,7 +48,13 @@ private:
int main(int argc, char* argv[])
{
TestLRUStorage test(&cout);
char* libdir = MXS_STRDUP("../../../../../query_classifier/qc_sqlite/");
set_libdir(libdir);
return test.run(argc, argv);
TestLRUStorage test(&cout);
int rv = test.run(argc, argv);
// TODO: Remove this once globally allocated memory is freed
MXS_FREE(libdir);
return rv;
}

View File

@ -13,6 +13,8 @@
#include <maxscale/cppdefs.hh>
#include <iostream>
#include <maxscale/alloc.h>
#include <maxscale/paths.h>
#include "teststorage.hh"
#include "testerrawstorage.hh"
@ -46,7 +48,13 @@ private:
int main(int argc, char* argv[])
{
TestRawStorage test(&cout);
char* libdir = MXS_STRDUP("../../../../../query_classifier/qc_sqlite/");
set_libdir(libdir);
return test.run(argc, argv);
TestRawStorage test(&cout);
int rv = test.run(argc, argv);
// TODO: Remove this once globally allocated memory is freed
MXS_FREE(libdir);
return rv;
}

View File

@ -815,10 +815,11 @@ int test(FilterModule& filter_module, const FW_TEST& t)
TempFile file;
file.write(t.zRules);
MXS_CONFIG_PARAMETER action { (char*)"action", (char*)zAction, NULL };
MXS_CONFIG_PARAMETER rules = { (char*)"rules", (char*)file.name().c_str(), &action };
auto_ptr<FilterModule::ConfigParameters> sParameters = filter_module.create_default_parameters();
sParameters->set_value("action", zAction);
sParameters->set_value("rules", file.name());
auto_ptr<FilterModule::Instance> sInstance = filter_module.createInstance("test", NULL, &rules);
auto_ptr<FilterModule::Instance> sInstance = filter_module.createInstance("test", NULL, sParameters);
if (sInstance.get())
{

View File

@ -42,6 +42,13 @@ auto_ptr<FilterModule::Instance> FilterModule::createInstance(const char* zName,
return sInstance;
}
auto_ptr<FilterModule::Instance> FilterModule::createInstance(const char* zName,
char** pzOptions,
std::auto_ptr<ConfigParameters> sParameters)
{
return createInstance(zName, pzOptions, sParameters.get());
}
//
// FilterModule::Instance
//

View File

@ -23,14 +23,13 @@ namespace maxscale
/**
* An instance of FilterModule represents a filter module.
*/
class FilterModule : public SpecificModule<FilterModule>
class FilterModule : public SpecificModule<FilterModule, MXS_FILTER_OBJECT>
{
FilterModule(const FilterModule&);
FilterModule& operator = (const FilterModule&);
public:
static const char* zName; /*< The name describing the module type. */
typedef MXS_FILTER_OBJECT type_t; /*< The type of the module object. */
static const char* zName; /*< The name describing the module type. */
class Session;
class Instance
@ -141,6 +140,10 @@ public:
char** pzOptions,
MXS_CONFIG_PARAMETER* pParameters);
std::auto_ptr<Instance> createInstance(const char* zName,
char** pzOptions,
std::auto_ptr<ConfigParameters> sParameters);
private:
friend class Instance;
@ -184,15 +187,13 @@ private:
}
private:
friend class SpecificModule<FilterModule>;
// Not accepted by CentOS6: friend Base;
friend class SpecificModule<FilterModule, MXS_FILTER_OBJECT>;
FilterModule(MXS_FILTER_OBJECT* pApi)
: m_pApi(pApi)
FilterModule(const MXS_MODULE* pModule)
: SpecificModule<FilterModule, MXS_FILTER_OBJECT>(pModule)
{
}
private:
MXS_FILTER_OBJECT* m_pApi;
};
}

View File

@ -14,6 +14,7 @@
#include <maxscale/cppdefs.hh>
#include <map>
#include <maxscale/resultset.h>
#include "routersession.hh"
namespace maxscale
@ -33,6 +34,13 @@ class Backend
public:
virtual ~Backend();
/**
* Create an OK response.
*
* @return A GWBUF containing an OK response packet.
*/
static GWBUF* create_ok_response();
/**
* Called to handle a statement from a "client".
*
@ -135,6 +143,31 @@ public:
void handle_statement(RouterSession* pSession, GWBUF* pStatement);
};
/**
* The ResultsetBackend
*/
class ResultSetBackend : public BufferBackend
{
ResultSetBackend(const ResultSetBackend&);
ResultSetBackend& operator = (const ResultSetBackend&);
public:
ResultSetBackend();
void reset()
{
m_created = false;
}
void handle_statement(RouterSession* pSession, GWBUF* pStatement);
virtual RESULT_ROW* create_row(RESULTSET* pResult_set);
static RESULT_ROW* create_row(RESULTSET* pResult_set, void* pThis);
int m_counter;
bool m_created;
};
}
}

View File

@ -33,6 +33,8 @@ class Session : public MXS_SESSION
Session& operator = (Session&);
public:
typedef mxs_session_trx_state_t trx_state_t;
/**
* Constructor
*
@ -44,6 +46,26 @@ public:
Client& client() const;
bool is_autocommit() const
{
return session_is_autocommit(this);
}
void set_autocommit(bool autocommit)
{
session_set_autocommit(this, autocommit);
}
trx_state_t trx_state() const
{
return session_get_trx_state(this);
}
void set_trx_state(trx_state_t state)
{
session_set_trx_state(this, state);
}
private:
Client& m_client;
Dcb m_client_dcb;

View File

@ -14,6 +14,9 @@
#include <maxscale/cppdefs.hh>
#include <memory>
#include <deque>
#include <maxscale/config.h>
#include <maxscale/modinfo.h>
namespace maxscale
{
@ -25,16 +28,74 @@ namespace maxscale
class Module
{
public:
class ConfigParameters : public MXS_CONFIG_PARAMETER
{
ConfigParameters(const ConfigParameters&);
ConfigParameters& operator = (const ConfigParameters&);
public:
~ConfigParameters();
/**
* Get the value of a parameter
*
* @param zName The name of a parameter.
*
* @return The value of the parameter or NULL if the parameter does not exist.
*/
const char* get(const char* zName) const;
/**
* Set the value of a parameter
*
* @param zName The name of a parameter.
* @param zValue The value of the parameter.
*/
void set_value(const char* zName, const char* zValue);
void set_value(const char* zName, const std::string& value);
private:
friend class Module;
ConfigParameters(const MXS_MODULE_PARAM* pParams);
const MXS_CONFIG_PARAMETER* get_param(const char* zName) const;
MXS_CONFIG_PARAMETER* get_param(const char* zName);
MXS_CONFIG_PARAMETER* get_tail();
std::deque<std::string> m_values; /** Storage for modified parameters. */
};
/**
* Get a ConfigParameters instance containing the default values
* of all parameters.
*
* @return A ConfigParameters object.
*/
std::auto_ptr<ConfigParameters> create_default_parameters() const;
/**
* Load a module with a specific name, assumed to be of a specific type.
*
* @param zFile_name The name of the module.
* @param zType_name The expected type of the module.
*
* @return The module object, if the module could be loaded, otherwise NULL.
* @return The module specific entry point structure or NULL.
*/
static void* load(const char *zFile_name, const char *zType_name);
/**
* Get a module with a specific name, assumed to be of a specific type.
*
* @param zFile_name The name of the module.
* @param zType_name The expected type of the module.
*
* @return The loaded module, if the module could be loaded, otherwise NULL.
*/
static const MXS_MODULE* get(const char *zFile_name, const char *zType_name);
/**
* Perform process initialization of all modules. Should be called only
* when all modules intended to be loaded have been loaded.
@ -60,41 +121,55 @@ public:
* Perform thread finalization of all modules.
*/
static void thread_finish();
protected:
Module(const MXS_MODULE* pModule)
: m_module(*pModule)
{
}
const MXS_MODULE& m_module;
};
/**
* The template Module is intended to be derived from using the derived
* class as template argument.
*
* class XyzModule : public SpecificModule<XyzModule> { ... }
* class XyzModule : public SpecificModule<XyzModule, XYZ_MODULE_OBJECT> { ... }
*
* @param zFile_name The name of the module.
*
* @return A module instance if the module could be loaded and it was of
* the expected type.
*/
template<class T>
template<class T, class API>
class SpecificModule : public Module
{
public:
typedef SpecificModule<T, API> Base;
static std::auto_ptr<T> load(const char* zFile_name)
{
std::auto_ptr<T> sT;
void* pApi = Module::load(zFile_name, T::zName);
const MXS_MODULE* pModule = Module::get(zFile_name, T::zName);
if (pApi)
if (pModule)
{
sT.reset(new T(static_cast<typename T::type_t*>(pApi)));
sT.reset(new T(pModule));
}
return sT;
}
protected:
SpecificModule()
SpecificModule(const MXS_MODULE* pModule)
: Module(pModule)
, m_pApi(static_cast<API*>(pModule->module_object))
{
}
API* m_pApi;
};
}

View File

@ -24,25 +24,19 @@ namespace maxscale
* A QueryClassfierModule instance is an abstraction for a query
* classifier module.
*/
class QueryClassifierModule : public SpecificModule<QueryClassifierModule>
class QueryClassifierModule : public SpecificModule<QueryClassifierModule, QUERY_CLASSIFIER>
{
QueryClassifierModule(const QueryClassifierModule&);
QueryClassifierModule& operator = (const QueryClassifierModule&);
public:
static const char* zName; /*< The name describing the module type. */
typedef QUERY_CLASSIFIER type_t; /*< The type of the module object. */
static const char* zName; /*< The name describing the module type. */
private:
friend class SpecificModule<QueryClassifierModule>;
QueryClassifierModule(QUERY_CLASSIFIER* pApi)
: m_pApi(pApi)
QueryClassifierModule(const MXS_MODULE* pModule)
: Base(pModule)
{
}
private:
QUERY_CLASSIFIER* m_pApi;
};
}

View File

@ -13,6 +13,8 @@
#include "maxscale/mock/backend.hh"
#include <algorithm>
#include <vector>
#include <maxscale/query_classifier.h>
#include <maxscale/protocol/mysql.h>
#include <iostream>
@ -36,6 +38,19 @@ Backend::~Backend()
{
}
//static
GWBUF* Backend::create_ok_response()
{
/* Note: sequence id is always 01 (4th byte) */
const static uint8_t ok[MYSQL_OK_PACKET_MIN_LEN] =
{ 07, 00, 00, 01, 00, 00, 00, 02, 00, 00, 00 };
GWBUF* pResponse = gwbuf_alloc_and_load(sizeof(ok), &ok);
ss_dassert(pResponse);
return pResponse;
}
//
// BufferBackend
//
@ -138,18 +153,108 @@ OkBackend::OkBackend()
void OkBackend::handle_statement(RouterSession* pSession, GWBUF* pStatement)
{
/* Note: sequence id is always 01 (4th byte) */
const static uint8_t ok[MYSQL_OK_PACKET_MIN_LEN] =
{ 07, 00, 00, 01, 00, 00, 00, 02, 00, 00, 00 };
GWBUF* pResponse = gwbuf_alloc_and_load(sizeof(ok), &ok);
ss_dassert(pResponse);
enqueue_response(pSession, pResponse);
enqueue_response(pSession, create_ok_response());
gwbuf_free(pStatement);
}
//
// ResultSetBackend
//
ResultSetBackend::ResultSetBackend()
: m_counter(0)
, m_created(false)
{
}
namespace
{
class ResultSetDCB : public DCB
{
public:
ResultSetDCB()
{
DCB* pDcb = this;
memset(pDcb, 0, sizeof(*pDcb));
pDcb->func.write = &ResultSetDCB::write;
}
GWBUF* create_response() const
{
return gwbuf_alloc_and_load(m_response.size(), &m_response.front());
}
private:
int32_t write(GWBUF* pBuffer)
{
pBuffer = gwbuf_make_contiguous(pBuffer);
ss_dassert(pBuffer);
unsigned char* begin = GWBUF_DATA(pBuffer);
unsigned char* end = begin + GWBUF_LENGTH(pBuffer);
m_response.insert(m_response.end(), begin, end);
gwbuf_free(pBuffer);
return 1;
}
static int32_t write(DCB* pDcb, GWBUF* pBuffer)
{
return static_cast<ResultSetDCB*>(pDcb)->write(pBuffer);
}
std::vector<char> m_response;
};
}
void ResultSetBackend::handle_statement(RouterSession* pSession, GWBUF* pStatement)
{
qc_query_op_t op = qc_get_operation(pStatement);
if (op == QUERY_OP_SELECT)
{
RESULTSET* pResult_set = resultset_create(ResultSetBackend::create_row, this);
resultset_add_column(pResult_set, "a", 4, COL_TYPE_VARCHAR);
ResultSetDCB dcb;
resultset_stream_mysql(pResult_set, &dcb);
enqueue_response(pSession, dcb.create_response());
}
else
{
enqueue_response(pSession, create_ok_response());
}
}
RESULT_ROW* ResultSetBackend::create_row(RESULTSET* pResult_set)
{
RESULT_ROW* pRow = NULL;
if (!m_created)
{
pRow = resultset_make_row(pResult_set);
char buffer[32];
sprintf(buffer, "%d", ++m_counter);
resultset_row_set(pRow, 0, buffer);
m_created = true;
}
return pRow;
}
//static
RESULT_ROW* ResultSetBackend::create_row(RESULTSET* pResult_set, void* pThis)
{
return static_cast<ResultSetBackend*>(pThis)->create_row(pResult_set);
}
} // mock
} // maxscale

View File

@ -12,17 +12,157 @@
*/
#include "maxscale/module.hh"
#include <string>
#include "../../../core/internal/modules.h"
using std::auto_ptr;
namespace maxscale
{
//
// Module::ConfigParameters
//
Module::ConfigParameters::ConfigParameters(const MXS_MODULE_PARAM* pParams)
{
this->name = NULL;
this->value = NULL;
this->next = NULL;
MXS_CONFIG_PARAMETER* pCurrent = this;
while (pParams->name)
{
if (pParams->name && pParams->default_value)
{
if (this->name == NULL)
{
this->name = const_cast<char*>(pParams->name);
this->value = const_cast<char*>(pParams->default_value);
}
else
{
MXS_CONFIG_PARAMETER* pNext = new MXS_CONFIG_PARAMETER;
pNext->name = const_cast<char*>(pParams->name);
pNext->value = const_cast<char*>(pParams->default_value);
pNext->next = NULL;
pCurrent->next = pNext;
pCurrent = pNext;
}
}
++pParams;
}
}
Module::ConfigParameters::~ConfigParameters()
{
MXS_CONFIG_PARAMETER* pNext = this->next;
while (pNext)
{
MXS_CONFIG_PARAMETER* pCurrent = pNext;
pNext = pNext->next;
delete pCurrent;
}
}
const char* Module::ConfigParameters::get(const char* zName) const
{
const MXS_CONFIG_PARAMETER* pParam = get_param(zName);
return pParam ? pParam->value : NULL;
}
void Module::ConfigParameters::set_value(const char* zName, const char* zValue)
{
set_value(zName, std::string(zValue));
}
void Module::ConfigParameters::set_value(const char* zName, const std::string& value)
{
MXS_CONFIG_PARAMETER* pParam = get_param(zName);
if (!pParam)
{
MXS_CONFIG_PARAMETER* pTail = get_tail();
pParam = new MXS_CONFIG_PARAMETER;
m_values.push_back(zName);
pParam->name = const_cast<char*>(m_values.back().c_str());
pParam->value = NULL;
pParam->next = NULL;
pTail->next = pParam;
}
m_values.push_back(value);
pParam->value = const_cast<char*>(m_values.back().c_str());
}
const MXS_CONFIG_PARAMETER* Module::ConfigParameters::get_param(const char* zName) const
{
return const_cast<Module::ConfigParameters*>(this)->get_param(zName);
}
MXS_CONFIG_PARAMETER* Module::ConfigParameters::get_param(const char* zName)
{
MXS_CONFIG_PARAMETER* pParam = NULL;
if (this->name && (strcmp(this->name, zName) == 0))
{
pParam = this;
}
else
{
pParam = this->next;
while (pParam && (strcmp(pParam->name, zName) != 0))
{
pParam = pParam->next;
}
}
return pParam;
}
MXS_CONFIG_PARAMETER* Module::ConfigParameters::get_tail()
{
MXS_CONFIG_PARAMETER* pTail = this;
while (pTail->next)
{
pTail = pTail->next;
}
return pTail;
}
auto_ptr<Module::ConfigParameters> Module::create_default_parameters() const
{
return auto_ptr<ConfigParameters>(new ConfigParameters(m_module.parameters));
}
//
// Module
//
//static
void* Module::load(const char* zName, const char* zType)
{
return load_module(zName, zType);
}
//static
const MXS_MODULE* Module::get(const char* zName, const char* zType)
{
return get_module(zName, zType);
}
//static
bool Module::process_init()
{