Merge branch '2.2' into develop
This commit is contained in:
18
server/modules/filter/cache/test/CMakeLists.txt
vendored
18
server/modules/filter/cache/test/CMakeLists.txt
vendored
@ -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)
|
||||
|
||||
453
server/modules/filter/cache/test/test_cacheoptions.cc
vendored
Normal file
453
server/modules/filter/cache/test/test_cacheoptions.cc
vendored
Normal 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;
|
||||
}
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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())
|
||||
{
|
||||
|
||||
@ -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
|
||||
//
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user