Merge branch '2.2' into develop
This commit is contained in:
4
server/modules/filter/cache/rules.cc
vendored
4
server/modules/filter/cache/rules.cc
vendored
@ -1352,9 +1352,9 @@ static bool cache_rule_matches_column_simple(CACHE_RULE *self, const char *defau
|
||||
{
|
||||
matches = !matches;
|
||||
}
|
||||
}
|
||||
|
||||
++i;
|
||||
++i;
|
||||
}
|
||||
|
||||
if (tables)
|
||||
{
|
||||
|
@ -173,8 +173,9 @@ const struct store_test_case store_test_cases[] =
|
||||
STORE_TEST_CASE("query", "!=", "SELECT a FROM tbl", false, NULL, "SELECT a FROM tbl"),
|
||||
STORE_TEST_CASE("query", "=", "SELECT b FROM tbl", false, NULL, "SELECT a FROM tbl"),
|
||||
STORE_TEST_CASE("query", "!=", "SELECT b FROM tbl", true, NULL, "SELECT a FROM tbl"),
|
||||
|
||||
STORE_TEST_CASE("column", "=", "a", false, NULL, "SELECT b FROM tbl WHERE a = 5"),
|
||||
// We are no longer able to distinguish selected columns
|
||||
// from one used in the WHERE-clause.
|
||||
STORE_TEST_CASE("column", "=", "a", true, NULL, "SELECT b FROM tbl WHERE a = 5"),
|
||||
STORE_TEST_CASE("column", "=", "a", true, NULL, "SELECT a, b FROM tbl WHERE a = 5"),
|
||||
};
|
||||
|
||||
@ -242,7 +243,9 @@ int main()
|
||||
pConfig->n_threads = 1;
|
||||
|
||||
set_libdir(MXS_STRDUP_A("../../../../../query_classifier/qc_sqlite/"));
|
||||
if (qc_setup("qc_sqlite", QC_SQL_MODE_DEFAULT, "") && qc_process_init(QC_INIT_BOTH))
|
||||
if (qc_setup("qc_sqlite", QC_SQL_MODE_DEFAULT, "") &&
|
||||
qc_process_init(QC_INIT_BOTH) &&
|
||||
qc_thread_init(QC_INIT_BOTH))
|
||||
{
|
||||
set_libdir(MXS_STRDUP_A("../"));
|
||||
rc = test();
|
||||
|
@ -21,6 +21,10 @@ if(BISON_FOUND AND FLEX_FOUND)
|
||||
target_link_libraries(dbfwchk maxscale-common MySQLCommon dbfwfilter-core)
|
||||
install_executable(dbfwchk core)
|
||||
|
||||
if(BUILD_TESTS)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
else()
|
||||
message(FATAL_ERROR "Could not find Bison or Flex: ${BISON_EXECUTABLE} ${FLEX_EXECUTABLE}")
|
||||
endif()
|
||||
|
||||
|
@ -67,6 +67,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
|
||||
#include <maxscale/atomic.h>
|
||||
#include <maxscale/modulecmd.h>
|
||||
@ -97,16 +98,27 @@ namespace
|
||||
{
|
||||
|
||||
/** The rules and users for each thread */
|
||||
struct DbfwThread
|
||||
class DbfwThread
|
||||
{
|
||||
DbfwThread():
|
||||
rule_version(0)
|
||||
{
|
||||
}
|
||||
public:
|
||||
int& rule_version(const Dbfw* d) { return m_instance_data[d].rule_version; }
|
||||
RuleList& rules(const Dbfw* d) { return m_instance_data[d].rules; }
|
||||
UserMap& users(const Dbfw* d) { return m_instance_data[d].users; }
|
||||
|
||||
int rule_version;
|
||||
RuleList rules;
|
||||
UserMap users;
|
||||
private:
|
||||
class Data
|
||||
{
|
||||
public:
|
||||
Data()
|
||||
: rule_version(0)
|
||||
{}
|
||||
|
||||
int rule_version;
|
||||
RuleList rules;
|
||||
UserMap users;
|
||||
};
|
||||
|
||||
std::map<const Dbfw*, Data> m_instance_data;
|
||||
};
|
||||
|
||||
thread_local DbfwThread* this_thread = NULL;
|
||||
@ -140,7 +152,7 @@ static json_t* rules_to_json(const RuleList& rules)
|
||||
{
|
||||
json_t* rval = json_array();
|
||||
|
||||
for (RuleList::const_iterator it = this_thread->rules.begin(); it != this_thread->rules.end(); it++)
|
||||
for (RuleList::const_iterator it = rules.begin(); it != rules.end(); it++)
|
||||
{
|
||||
const SRule& rule = *it;
|
||||
json_array_append_new(rval, rule_to_json(rule));
|
||||
@ -410,7 +422,10 @@ bool dbfw_show_rules(const MODULECMD_ARG *argv, json_t** output)
|
||||
|
||||
dcb_printf(dcb, "Rule, Type, Times Matched\n");
|
||||
|
||||
if (this_thread->rules.empty() || this_thread->users.empty())
|
||||
RuleList& rules = this_thread->rules(inst);
|
||||
UserMap& users = this_thread->users(inst);
|
||||
|
||||
if (rules.empty() || users.empty())
|
||||
{
|
||||
if (!replace_rules(inst))
|
||||
{
|
||||
@ -418,7 +433,7 @@ bool dbfw_show_rules(const MODULECMD_ARG *argv, json_t** output)
|
||||
}
|
||||
}
|
||||
|
||||
for (RuleList::const_iterator it = this_thread->rules.begin(); it != this_thread->rules.end(); it++)
|
||||
for (RuleList::const_iterator it = rules.begin(); it != rules.end(); it++)
|
||||
{
|
||||
const SRule& rule = *it;
|
||||
char buf[rule->name().length() + 200]; // Some extra space
|
||||
@ -436,7 +451,10 @@ bool dbfw_show_rules_json(const MODULECMD_ARG *argv, json_t** output)
|
||||
|
||||
json_t* arr = json_array();
|
||||
|
||||
if (this_thread->rules.empty() || this_thread->users.empty())
|
||||
RuleList& rules = this_thread->rules(inst);
|
||||
UserMap& users = this_thread->users(inst);
|
||||
|
||||
if (rules.empty() || users.empty())
|
||||
{
|
||||
if (!replace_rules(inst))
|
||||
{
|
||||
@ -444,7 +462,7 @@ bool dbfw_show_rules_json(const MODULECMD_ARG *argv, json_t** output)
|
||||
}
|
||||
}
|
||||
|
||||
for (RuleList::const_iterator it = this_thread->rules.begin(); it != this_thread->rules.end(); it++)
|
||||
for (RuleList::const_iterator it = rules.begin(); it != rules.end(); it++)
|
||||
{
|
||||
const SRule& rule = *it;
|
||||
json_array_append_new(arr, rule_to_json(rule));
|
||||
@ -870,11 +888,11 @@ void define_columns_rule(void* scanner)
|
||||
*
|
||||
* @param scanner Current scanner
|
||||
*/
|
||||
void define_function_rule(void* scanner)
|
||||
void define_function_rule(void* scanner, bool inverted)
|
||||
{
|
||||
struct parser_stack* rstack = (struct parser_stack*)dbfw_yyget_extra((yyscan_t) scanner);
|
||||
ss_dassert(rstack);
|
||||
rstack->add(new FunctionRule(rstack->name, rstack->values));
|
||||
rstack->add(new FunctionRule(rstack->name, rstack->values, inverted));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -894,11 +912,11 @@ void define_function_usage_rule(void* scanner)
|
||||
*
|
||||
* @param scanner Current scanner
|
||||
*/
|
||||
void define_column_function_rule(void* scanner)
|
||||
void define_column_function_rule(void* scanner, bool inverted)
|
||||
{
|
||||
struct parser_stack* rstack = (struct parser_stack*)dbfw_yyget_extra((yyscan_t) scanner);
|
||||
ss_dassert(rstack);
|
||||
rstack->add(new ColumnFunctionRule(rstack->name, rstack->values, rstack->auxiliary_values));
|
||||
rstack->add(new ColumnFunctionRule(rstack->name, rstack->values, rstack->auxiliary_values, inverted));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1092,11 +1110,11 @@ bool replace_rules(Dbfw* instance)
|
||||
|
||||
if (process_rule_file(filename, &rules, &users))
|
||||
{
|
||||
this_thread->rules.swap(rules);
|
||||
this_thread->users.swap(users);
|
||||
this_thread->rules(instance).swap(rules);
|
||||
this_thread->users(instance).swap(users);
|
||||
rval = true;
|
||||
}
|
||||
else if (!this_thread->rules.empty() && !this_thread->users.empty())
|
||||
else if (!this_thread->rules(instance).empty() && !this_thread->users(instance).empty())
|
||||
{
|
||||
MXS_ERROR("Failed to parse rules at '%s'. Old rules are still used.",
|
||||
filename.c_str());
|
||||
@ -1116,25 +1134,40 @@ static bool update_rules(Dbfw* my_instance)
|
||||
bool rval = true;
|
||||
int rule_version = my_instance->get_rule_version();
|
||||
|
||||
if (this_thread->rule_version < rule_version)
|
||||
if (this_thread->rule_version(my_instance) < rule_version)
|
||||
{
|
||||
if (!replace_rules(my_instance))
|
||||
{
|
||||
rval = false;
|
||||
}
|
||||
|
||||
this_thread->rule_version = rule_version;
|
||||
this_thread->rule_version(my_instance) = rule_version;
|
||||
}
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/**
|
||||
* Global rule version. Every time a Dbfw instance is created, its rule version will
|
||||
* be the value of this variable, which is then incremented by one.
|
||||
*
|
||||
* This is to ensure that each created Dbfw instance will have a unique set of rules,
|
||||
* irrespective of whether a new instance is created in exactly the same memory location
|
||||
* where an earlier, now deleted instance existed.
|
||||
*/
|
||||
int global_version = 1;
|
||||
|
||||
};
|
||||
|
||||
Dbfw::Dbfw(MXS_CONFIG_PARAMETER* params):
|
||||
m_action((enum fw_actions)config_get_enum(params, "action", action_values)),
|
||||
m_log_match(0),
|
||||
m_lock(SPINLOCK_INIT),
|
||||
m_filename(config_get_string(params, "rules")),
|
||||
m_version(1)
|
||||
m_version(atomic_add(&global_version, 1))
|
||||
{
|
||||
if (config_get_bool(params, "log_match"))
|
||||
{
|
||||
@ -1254,11 +1287,16 @@ static SUser find_user_data(const UserMap& users, std::string name, std::string
|
||||
if (it == users.end())
|
||||
{
|
||||
snprintf(nameaddr, sizeof(nameaddr), "%%@%s", remote.c_str());
|
||||
ip_start = strchr(nameaddr, '@') + 1;
|
||||
it = users.find(nameaddr);
|
||||
|
||||
while (it == users.end() && next_ip_class(ip_start))
|
||||
if (it == users.end())
|
||||
{
|
||||
it = users.find(nameaddr);
|
||||
ip_start = strchr(nameaddr, '@') + 1;
|
||||
|
||||
while (it == users.end() && next_ip_class(ip_start))
|
||||
{
|
||||
it = users.find(nameaddr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1409,7 +1447,7 @@ int DbfwSession::routeQuery(GWBUF* buffer)
|
||||
ss_dassert(analyzed_queue);
|
||||
}
|
||||
|
||||
SUser suser = find_user_data(this_thread->users, user(), remote());
|
||||
SUser suser = find_user_data(this_thread->users(m_instance), user(), remote());
|
||||
bool query_ok = false;
|
||||
|
||||
if (command_is_mandatory(buffer))
|
||||
@ -1673,7 +1711,9 @@ void Dbfw::diagnostics(DCB *dcb) const
|
||||
dcb_printf(dcb, "Firewall Filter\n");
|
||||
dcb_printf(dcb, "Rule, Type, Times Matched\n");
|
||||
|
||||
for (RuleList::const_iterator it = this_thread->rules.begin(); it != this_thread->rules.end(); it++)
|
||||
RuleList& rules = this_thread->rules(this);
|
||||
|
||||
for (RuleList::const_iterator it = rules.begin(); it != rules.end(); it++)
|
||||
{
|
||||
const SRule& rule = *it;
|
||||
char buf[rule->name().length() + 200];
|
||||
@ -1694,5 +1734,5 @@ void Dbfw::diagnostics(DCB *dcb) const
|
||||
*/
|
||||
json_t* Dbfw::diagnostics_json() const
|
||||
{
|
||||
return rules_to_json(this_thread->rules);
|
||||
return rules_to_json(this_thread->rules(this));
|
||||
}
|
||||
|
@ -43,9 +43,9 @@ void define_wildcard_rule(void* scanner);
|
||||
void define_where_clause_rule(void* scanner);
|
||||
bool define_regex_rule(void* scanner, char* pattern);
|
||||
void define_columns_rule(void* scanner);
|
||||
void define_function_rule(void* scanner);
|
||||
void define_function_rule(void* scanner, bool inverted);
|
||||
void define_function_usage_rule(void* scanner);
|
||||
void define_column_function_rule(void* scanner);
|
||||
void define_column_function_rule(void* scanner, bool inverted);
|
||||
void define_limit_queries_rule(void* scanner, int max, int timeperiod, int holdoff);
|
||||
bool add_at_times_rule(void* scanner, const char* range);
|
||||
void add_on_queries_rule(void* scanner, const char* sql);
|
||||
|
@ -37,7 +37,7 @@
|
||||
%token FWTOK_RULE FWTOK_USERS FWTOK_RULES FWTOK_ANY FWTOK_ALL
|
||||
%token FWTOK_STRICT_ALL FWTOK_MATCH FWTOK_WILDCARD FWTOK_COLUMNS FWTOK_REGEX
|
||||
%token FWTOK_LIMIT_QUERIES FWTOK_WHERE_CLAUSE FWTOK_AT_TIMES FWTOK_ON_QUERIES
|
||||
%token FWTOK_FUNCTION FWTOK_USES_FUNCTION FWTOK_COMMENT FWTOK_PIPE
|
||||
%token FWTOK_FUNCTION FWTOK_USES_FUNCTION FWTOK_COMMENT FWTOK_PIPE FWTOK_NOT_FUNCTION
|
||||
|
||||
/** Terminal typed symbols */
|
||||
%token <floatval>FWTOK_FLOAT <strval>FWTOK_TIME <strval>FWTOK_BTSTR
|
||||
@ -124,9 +124,15 @@ mandatory
|
||||
{define_limit_queries_rule(scanner, $2, $3, $4);}
|
||||
| FWTOK_REGEX FWTOK_QUOTEDSTR {define_regex_rule(scanner, $2);}
|
||||
| FWTOK_COLUMNS valuelist {define_columns_rule(scanner);}
|
||||
| FWTOK_FUNCTION valuelist {define_function_rule(scanner);}
|
||||
| FWTOK_FUNCTION {define_function_rule(scanner);}
|
||||
| FWTOK_FUNCTION valuelist FWTOK_COLUMNS auxiliaryvaluelist {define_column_function_rule(scanner);}
|
||||
| FWTOK_FUNCTION valuelist {define_function_rule(scanner, false);}
|
||||
| FWTOK_NOT_FUNCTION valuelist {define_function_rule(scanner, true);}
|
||||
| FWTOK_NOT_FUNCTION {define_function_rule(scanner, true);}
|
||||
| FWTOK_FUNCTION valuelist FWTOK_COLUMNS auxiliaryvaluelist
|
||||
{define_column_function_rule(scanner, false);}
|
||||
| FWTOK_NOT_FUNCTION valuelist FWTOK_COLUMNS auxiliaryvaluelist
|
||||
{define_column_function_rule(scanner, true);}
|
||||
| FWTOK_NOT_FUNCTION FWTOK_COLUMNS auxiliaryvaluelist
|
||||
{define_column_function_rule(scanner, true);}
|
||||
| FWTOK_USES_FUNCTION valuelist {define_function_usage_rule(scanner);}
|
||||
;
|
||||
|
||||
|
@ -41,8 +41,11 @@ Rule::~Rule()
|
||||
|
||||
bool Rule::matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const
|
||||
{
|
||||
*msg = create_error("Permission denied at this time.");
|
||||
MXS_NOTICE("rule '%s': query denied at this time.", name().c_str());
|
||||
MXS_NOTICE("rule '%s': query matches at this time.", name().c_str());
|
||||
if (session->get_action() == FW_ACTION_BLOCK)
|
||||
{
|
||||
*msg = create_error("Permission denied at this time.");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -92,8 +95,11 @@ bool WildCardRule::matches_query(DbfwSession* session, GWBUF* buffer, char** msg
|
||||
if (strcmp(infos[i].column, "*") == 0)
|
||||
{
|
||||
MXS_NOTICE("rule '%s': query contains a wildcard.", name().c_str());
|
||||
if (session->get_action() == FW_ACTION_BLOCK)
|
||||
{
|
||||
*msg = create_error("Usage of wildcard denied.");
|
||||
}
|
||||
rval = true;
|
||||
*msg = create_error("Usage of wildcard denied.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -107,10 +113,12 @@ bool NoWhereClauseRule::matches_query(DbfwSession* session, GWBUF* buffer, char*
|
||||
|
||||
if (query_is_sql(buffer) && !qc_query_has_clause(buffer))
|
||||
{
|
||||
MXS_NOTICE("rule '%s': query has no where/having clause.", name().c_str());
|
||||
if (session->get_action() == FW_ACTION_BLOCK)
|
||||
{
|
||||
*msg = create_error("Required WHERE/HAVING clause is missing.");
|
||||
}
|
||||
rval = true;
|
||||
*msg = create_error("Required WHERE/HAVING clause is missing.");
|
||||
MXS_NOTICE("rule '%s': query has no where/having "
|
||||
"clause, query is denied.", name().c_str());
|
||||
}
|
||||
|
||||
return rval;
|
||||
@ -133,8 +141,11 @@ bool RegexRule::matches_query(DbfwSession* session, GWBUF* buffer, char** msg) c
|
||||
if (pcre2_match(re, (PCRE2_SPTR)sql, (size_t)len, 0, 0, mdata, NULL) > 0)
|
||||
{
|
||||
MXS_NOTICE("rule '%s': regex matched on query", name().c_str());
|
||||
if (session->get_action() == FW_ACTION_BLOCK)
|
||||
{
|
||||
*msg = create_error("Permission denied, query matched regular expression.");
|
||||
}
|
||||
rval = true;
|
||||
*msg = create_error("Permission denied, query matched regular expression.");
|
||||
}
|
||||
|
||||
pcre2_match_data_free(mdata);
|
||||
@ -161,9 +172,12 @@ bool ColumnsRule::matches_query(DbfwSession* session, GWBUF* buffer, char** msg)
|
||||
|
||||
if (it != m_values.end())
|
||||
{
|
||||
MXS_NOTICE("rule '%s': query targets forbidden column: %s",
|
||||
MXS_NOTICE("rule '%s': query targets specified column: %s",
|
||||
name().c_str(), tok.c_str());
|
||||
*msg = create_error("Permission denied to column '%s'.", tok.c_str());
|
||||
if (session->get_action() == FW_ACTION_BLOCK)
|
||||
{
|
||||
*msg = create_error("Permission denied to column '%s'.", tok.c_str());
|
||||
}
|
||||
rval = true;
|
||||
break;
|
||||
}
|
||||
@ -184,27 +198,23 @@ bool FunctionRule::matches_query(DbfwSession* session, GWBUF* buffer, char** msg
|
||||
size_t n_infos;
|
||||
qc_get_function_info(buffer, &infos, &n_infos);
|
||||
|
||||
if (n_infos == 0 && session->get_action() == FW_ACTION_ALLOW)
|
||||
for (size_t i = 0; i < n_infos; ++i)
|
||||
{
|
||||
rval = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < n_infos; ++i)
|
||||
std::string tok = infos[i].name;
|
||||
std::transform(tok.begin(), tok.end(), tok.begin(), ::tolower);
|
||||
ValueList::const_iterator it = std::find(m_values.begin(), m_values.end(), tok);
|
||||
|
||||
if ((!m_inverted && (it != m_values.end())) ||
|
||||
(m_inverted && (it == m_values.end())))
|
||||
{
|
||||
std::string tok = infos[i].name;
|
||||
std::transform(tok.begin(), tok.end(), tok.begin(), ::tolower);
|
||||
ValueList::const_iterator it = std::find(m_values.begin(), m_values.end(), tok);
|
||||
|
||||
if (it != m_values.end())
|
||||
MXS_NOTICE("rule '%s': query matches function: %s",
|
||||
name().c_str(), tok.c_str());
|
||||
if (session->get_action() == FW_ACTION_BLOCK)
|
||||
{
|
||||
MXS_NOTICE("rule '%s': query uses forbidden function: %s",
|
||||
name().c_str(), tok.c_str());
|
||||
*msg = create_error("Permission denied to function '%s'.", tok.c_str());
|
||||
rval = true;
|
||||
break;
|
||||
}
|
||||
|
||||
rval = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -230,9 +240,12 @@ bool FunctionUsageRule::matches_query(DbfwSession* session, GWBUF* buffer, char*
|
||||
|
||||
if (it != m_values.end())
|
||||
{
|
||||
MXS_NOTICE("rule '%s': query uses a function with forbidden column: %s",
|
||||
MXS_NOTICE("rule '%s': query uses a function with specified column: %s",
|
||||
name().c_str(), tok.c_str());
|
||||
*msg = create_error("Permission denied to column '%s' with function.", tok.c_str());
|
||||
if (session->get_action() == FW_ACTION_BLOCK)
|
||||
{
|
||||
*msg = create_error("Permission denied to column '%s' with function.", tok.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -259,7 +272,8 @@ bool ColumnFunctionRule::matches_query(DbfwSession* session, GWBUF* buffer, char
|
||||
m_values.end(),
|
||||
func);
|
||||
|
||||
if (func_it != m_values.end())
|
||||
if ((!m_inverted && (func_it != m_values.end())) ||
|
||||
(m_inverted && (func_it == m_values.end())))
|
||||
{
|
||||
/** The function matches, now check if the column matches */
|
||||
|
||||
@ -273,14 +287,16 @@ bool ColumnFunctionRule::matches_query(DbfwSession* session, GWBUF* buffer, char
|
||||
|
||||
if (col_it != m_columns.end())
|
||||
{
|
||||
MXS_NOTICE("rule '%s': query uses function '%s' with forbidden column: %s",
|
||||
MXS_NOTICE("rule '%s': query uses function '%s' with specified column: %s",
|
||||
name().c_str(), col.c_str(), func.c_str());
|
||||
*msg = create_error("Permission denied to column '%s' with function '%s'.",
|
||||
col.c_str(), func.c_str());
|
||||
if (session->get_action() == FW_ACTION_BLOCK)
|
||||
{
|
||||
*msg = create_error("Permission denied to column '%s' with function '%s'.",
|
||||
col.c_str(), func.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,12 +180,16 @@ class FunctionRule: public ValueListRule
|
||||
FunctionRule& operator=(const FunctionRule&);
|
||||
|
||||
public:
|
||||
FunctionRule(std::string name, const ValueList& values):
|
||||
ValueListRule(name, "FUNCTION", values)
|
||||
FunctionRule(std::string name, const ValueList& values, bool inverted):
|
||||
ValueListRule(name, inverted ? "NOT_FUNCTION" : "FUNCTION", values),
|
||||
m_inverted(inverted)
|
||||
{
|
||||
}
|
||||
|
||||
bool matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const;
|
||||
|
||||
private:
|
||||
bool m_inverted; /*< Should the match be inverted. */
|
||||
};
|
||||
|
||||
/**
|
||||
@ -214,16 +218,18 @@ class ColumnFunctionRule: public ValueListRule
|
||||
ColumnFunctionRule& operator=(const ColumnFunctionRule&);
|
||||
|
||||
public:
|
||||
ColumnFunctionRule(std::string name, const ValueList& values, const ValueList& columns):
|
||||
ValueListRule(name, "COLUMN_FUNCTION", values),
|
||||
m_columns(columns)
|
||||
ColumnFunctionRule(std::string name, const ValueList& values, const ValueList& columns, bool inverted):
|
||||
ValueListRule(name, inverted ? "NOT_COLUMN_FUNCTION" : "COLUMN_FUNCTION", values),
|
||||
m_columns(columns),
|
||||
m_inverted(inverted)
|
||||
{
|
||||
}
|
||||
|
||||
bool matches_query(DbfwSession* session, GWBUF* buffer, char** msg) const;
|
||||
|
||||
private:
|
||||
ValueList m_columns; /*< List of columns to match */
|
||||
ValueList m_columns; /*< List of columns to match */
|
||||
bool m_inverted; /*< Should the match be inverted. */
|
||||
};
|
||||
|
||||
/**
|
||||
|
19
server/modules/filter/dbfwfilter/test/CMakeLists.txt
Normal file
19
server/modules/filter/dbfwfilter/test/CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../test)
|
||||
|
||||
add_executable(test_dbfwfilter
|
||||
test_dbfwfilter.cc
|
||||
tempfile.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_dbfwfilter maxscale-common)
|
||||
|
||||
add_test(test_dbfwfilter test_dbfwfilter)
|
51
server/modules/filter/dbfwfilter/test/tempfile.cc
Normal file
51
server/modules/filter/dbfwfilter/test/tempfile.cc
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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 "tempfile.hh"
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <maxscale/debug.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static char NAME_TEMPLATE[] = "/tmp/XXXXXX";
|
||||
|
||||
}
|
||||
|
||||
TempFile::TempFile()
|
||||
: m_fd(-1)
|
||||
, m_name(NAME_TEMPLATE)
|
||||
{
|
||||
m_fd = mkstemp((char*)m_name.c_str());
|
||||
ss_dassert(m_fd != -1);
|
||||
}
|
||||
|
||||
TempFile::~TempFile()
|
||||
{
|
||||
int rc = unlink(m_name.c_str());
|
||||
ss_dassert(rc != -1);
|
||||
close(m_fd);
|
||||
}
|
||||
|
||||
void TempFile::write(const void* pData, size_t count)
|
||||
{
|
||||
int rc = ::write(m_fd, pData, count);
|
||||
ss_dassert(rc != -1);
|
||||
ss_dassert((size_t)rc == count);
|
||||
}
|
||||
|
||||
void TempFile::write(const char* zData)
|
||||
{
|
||||
write(zData, strlen(zData));
|
||||
}
|
69
server/modules/filter/dbfwfilter/test/tempfile.hh
Normal file
69
server/modules/filter/dbfwfilter/test/tempfile.hh
Normal file
@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 <string>
|
||||
|
||||
/**
|
||||
* TempFile is a class using which a temporary file can be created and
|
||||
* written to.
|
||||
*/
|
||||
class TempFile
|
||||
{
|
||||
TempFile(const TempFile&);
|
||||
TempFile& operator = (const TempFile&);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* Create temporary file in /tmp
|
||||
*/
|
||||
TempFile();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
* Close and unlink file.
|
||||
*/
|
||||
~TempFile();
|
||||
|
||||
/**
|
||||
* The name of the created temporary file.
|
||||
*
|
||||
* @return The name of the file.
|
||||
*/
|
||||
std::string name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write data to the file.
|
||||
*
|
||||
* @param pData Pointer to data.
|
||||
* @param count The number of bytes to write.
|
||||
*/
|
||||
void write(const void* pData, size_t count);
|
||||
|
||||
/**
|
||||
* Write data to the file.
|
||||
*
|
||||
* @param zData Null terminated buffer to write.
|
||||
*/
|
||||
void write(const char* zData);
|
||||
|
||||
private:
|
||||
int m_fd;
|
||||
std::string m_name;
|
||||
};
|
1024
server/modules/filter/dbfwfilter/test/test_dbfwfilter.cc
Normal file
1024
server/modules/filter/dbfwfilter/test/test_dbfwfilter.cc
Normal file
File diff suppressed because it is too large
Load Diff
@ -40,6 +40,7 @@ CMP [=<>!]+
|
||||
deny|allow MXS_WARNING("Use of '%s' is deprecated, use 'match' instead", yytext);return FWTOK_MATCH;
|
||||
rule return FWTOK_RULE;
|
||||
function return FWTOK_FUNCTION;
|
||||
not_function return FWTOK_NOT_FUNCTION;
|
||||
uses_function return FWTOK_USES_FUNCTION;
|
||||
no_where_clause return FWTOK_WHERE_CLAUSE;
|
||||
wildcard return FWTOK_WILDCARD;
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <maxscale/cppdefs.hh>
|
||||
|
||||
#include <cmath>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <time.h>
|
||||
@ -60,6 +61,7 @@ enum log_options
|
||||
LOG_DATA_DATE = (1 << 2),
|
||||
LOG_DATA_USER = (1 << 3),
|
||||
LOG_DATA_QUERY = (1 << 4),
|
||||
LOG_DATA_REPLY_TIME = (1 << 5),
|
||||
};
|
||||
|
||||
/* Default values for logged data */
|
||||
@ -71,7 +73,9 @@ static MXS_FILTER_SESSION *newSession(MXS_FILTER *instance, MXS_SESSION *session
|
||||
static void closeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session);
|
||||
static void freeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session);
|
||||
static void setDownstream(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, MXS_DOWNSTREAM *downstream);
|
||||
static void setUpstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_UPSTREAM *upstream);
|
||||
static int routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, GWBUF *queue);
|
||||
static int clientReply(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue);
|
||||
static void diagnostic(MXS_FILTER *instance, MXS_FILTER_SESSION *fsession, DCB *dcb);
|
||||
static json_t* diagnostic_json(const MXS_FILTER *instance, const MXS_FILTER_SESSION *fsession);
|
||||
static uint64_t getCapabilities(MXS_FILTER* instance);
|
||||
@ -108,10 +112,39 @@ typedef struct
|
||||
bool write_warning_given;
|
||||
} QLA_INSTANCE;
|
||||
|
||||
/**
|
||||
* Helper struct for holding data before it's written to file.
|
||||
*/
|
||||
struct LOG_EVENT_DATA
|
||||
{
|
||||
bool has_message; // Does message data exist?
|
||||
GWBUF* query_clone; // Clone of the query buffer.
|
||||
char query_date[QLA_DATE_BUFFER_SIZE]; // Text representation of date.
|
||||
timespec begin_time; // Timer value at the moment of receiving query.
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
/**
|
||||
* Resets event data. Since QLA_SESSION is allocated with calloc, separate initialisation is not needed.
|
||||
*
|
||||
* @param event Event to reset
|
||||
*/
|
||||
void clear(LOG_EVENT_DATA& event)
|
||||
{
|
||||
event.has_message = false;
|
||||
gwbuf_free(event.query_clone);
|
||||
event.query_clone = NULL;
|
||||
event.query_date[0] = '\0';
|
||||
event.begin_time = {0, 0};
|
||||
}
|
||||
}
|
||||
|
||||
/* The session structure for this QLA filter. */
|
||||
typedef struct
|
||||
{
|
||||
int active;
|
||||
MXS_UPSTREAM up;
|
||||
MXS_DOWNSTREAM down;
|
||||
char *filename; /* The session-specific log file name */
|
||||
FILE *fp; /* The session-specific log file */
|
||||
@ -120,11 +153,12 @@ typedef struct
|
||||
size_t ses_id; /* The session this filter serves */
|
||||
const char *user; /* The client */
|
||||
pcre2_match_data* match_data; /* Regex match data */
|
||||
LOG_EVENT_DATA event_data; /* Information about the latest event, required if logging execution time. */
|
||||
} QLA_SESSION;
|
||||
|
||||
static FILE* open_log_file(uint32_t, QLA_INSTANCE *, const char *);
|
||||
static int write_log_entry(uint32_t, FILE*, QLA_INSTANCE*, QLA_SESSION*, const char*,
|
||||
const char*, size_t);
|
||||
static int write_log_entry(uint32_t, FILE*, QLA_INSTANCE*, QLA_SESSION*,
|
||||
const char*, const char*, size_t, int);
|
||||
static bool cb_log(const MODULECMD_ARG *argv, json_t** output);
|
||||
|
||||
static const MXS_ENUM_VALUE option_values[] =
|
||||
@ -144,11 +178,12 @@ static const MXS_ENUM_VALUE log_type_values[] =
|
||||
|
||||
static const MXS_ENUM_VALUE log_data_values[] =
|
||||
{
|
||||
{"service", LOG_DATA_SERVICE},
|
||||
{"session", LOG_DATA_SESSION},
|
||||
{"date", LOG_DATA_DATE},
|
||||
{"user", LOG_DATA_USER},
|
||||
{"query", LOG_DATA_QUERY},
|
||||
{"service", LOG_DATA_SERVICE},
|
||||
{"session", LOG_DATA_SESSION},
|
||||
{"date", LOG_DATA_DATE},
|
||||
{"user", LOG_DATA_USER},
|
||||
{"query", LOG_DATA_QUERY},
|
||||
{"reply_time", LOG_DATA_REPLY_TIME},
|
||||
{NULL}
|
||||
};
|
||||
|
||||
@ -199,9 +234,9 @@ MXS_MODULE* MXS_CREATE_MODULE()
|
||||
closeSession,
|
||||
freeSession,
|
||||
setDownstream,
|
||||
NULL, // No Upstream requirement
|
||||
setUpstream,
|
||||
routeQuery,
|
||||
NULL, // No client reply
|
||||
clientReply,
|
||||
diagnostic,
|
||||
diagnostic_json,
|
||||
getCapabilities,
|
||||
@ -483,6 +518,7 @@ closeSession(MXS_FILTER *instance, MXS_FILTER_SESSION *session)
|
||||
{
|
||||
fclose(my_session->fp);
|
||||
}
|
||||
clear(my_session->event_data);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -518,6 +554,65 @@ setDownstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_DOWNSTREAM
|
||||
my_session->down = *downstream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upstream filter or router to which queries will be
|
||||
* passed from this filter.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The filter session
|
||||
* @param upstream The upstream filter or router.
|
||||
*/
|
||||
static void
|
||||
setUpstream(MXS_FILTER *instance, MXS_FILTER_SESSION *session, MXS_UPSTREAM *upstream)
|
||||
{
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||
my_session->up = *upstream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write QLA log entry/entries to disk
|
||||
*
|
||||
* @param my_instance Filter instance
|
||||
* @param my_session Filter session
|
||||
* @param query Query string, not 0-terminated
|
||||
* @param querylen Query string length
|
||||
* @param date_string Date string
|
||||
* @param elapsed_ms Query execution time, in milliseconds
|
||||
*/
|
||||
void write_log_entries(QLA_INSTANCE* my_instance, QLA_SESSION* my_session,
|
||||
const char* query, int querylen, const char* date_string, int elapsed_ms)
|
||||
{
|
||||
bool write_error = false;
|
||||
if (my_instance->log_mode_flags & CONFIG_FILE_SESSION)
|
||||
{
|
||||
// In this case there is no need to write the session
|
||||
// number into the files.
|
||||
uint32_t data_flags = (my_instance->log_file_data_flags &
|
||||
~LOG_DATA_SESSION);
|
||||
if (write_log_entry(data_flags, my_session->fp, my_instance, my_session,
|
||||
date_string, query, querylen, elapsed_ms) < 0)
|
||||
{
|
||||
write_error = true;
|
||||
}
|
||||
}
|
||||
if (my_instance->log_mode_flags & CONFIG_FILE_UNIFIED)
|
||||
{
|
||||
uint32_t data_flags = my_instance->log_file_data_flags;
|
||||
if (write_log_entry(data_flags, my_instance->unified_fp, my_instance, my_session,
|
||||
date_string, query, querylen, elapsed_ms) < 0)
|
||||
{
|
||||
write_error = true;
|
||||
}
|
||||
}
|
||||
if (write_error && !my_instance->write_warning_given)
|
||||
{
|
||||
MXS_ERROR("qla-filter '%s': Log file write failed. "
|
||||
"Suppressing further similar warnings.",
|
||||
my_instance->name);
|
||||
my_instance->write_warning_given = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The routeQuery entry point. This is passed the query buffer
|
||||
* to which the filter should be applied. Once applied the
|
||||
@ -533,56 +628,46 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
{
|
||||
QLA_INSTANCE *my_instance = (QLA_INSTANCE *) instance;
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||
char *ptr = NULL;
|
||||
int length = 0;
|
||||
struct tm t;
|
||||
struct timeval tv;
|
||||
char *query = NULL;
|
||||
int query_len = 0;
|
||||
|
||||
if (my_session->active &&
|
||||
modutil_extract_SQL(queue, &ptr, &length) &&
|
||||
modutil_extract_SQL(queue, &query, &query_len) &&
|
||||
mxs_pcre2_check_match_exclude(my_instance->re_match, my_instance->re_exclude,
|
||||
my_session->match_data, ptr, length, MXS_MODULE_NAME))
|
||||
my_session->match_data, query, query_len, MXS_MODULE_NAME))
|
||||
{
|
||||
char buffer[QLA_DATE_BUFFER_SIZE];
|
||||
gettimeofday(&tv, NULL);
|
||||
localtime_r(&tv.tv_sec, &t);
|
||||
strftime(buffer, sizeof(buffer), "%F %T", &t);
|
||||
|
||||
/**
|
||||
* Loop over all the possible log file modes and write to
|
||||
* the enabled files.
|
||||
*/
|
||||
|
||||
char *sql_string = ptr;
|
||||
bool write_error = false;
|
||||
if (my_instance->log_mode_flags & CONFIG_FILE_SESSION)
|
||||
const uint32_t data_flags = my_instance->log_file_data_flags;
|
||||
LOG_EVENT_DATA& event = my_session->event_data;
|
||||
if (data_flags & LOG_DATA_DATE)
|
||||
{
|
||||
// In this case there is no need to write the session
|
||||
// number into the files.
|
||||
uint32_t data_flags = (my_instance->log_file_data_flags &
|
||||
~LOG_DATA_SESSION);
|
||||
|
||||
if (write_log_entry(data_flags, my_session->fp,
|
||||
my_instance, my_session, buffer, sql_string, length) < 0)
|
||||
{
|
||||
write_error = true;
|
||||
}
|
||||
// Print current date to a buffer. Use the buffer in the event data struct even if execution time
|
||||
// is not needed.
|
||||
const time_t utc_seconds = time(NULL);
|
||||
tm local_time;
|
||||
localtime_r(&utc_seconds, &local_time);
|
||||
strftime(event.query_date, QLA_DATE_BUFFER_SIZE, "%F %T", &local_time);
|
||||
}
|
||||
if (my_instance->log_mode_flags & CONFIG_FILE_UNIFIED)
|
||||
|
||||
if (data_flags & LOG_DATA_REPLY_TIME)
|
||||
{
|
||||
uint32_t data_flags = my_instance->log_file_data_flags;
|
||||
if (write_log_entry(data_flags, my_instance->unified_fp,
|
||||
my_instance, my_session, buffer, sql_string, length) < 0)
|
||||
// Have to measure reply time from server. Save query data for printing during clientReply.
|
||||
// If old event data exists, it is erased. This only happens if client sends a query before
|
||||
// receiving reply to previous query.
|
||||
if (event.has_message)
|
||||
{
|
||||
write_error = true;
|
||||
clear(event);
|
||||
}
|
||||
clock_gettime(CLOCK_MONOTONIC, &event.begin_time);
|
||||
if (data_flags & LOG_DATA_QUERY)
|
||||
{
|
||||
event.query_clone = gwbuf_clone(queue);
|
||||
}
|
||||
event.has_message = true;
|
||||
}
|
||||
if (write_error && !my_instance->write_warning_given)
|
||||
else
|
||||
{
|
||||
MXS_ERROR("qla-filter '%s': Log file write failed. "
|
||||
"Suppressing further similar warnings.",
|
||||
my_instance->name);
|
||||
my_instance->write_warning_given = true;
|
||||
// If execution times are not logged, write the log entry now.
|
||||
write_log_entries(my_instance, my_session, query, query_len, event.query_date, -1);
|
||||
}
|
||||
}
|
||||
/* Pass the query downstream */
|
||||
@ -590,6 +675,41 @@ routeQuery(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
my_session->down.session, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* The clientReply entry point. Required for measuring and printing query execution time.
|
||||
*
|
||||
* @param instance The filter instance data
|
||||
* @param session The filter session
|
||||
* @param queue The query data
|
||||
*/
|
||||
static int
|
||||
clientReply(MXS_FILTER *instance, MXS_FILTER_SESSION *session, GWBUF *queue)
|
||||
{
|
||||
QLA_INSTANCE *my_instance = (QLA_INSTANCE *) instance;
|
||||
QLA_SESSION *my_session = (QLA_SESSION *) session;
|
||||
LOG_EVENT_DATA& event = my_session->event_data;
|
||||
if (event.has_message)
|
||||
{
|
||||
const uint32_t data_flags = my_instance->log_file_data_flags;
|
||||
ss_dassert(data_flags & LOG_DATA_REPLY_TIME);
|
||||
char* query = NULL;
|
||||
int query_len = 0;
|
||||
if (data_flags & LOG_DATA_QUERY)
|
||||
{
|
||||
modutil_extract_SQL(event.query_clone, &query, &query_len);
|
||||
}
|
||||
timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now); // Gives time in seconds + nanoseconds
|
||||
// Calculate elapsed time in milliseconds.
|
||||
double elapsed_ms = 1E3 * (now.tv_sec - event.begin_time.tv_sec) +
|
||||
(now.tv_nsec - event.begin_time.tv_nsec) / (double)1E6;
|
||||
write_log_entries(my_instance, my_session, query, query_len, event.query_date,
|
||||
std::floor(elapsed_ms + 0.5));
|
||||
clear(event);
|
||||
}
|
||||
return my_session->up.clientReply(my_session->up.instance, my_session->up.session, queue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Diagnostics routine
|
||||
*
|
||||
@ -731,9 +851,9 @@ static FILE* open_log_file(uint32_t data_flags, QLA_INSTANCE *instance, const ch
|
||||
const char DATE[] = "Date,";
|
||||
const char USERHOST[] = "User@Host,";
|
||||
const char QUERY[] = "Query,";
|
||||
|
||||
const char REPLY_TIME[] = "Reply_time,";
|
||||
const int headerlen = sizeof(SERVICE) + sizeof(SERVICE) + sizeof(DATE) +
|
||||
sizeof(USERHOST) + sizeof(QUERY);
|
||||
sizeof(USERHOST) + sizeof(QUERY) + sizeof(REPLY_TIME);
|
||||
|
||||
char print_str[headerlen];
|
||||
memset(print_str, '\0', headerlen);
|
||||
@ -759,6 +879,11 @@ static FILE* open_log_file(uint32_t data_flags, QLA_INSTANCE *instance, const ch
|
||||
strcat(current_pos, USERHOST);
|
||||
current_pos += sizeof(USERHOST) - 1;
|
||||
}
|
||||
if (instance->log_file_data_flags & LOG_DATA_REPLY_TIME)
|
||||
{
|
||||
strcat(current_pos, REPLY_TIME);
|
||||
current_pos += sizeof(REPLY_TIME) - 1;
|
||||
}
|
||||
if (instance->log_file_data_flags & LOG_DATA_QUERY)
|
||||
{
|
||||
strcat(current_pos, QUERY);
|
||||
@ -800,11 +925,12 @@ static FILE* open_log_file(uint32_t data_flags, QLA_INSTANCE *instance, const ch
|
||||
* @param time_string Date entry
|
||||
* @param sql_string SQL-query, *not* NULL terminated
|
||||
* @param sql_str_len Length of SQL-string
|
||||
* @param elapsed_ms Query execution time, in milliseconds
|
||||
* @return The number of characters written, or a negative value on failure
|
||||
*/
|
||||
static int write_log_entry(uint32_t data_flags, FILE *logfile, QLA_INSTANCE *instance,
|
||||
QLA_SESSION *session, const char *time_string, const char *sql_string,
|
||||
size_t sql_str_len)
|
||||
size_t sql_str_len, int elapsed_ms)
|
||||
{
|
||||
ss_dassert(logfile != NULL);
|
||||
size_t print_len = 0;
|
||||
@ -816,13 +942,14 @@ static int write_log_entry(uint32_t data_flags, FILE *logfile, QLA_INSTANCE *ins
|
||||
*/
|
||||
|
||||
// The numbers have some extra for delimiters.
|
||||
const size_t integer_chars = 20; // Enough space for any integer type
|
||||
if (data_flags & LOG_DATA_SERVICE)
|
||||
{
|
||||
print_len += strlen(session->service) + 1;
|
||||
}
|
||||
if (data_flags & LOG_DATA_SESSION)
|
||||
{
|
||||
print_len += 20; // To print a 64bit integer
|
||||
print_len += integer_chars;
|
||||
}
|
||||
if (data_flags & LOG_DATA_DATE)
|
||||
{
|
||||
@ -832,6 +959,10 @@ static int write_log_entry(uint32_t data_flags, FILE *logfile, QLA_INSTANCE *ins
|
||||
{
|
||||
print_len += strlen(session->user) + strlen(session->remote) + 2;
|
||||
}
|
||||
if (data_flags & LOG_DATA_REPLY_TIME)
|
||||
{
|
||||
print_len += integer_chars;
|
||||
}
|
||||
if (data_flags & LOG_DATA_QUERY)
|
||||
{
|
||||
print_len += sql_str_len + 1; // Can't use strlen, not null-terminated
|
||||
@ -898,6 +1029,17 @@ static int write_log_entry(uint32_t data_flags, FILE *logfile, QLA_INSTANCE *ins
|
||||
current_pos += rval;
|
||||
}
|
||||
}
|
||||
if (!error && (data_flags & LOG_DATA_REPLY_TIME))
|
||||
{
|
||||
if ((rval = sprintf(current_pos, "%d,", elapsed_ms)) < 0)
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
current_pos += rval;
|
||||
}
|
||||
}
|
||||
if (!error && (data_flags & LOG_DATA_QUERY))
|
||||
{
|
||||
strncat(current_pos, sql_string, sql_str_len); // non-null-terminated string
|
||||
|
89
server/modules/filter/test/filtermodule.cc
Normal file
89
server/modules/filter/test/filtermodule.cc
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 <maxscale/cppdefs.hh>
|
||||
#include "maxscale/filtermodule.hh"
|
||||
#include "../../../core/internal/modules.h"
|
||||
|
||||
using std::auto_ptr;
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
//
|
||||
// FilterModule
|
||||
//
|
||||
|
||||
const char* FilterModule::zName = MODULE_FILTER;
|
||||
|
||||
auto_ptr<FilterModule::Instance> FilterModule::createInstance(const char* zName,
|
||||
char** pzOptions,
|
||||
MXS_CONFIG_PARAMETER* pParameters)
|
||||
{
|
||||
auto_ptr<Instance> sInstance;
|
||||
|
||||
MXS_FILTER* pFilter = m_pApi->createInstance(zName, pzOptions, pParameters);
|
||||
|
||||
if (pFilter)
|
||||
{
|
||||
sInstance.reset(new Instance(this, pFilter));
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
//
|
||||
// FilterModule::Instance
|
||||
//
|
||||
|
||||
FilterModule::Instance::Instance(FilterModule* pModule, MXS_FILTER* pInstance)
|
||||
: m_module(*pModule)
|
||||
, m_pInstance(pInstance)
|
||||
{
|
||||
}
|
||||
|
||||
FilterModule::Instance::~Instance()
|
||||
{
|
||||
m_module.destroyInstance(m_pInstance);
|
||||
}
|
||||
|
||||
auto_ptr<FilterModule::Session> FilterModule::Instance::newSession(MXS_SESSION* pSession)
|
||||
{
|
||||
auto_ptr<Session> sFilter_session;
|
||||
|
||||
MXS_FILTER_SESSION* pFilter_session = m_module.newSession(m_pInstance, pSession);
|
||||
|
||||
if (pFilter_session)
|
||||
{
|
||||
sFilter_session.reset(new Session(this, pFilter_session));
|
||||
}
|
||||
|
||||
return sFilter_session;
|
||||
}
|
||||
|
||||
//
|
||||
// FilterModule::Session
|
||||
//
|
||||
|
||||
FilterModule::Session::Session(Instance* pInstance, MXS_FILTER_SESSION* pFilter_session)
|
||||
: m_instance(*pInstance)
|
||||
, m_pFilter_session(pFilter_session)
|
||||
{
|
||||
}
|
||||
|
||||
FilterModule::Session::~Session()
|
||||
{
|
||||
m_instance.freeSession(m_pFilter_session);
|
||||
}
|
||||
|
||||
}
|
198
server/modules/filter/test/maxscale/filtermodule.hh
Normal file
198
server/modules/filter/test/maxscale/filtermodule.hh
Normal file
@ -0,0 +1,198 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 <maxscale/cppdefs.hh>
|
||||
#include <memory>
|
||||
#include <maxscale/filter.hh>
|
||||
#include "module.hh"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
/**
|
||||
* An instance of FilterModule represents a filter module.
|
||||
*/
|
||||
class FilterModule : public SpecificModule<FilterModule>
|
||||
{
|
||||
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. */
|
||||
|
||||
class Session;
|
||||
class Instance
|
||||
{
|
||||
Instance(const Instance&);
|
||||
Instance& operator = (const Instance&);
|
||||
public:
|
||||
~Instance();
|
||||
|
||||
/**
|
||||
* Create a new filter session.
|
||||
*
|
||||
* @param pSession The session to which the filter session belongs.
|
||||
*
|
||||
* @return A new filter session or NULL if the creation failed.
|
||||
*/
|
||||
std::auto_ptr<Session> newSession(MXS_SESSION* pSession);
|
||||
|
||||
private:
|
||||
friend class FilterModule;
|
||||
|
||||
Instance(FilterModule* pModule, MXS_FILTER* pInstance);
|
||||
|
||||
private:
|
||||
friend class Session;
|
||||
|
||||
void freeSession(MXS_FILTER_SESSION* pFilter_session)
|
||||
{
|
||||
m_module.freeSession(m_pInstance, pFilter_session);
|
||||
}
|
||||
|
||||
int routeQuery(MXS_FILTER_SESSION* pFilter_session, GWBUF* pStatement)
|
||||
{
|
||||
return m_module.routeQuery(m_pInstance, pFilter_session, pStatement);
|
||||
}
|
||||
|
||||
int clientReply(MXS_FILTER_SESSION* pFilter_session, GWBUF* pStatement)
|
||||
{
|
||||
return m_module.clientReply(m_pInstance, pFilter_session, pStatement);
|
||||
}
|
||||
|
||||
void setDownstream(MXS_FILTER_SESSION* pFilter_session, MXS_DOWNSTREAM* pDownstream)
|
||||
{
|
||||
m_module.setDownstream(m_pInstance, pFilter_session, pDownstream);
|
||||
}
|
||||
|
||||
void setUpstream(MXS_FILTER_SESSION* pFilter_session, MXS_UPSTREAM* pUpstream)
|
||||
{
|
||||
m_module.setUpstream(m_pInstance, pFilter_session, pUpstream);
|
||||
}
|
||||
|
||||
private:
|
||||
FilterModule& m_module;
|
||||
MXS_FILTER* m_pInstance;
|
||||
};
|
||||
|
||||
class Session
|
||||
{
|
||||
Session(const Session&);
|
||||
Session& operator = (const Session&);
|
||||
|
||||
public:
|
||||
~Session();
|
||||
|
||||
/**
|
||||
* The following member functions correspond to the MaxScale filter API.
|
||||
*/
|
||||
int routeQuery(GWBUF* pStatement)
|
||||
{
|
||||
return m_instance.routeQuery(m_pFilter_session, pStatement);
|
||||
}
|
||||
|
||||
int clientReply(GWBUF* pBuffer)
|
||||
{
|
||||
return m_instance.clientReply(m_pFilter_session, pBuffer);
|
||||
}
|
||||
|
||||
void setDownstream(MXS_DOWNSTREAM* pDownstream)
|
||||
{
|
||||
m_instance.setDownstream(m_pFilter_session, pDownstream);
|
||||
}
|
||||
|
||||
void setUpstream(MXS_UPSTREAM* pUpstream)
|
||||
{
|
||||
m_instance.setUpstream(m_pFilter_session, pUpstream);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class Instance;
|
||||
|
||||
Session(Instance* pInstance, MXS_FILTER_SESSION* pFilter_session);
|
||||
|
||||
private:
|
||||
Instance& m_instance;
|
||||
MXS_FILTER_SESSION* m_pFilter_session;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a new instance.
|
||||
*
|
||||
* @param zName The name of the instance (config file section name),
|
||||
* @param pzOptions Optional options.
|
||||
* @param pParameters Configuration parameters.
|
||||
*
|
||||
* @return A new instance or NULL if creation failed.
|
||||
*/
|
||||
std::auto_ptr<Instance> createInstance(const char* zName,
|
||||
char** pzOptions,
|
||||
MXS_CONFIG_PARAMETER* pParameters);
|
||||
|
||||
private:
|
||||
friend class Instance;
|
||||
|
||||
void destroyInstance(MXS_FILTER* pInstance)
|
||||
{
|
||||
m_pApi->destroyInstance(pInstance);
|
||||
}
|
||||
|
||||
MXS_FILTER_SESSION* newSession(MXS_FILTER* pInstance, MXS_SESSION* pSession)
|
||||
{
|
||||
return m_pApi->newSession(pInstance, pSession);
|
||||
}
|
||||
|
||||
void freeSession(MXS_FILTER* pInstance, MXS_FILTER_SESSION* pFilter_session)
|
||||
{
|
||||
m_pApi->freeSession(pInstance, pFilter_session);
|
||||
}
|
||||
|
||||
int routeQuery(MXS_FILTER* pInstance, MXS_FILTER_SESSION* pFilter_session, GWBUF* pStatement)
|
||||
{
|
||||
return m_pApi->routeQuery(pInstance, pFilter_session, pStatement);
|
||||
}
|
||||
|
||||
int clientReply(MXS_FILTER* pInstance, MXS_FILTER_SESSION* pFilter_session, GWBUF* pStatement)
|
||||
{
|
||||
return m_pApi->clientReply(pInstance, pFilter_session, pStatement);
|
||||
}
|
||||
|
||||
void setDownstream(MXS_FILTER* pInstance,
|
||||
MXS_FILTER_SESSION* pFilter_session,
|
||||
MXS_DOWNSTREAM* pDownstream)
|
||||
{
|
||||
m_pApi->setDownstream(pInstance, pFilter_session, pDownstream);
|
||||
}
|
||||
|
||||
void setUpstream(MXS_FILTER* pInstance,
|
||||
MXS_FILTER_SESSION* pFilter_session,
|
||||
MXS_UPSTREAM* pUpstream)
|
||||
{
|
||||
m_pApi->setUpstream(pInstance, pFilter_session, pUpstream);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class SpecificModule<FilterModule>;
|
||||
|
||||
FilterModule(MXS_FILTER_OBJECT* pApi)
|
||||
: m_pApi(pApi)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
MXS_FILTER_OBJECT* m_pApi;
|
||||
};
|
||||
|
||||
}
|
140
server/modules/filter/test/maxscale/mock/backend.hh
Normal file
140
server/modules/filter/test/maxscale/mock/backend.hh
Normal file
@ -0,0 +1,140 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 <maxscale/cppdefs.hh>
|
||||
#include <map>
|
||||
#include "routersession.hh"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
/**
|
||||
* The abstract class Backend represents a backend.
|
||||
*/
|
||||
class Backend
|
||||
{
|
||||
Backend(const Backend&);
|
||||
Backend& operator = (const Backend&);
|
||||
|
||||
public:
|
||||
virtual ~Backend();
|
||||
|
||||
/**
|
||||
* Called to handle a statement from a "client".
|
||||
*
|
||||
* @param pSession The originating router session.
|
||||
* @param pStatement A buffer containing a statement.
|
||||
*/
|
||||
virtual void handle_statement(RouterSession* pSession, GWBUF* pStatement) = 0;
|
||||
|
||||
/**
|
||||
* Called when the backend should respond to the client.
|
||||
*
|
||||
* @param pSession The router session to respond to.
|
||||
*
|
||||
* @return True, if the backend has additional responses to the router session.
|
||||
*/
|
||||
virtual bool respond(RouterSession* pSession) = 0;
|
||||
|
||||
/**
|
||||
* Whether the backend has a response for some router.
|
||||
*
|
||||
* @param pSession A router session.
|
||||
*
|
||||
* @return True if there are responses for the router session.
|
||||
*/
|
||||
virtual bool idle(const RouterSession* pSession) const = 0;
|
||||
|
||||
/**
|
||||
* Discards an available response.
|
||||
*
|
||||
* @param pSession A router session.
|
||||
*
|
||||
* @return True if there are additional responses for the router session.
|
||||
*/
|
||||
virtual bool discard_one_response(const RouterSession* pSession) = 0;
|
||||
|
||||
/**
|
||||
* Discards all available responses.
|
||||
*
|
||||
* @param pSession A router session.
|
||||
*/
|
||||
virtual void discard_all_responses(const RouterSession* pSession) = 0;
|
||||
|
||||
protected:
|
||||
Backend();
|
||||
};
|
||||
|
||||
/**
|
||||
* The abstract class BufferBackend is a helper class for concrete
|
||||
* backend classes.
|
||||
*/
|
||||
class BufferBackend : public Backend
|
||||
{
|
||||
BufferBackend(const BufferBackend&);
|
||||
BufferBackend& operator = (const BufferBackend&);
|
||||
|
||||
public:
|
||||
~BufferBackend();
|
||||
|
||||
bool respond(RouterSession* pSession);
|
||||
|
||||
bool idle(const RouterSession* pSession) const;
|
||||
|
||||
bool discard_one_response(const RouterSession* pSession);
|
||||
|
||||
void discard_all_responses(const RouterSession* pSession);
|
||||
|
||||
protected:
|
||||
BufferBackend();
|
||||
|
||||
/**
|
||||
* Enqueues a response for a particular router session.
|
||||
*
|
||||
* @param pSession The session to enqueue the response for.
|
||||
* @param pResponse The response.
|
||||
*/
|
||||
void enqueue_response(const RouterSession* pSession, GWBUF* pResponse);
|
||||
|
||||
private:
|
||||
GWBUF* dequeue_response(const RouterSession* pSession, bool* pEmpty);
|
||||
|
||||
private:
|
||||
typedef std::deque<GWBUF*> Responses;
|
||||
typedef std::map<const RouterSession*, Responses> SessionResponses;
|
||||
|
||||
SessionResponses m_session_responses;
|
||||
};
|
||||
|
||||
/**
|
||||
* The OkBackend is a concrete backend class that response with an
|
||||
* OK packet to all statements.
|
||||
*/
|
||||
class OkBackend : public BufferBackend
|
||||
{
|
||||
OkBackend(const OkBackend&);
|
||||
OkBackend& operator = (const OkBackend&);
|
||||
|
||||
public:
|
||||
OkBackend();
|
||||
|
||||
void handle_statement(RouterSession* pSession, GWBUF* pStatement);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
141
server/modules/filter/test/maxscale/mock/client.hh
Normal file
141
server/modules/filter/test/maxscale/mock/client.hh
Normal file
@ -0,0 +1,141 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 <maxscale/cppdefs.hh>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <maxscale/filter.h>
|
||||
#include "../filtermodule.hh"
|
||||
#include "dcb.hh"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
/**
|
||||
* An instance of Client represents a client. It can be used as the
|
||||
* upstream filter of another filter.
|
||||
*/
|
||||
class Client : public MXS_FILTER_SESSION
|
||||
, public Dcb::Handler
|
||||
{
|
||||
Client(const Client&);
|
||||
Client& operator = (const Client&);
|
||||
|
||||
public:
|
||||
/**
|
||||
* A Handler can be used for processing responses.
|
||||
*/
|
||||
class Handler
|
||||
{
|
||||
public:
|
||||
virtual ~Handler();
|
||||
|
||||
/**
|
||||
* Called when a response is received from the backend.
|
||||
*
|
||||
* @param pResponse The response packet.
|
||||
*
|
||||
* @return 1 if processing should continue, 0 otherwise.
|
||||
*/
|
||||
virtual int32_t backend_reply(GWBUF* pResponse) = 0;
|
||||
|
||||
/**
|
||||
* Called when a response is sent directly by a filter.
|
||||
*
|
||||
* @param pResponse The response packet.
|
||||
*
|
||||
* @return 1 if processing should continue, 0 otherwise.
|
||||
*/
|
||||
virtual int32_t maxscale_reply(GWBUF* pResponse) = 0;
|
||||
|
||||
/**
|
||||
* Called when @reset is called on the @c Client instance.
|
||||
*/
|
||||
virtual void reset();
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param zUser The client of the session,
|
||||
* @param zHost The host of the client.
|
||||
* @param pHandler Optional response handler.
|
||||
*/
|
||||
Client(const char* zUser,
|
||||
const char* zHost,
|
||||
Handler* pHandler = NULL);
|
||||
~Client();
|
||||
|
||||
/**
|
||||
* @return The name of the client.
|
||||
*/
|
||||
const char* user() const;
|
||||
|
||||
/**
|
||||
* @return The name of the host.
|
||||
*/
|
||||
const char* host() const;
|
||||
|
||||
/**
|
||||
* Set a response handler
|
||||
*
|
||||
* @param pHandler The new response handler.
|
||||
*
|
||||
* @return The previous response handler.
|
||||
*/
|
||||
Handler* set_handler(Handler* pHandler);
|
||||
|
||||
/**
|
||||
* How many responses have been handled.
|
||||
*
|
||||
* @return The number of responses since last call to @c reset.
|
||||
*/
|
||||
size_t n_responses() const;
|
||||
|
||||
/**
|
||||
* Reset the Client object. The number of counted responsed will
|
||||
* be set to 0. If the Client object has a handler, then its @c reset
|
||||
* function will be called as well.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Set this object as client filter of provided filter.
|
||||
*
|
||||
* @param session The filter session whose upstream filter should be set.
|
||||
*/
|
||||
void set_as_upstream_on(FilterModule::Session& session);
|
||||
|
||||
private:
|
||||
int32_t clientReply(GWBUF* pResponse);
|
||||
|
||||
static int32_t clientReply(MXS_FILTER* pInstance, MXS_FILTER_SESSION* pSession, GWBUF* pResponse);
|
||||
|
||||
// Dcb::Handler
|
||||
int32_t write(GWBUF* pBuffer);
|
||||
|
||||
private:
|
||||
MXS_FILTER m_instance;
|
||||
std::string m_user;
|
||||
std::string m_host;
|
||||
Handler* m_pHandler;
|
||||
size_t m_n_responses;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
83
server/modules/filter/test/maxscale/mock/dcb.hh
Normal file
83
server/modules/filter/test/maxscale/mock/dcb.hh
Normal file
@ -0,0 +1,83 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 "mock.hh"
|
||||
#include <maxscale/dcb.h>
|
||||
#include <maxscale/session.h>
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
/**
|
||||
* The class Dcb provides a mock DCB that can be used when testing.
|
||||
*/
|
||||
class Dcb : public DCB
|
||||
{
|
||||
Dcb(const Dcb&);
|
||||
Dcb& operator = (const Dcb&);
|
||||
|
||||
public:
|
||||
class Handler
|
||||
{
|
||||
public:
|
||||
virtual int32_t write(GWBUF* pBuffer) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param pSession The session object of the DCB.
|
||||
* @param zUser The client of the connection.
|
||||
* @param zHost The host of the connection.
|
||||
* @param pHandler Optional handler.
|
||||
*/
|
||||
Dcb(MXS_SESSION* pSession,
|
||||
const char* zUser,
|
||||
const char* zHost,
|
||||
Handler* pHandler = NULL);
|
||||
~Dcb();
|
||||
|
||||
/**
|
||||
* Get the current handler of the Dcb.
|
||||
*
|
||||
* @return A Handler or NULL.
|
||||
*/
|
||||
Handler* handler() const;
|
||||
|
||||
/**
|
||||
* Set the current handler of the Dcb.
|
||||
*
|
||||
* @param pHandler The new handler.
|
||||
*
|
||||
* @return The previous handler or NULL.
|
||||
*/
|
||||
Handler* set_handler(Handler* pHandler);
|
||||
|
||||
private:
|
||||
int32_t write(GWBUF* pData);
|
||||
|
||||
static int32_t write(DCB* pDcb, GWBUF* pData);
|
||||
|
||||
private:
|
||||
std::string m_user;
|
||||
std::string m_host;
|
||||
Handler* m_pHandler;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
54
server/modules/filter/test/maxscale/mock/mock.hh
Normal file
54
server/modules/filter/test/maxscale/mock/mock.hh
Normal file
@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 <maxscale/cppdefs.hh>
|
||||
#include <string>
|
||||
#include <string.h>
|
||||
#include <maxscale/buffer.h>
|
||||
#include <maxscale/modutil.h>
|
||||
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
/**
|
||||
* Create a COM_QUERY packet containing the provided statement.
|
||||
*
|
||||
* @param zStatement Null terminated string containing an SQL statement.
|
||||
*
|
||||
* @return A buffer containing a COM_QUERY packet.
|
||||
*/
|
||||
inline GWBUF* create_com_query(const char* zStatement)
|
||||
{
|
||||
return modutil_create_query(zStatement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a COM_QUERY packet containing the provided statement.
|
||||
*
|
||||
* @param statement String containing an SQL statement.
|
||||
*
|
||||
* @return A buffer containing a COM_QUERY packet.
|
||||
*/
|
||||
inline GWBUF* create_com_query(const std::string& statement)
|
||||
{
|
||||
return create_com_query(statement.c_str());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
106
server/modules/filter/test/maxscale/mock/routersession.hh
Normal file
106
server/modules/filter/test/maxscale/mock/routersession.hh
Normal file
@ -0,0 +1,106 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 <maxscale/cppdefs.hh>
|
||||
#include <memory>
|
||||
#include <deque>
|
||||
#include <maxscale/router.hh>
|
||||
#include "../filtermodule.hh"
|
||||
#include "mock.hh"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
class Backend;
|
||||
|
||||
/**
|
||||
* An instance of RouterSession is a router to which a filter forwards
|
||||
* data.
|
||||
*/
|
||||
class RouterSession : private MXS_ROUTER_SESSION
|
||||
{
|
||||
RouterSession(const RouterSession&);
|
||||
RouterSession& operator = (const RouterSession&);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param pBackend The backend associated with the router.
|
||||
*/
|
||||
RouterSession(Backend* pBackend);
|
||||
~RouterSession();
|
||||
|
||||
/**
|
||||
* Set the router as the downstream filter of a particular filter.
|
||||
* The filter will at the same time become the upstream filter of
|
||||
* this router.
|
||||
*
|
||||
* @param pFilter_session The filter to set this router as downstream
|
||||
* filter of.
|
||||
*/
|
||||
void set_as_downstream_on(FilterModule::Session* pFilter_session);
|
||||
|
||||
/**
|
||||
* Called by the backend to deliver a response.
|
||||
*
|
||||
* @return Whatever the upstream filter returns.
|
||||
*/
|
||||
int32_t clientReply(GWBUF* pResponse);
|
||||
|
||||
/**
|
||||
* Causes the router to make its associated backend deliver a response
|
||||
* to this router, which will then deliver it forward to its associated
|
||||
* upstream filter.
|
||||
*
|
||||
* @return True if there are additional responses to deliver.
|
||||
*/
|
||||
bool respond();
|
||||
|
||||
/**
|
||||
* Are there responses available.
|
||||
*
|
||||
* @return True, if there are no responses, false otherwise.
|
||||
*/
|
||||
bool idle() const;
|
||||
|
||||
/**
|
||||
* Discards one response.
|
||||
*
|
||||
* @return True, if there are additional responses.
|
||||
*/
|
||||
bool discard_one_response();
|
||||
|
||||
/**
|
||||
* Discards all responses.
|
||||
*/
|
||||
void discard_all_responses();
|
||||
|
||||
private:
|
||||
int32_t routeQuery(MXS_ROUTER* pInstance, GWBUF* pStatement);
|
||||
|
||||
static int32_t routeQuery(MXS_FILTER* pInstance, MXS_FILTER_SESSION* pRouter_session, GWBUF* pStatement);
|
||||
|
||||
private:
|
||||
MXS_ROUTER m_instance;
|
||||
Backend* m_pBackend;
|
||||
FilterModule::Session* m_pUpstream_filter_session;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
55
server/modules/filter/test/maxscale/mock/session.hh
Normal file
55
server/modules/filter/test/maxscale/mock/session.hh
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 "mock.hh"
|
||||
#include <maxscale/session.h>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
#include "client.hh"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
/**
|
||||
* The class Session provides a mock MXS_SESSION that can be used when
|
||||
* testing.
|
||||
*/
|
||||
class Session : public MXS_SESSION
|
||||
{
|
||||
Session(const Session&);
|
||||
Session& operator = (Session&);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param pClient The client of the session. Must remain valid for
|
||||
* the lifetime of the Session.
|
||||
*/
|
||||
Session(Client* pClient);
|
||||
~Session();
|
||||
|
||||
Client& client() const;
|
||||
|
||||
private:
|
||||
Client& m_client;
|
||||
Dcb m_client_dcb;
|
||||
MYSQL_session m_mysql_session;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
100
server/modules/filter/test/maxscale/module.hh
Normal file
100
server/modules/filter/test/maxscale/module.hh
Normal file
@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 <maxscale/cppdefs.hh>
|
||||
#include <memory>
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
/**
|
||||
* The class Module is an abstraction for a MaxScale module, to
|
||||
* be used as the base class of a specific module.
|
||||
*/
|
||||
class Module
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static void* load(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.
|
||||
*
|
||||
* @return True, if the process initialization succeeded.
|
||||
*/
|
||||
static bool process_init();
|
||||
|
||||
/**
|
||||
* Perform process finalization of all modules.
|
||||
*/
|
||||
static void process_finish();
|
||||
|
||||
/**
|
||||
* Perform thread initialization of all modules. Should be called only
|
||||
* when all modules intended to be loaded have been loaded.
|
||||
*
|
||||
* @return True, if the thread initialization could be performed.
|
||||
*/
|
||||
static bool thread_init();
|
||||
|
||||
/**
|
||||
* Perform thread finalization of all modules.
|
||||
*/
|
||||
static void thread_finish();
|
||||
};
|
||||
|
||||
/**
|
||||
* The template Module is intended to be derived from using the derived
|
||||
* class as template argument.
|
||||
*
|
||||
* class XyzModule : public SpecificModule<XyzModule> { ... }
|
||||
*
|
||||
* @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>
|
||||
class SpecificModule : public Module
|
||||
{
|
||||
public:
|
||||
static std::auto_ptr<T> load(const char* zFile_name)
|
||||
{
|
||||
std::auto_ptr<T> sT;
|
||||
|
||||
void* pApi = Module::load(zFile_name, T::zName);
|
||||
|
||||
if (pApi)
|
||||
{
|
||||
sT.reset(new T(static_cast<typename T::type_t*>(pApi)));
|
||||
}
|
||||
|
||||
return sT;
|
||||
}
|
||||
|
||||
protected:
|
||||
SpecificModule()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}
|
48
server/modules/filter/test/maxscale/queryclassifiermodule.hh
Normal file
48
server/modules/filter/test/maxscale/queryclassifiermodule.hh
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
/*
|
||||
* 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 <maxscale/cppdefs.hh>
|
||||
#include <memory>
|
||||
#include <maxscale/query_classifier.h>
|
||||
#include "module.hh"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
/**
|
||||
* A QueryClassfierModule instance is an abstraction for a query
|
||||
* classifier module.
|
||||
*/
|
||||
class QueryClassifierModule : public SpecificModule<QueryClassifierModule>
|
||||
{
|
||||
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. */
|
||||
|
||||
private:
|
||||
friend class SpecificModule<QueryClassifierModule>;
|
||||
|
||||
QueryClassifierModule(QUERY_CLASSIFIER* pApi)
|
||||
: m_pApi(pApi)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
QUERY_CLASSIFIER* m_pApi;
|
||||
};
|
||||
|
||||
}
|
26
server/modules/filter/test/mock.cc
Normal file
26
server/modules/filter/test/mock.cc
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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 "maxscale/mock/mock.hh"
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
155
server/modules/filter/test/mock_backend.cc
Normal file
155
server/modules/filter/test/mock_backend.cc
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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 "maxscale/mock/backend.hh"
|
||||
#include <algorithm>
|
||||
#include <maxscale/protocol/mysql.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
//
|
||||
// Backend
|
||||
//
|
||||
|
||||
Backend::Backend()
|
||||
{
|
||||
}
|
||||
|
||||
Backend::~Backend()
|
||||
{
|
||||
}
|
||||
|
||||
//
|
||||
// BufferBackend
|
||||
//
|
||||
BufferBackend::BufferBackend()
|
||||
{
|
||||
}
|
||||
|
||||
BufferBackend::~BufferBackend()
|
||||
{
|
||||
}
|
||||
|
||||
bool BufferBackend::respond(RouterSession* pSession)
|
||||
{
|
||||
bool empty = false;
|
||||
GWBUF* pResponse = dequeue_response(pSession, &empty);
|
||||
|
||||
if (pResponse)
|
||||
{
|
||||
pSession->clientReply(pResponse);
|
||||
}
|
||||
|
||||
return !empty;
|
||||
}
|
||||
|
||||
bool BufferBackend::idle(const RouterSession* pSession) const
|
||||
{
|
||||
bool rv = true;
|
||||
|
||||
SessionResponses::const_iterator i = m_session_responses.find(pSession);
|
||||
|
||||
if (i != m_session_responses.end())
|
||||
{
|
||||
const Responses& responses = i->second;
|
||||
rv = responses.empty();
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool BufferBackend::discard_one_response(const RouterSession* pSession)
|
||||
{
|
||||
bool empty = false;
|
||||
gwbuf_free(dequeue_response(pSession, &empty));
|
||||
|
||||
return !empty;
|
||||
}
|
||||
|
||||
void BufferBackend::discard_all_responses(const RouterSession* pSession)
|
||||
{
|
||||
ss_dassert(!idle(pSession));
|
||||
|
||||
if (!idle(pSession))
|
||||
{
|
||||
Responses& responses = m_session_responses[pSession];
|
||||
ss_dassert(!responses.empty());
|
||||
|
||||
std::for_each(responses.begin(), responses.end(), gwbuf_free);
|
||||
responses.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void BufferBackend::enqueue_response(const RouterSession* pSession, GWBUF* pResponse)
|
||||
{
|
||||
Responses& responses = m_session_responses[pSession];
|
||||
|
||||
responses.push_back(pResponse);
|
||||
}
|
||||
|
||||
GWBUF* BufferBackend::dequeue_response(const RouterSession* pSession, bool* pEmpty)
|
||||
{
|
||||
ss_dassert(!idle(pSession));
|
||||
GWBUF* pResponse = NULL;
|
||||
*pEmpty = true;
|
||||
|
||||
if (!idle(pSession))
|
||||
{
|
||||
Responses& responses = m_session_responses[pSession];
|
||||
ss_dassert(!responses.empty());
|
||||
|
||||
if (!responses.empty())
|
||||
{
|
||||
pResponse = responses.front();
|
||||
responses.pop_front();
|
||||
}
|
||||
|
||||
*pEmpty = responses.empty();
|
||||
}
|
||||
|
||||
return pResponse;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// OkBackend
|
||||
//
|
||||
|
||||
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);
|
||||
|
||||
gwbuf_free(pStatement);
|
||||
}
|
||||
|
||||
} // mock
|
||||
|
||||
} // maxscale
|
143
server/modules/filter/test/mock_client.cc
Normal file
143
server/modules/filter/test/mock_client.cc
Normal file
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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 "maxscale/mock/client.hh"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
//
|
||||
// Client
|
||||
//
|
||||
Client::Client(const char* zUser,
|
||||
const char* zHost,
|
||||
Handler* pHandler)
|
||||
: m_user(zUser)
|
||||
, m_host(zHost)
|
||||
, m_pHandler(pHandler)
|
||||
, m_n_responses(0)
|
||||
{
|
||||
}
|
||||
|
||||
Client::~Client()
|
||||
{
|
||||
}
|
||||
|
||||
const char* Client::user() const
|
||||
{
|
||||
return m_user.c_str();
|
||||
}
|
||||
|
||||
const char* Client::host() const
|
||||
{
|
||||
return m_host.c_str();
|
||||
}
|
||||
|
||||
size_t Client::n_responses() const
|
||||
{
|
||||
return m_n_responses;
|
||||
}
|
||||
|
||||
Client::Handler* Client::set_handler(Handler* pHandler)
|
||||
{
|
||||
Handler* pH = m_pHandler;
|
||||
m_pHandler = pHandler;
|
||||
return pH;
|
||||
}
|
||||
|
||||
void Client::reset()
|
||||
{
|
||||
m_n_responses = 0;
|
||||
|
||||
if (m_pHandler)
|
||||
{
|
||||
m_pHandler->reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Client::set_as_upstream_on(FilterModule::Session& filter_session)
|
||||
{
|
||||
MXS_UPSTREAM upstream;
|
||||
upstream.instance = &m_instance;
|
||||
upstream.session = this;
|
||||
upstream.clientReply = &Client::clientReply;
|
||||
upstream.error = NULL;
|
||||
|
||||
filter_session.setUpstream(&upstream);
|
||||
}
|
||||
|
||||
int32_t Client::clientReply(GWBUF* pResponse)
|
||||
{
|
||||
int32_t rv = 1;
|
||||
|
||||
++m_n_responses;
|
||||
|
||||
if (m_pHandler)
|
||||
{
|
||||
rv = m_pHandler->backend_reply(pResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
gwbuf_free(pResponse);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
int32_t Client::write(GWBUF* pResponse)
|
||||
{
|
||||
int32_t rv = 1;
|
||||
|
||||
++m_n_responses;
|
||||
|
||||
if (m_pHandler)
|
||||
{
|
||||
rv = m_pHandler->maxscale_reply(pResponse);
|
||||
}
|
||||
else
|
||||
{
|
||||
gwbuf_free(pResponse);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//static
|
||||
int32_t Client::clientReply(MXS_FILTER* pInstance,
|
||||
MXS_FILTER_SESSION* pSession,
|
||||
GWBUF* pResponse)
|
||||
{
|
||||
Client* pClient = reinterpret_cast<Client*>(pSession);
|
||||
ss_dassert(pInstance == &pClient->m_instance);
|
||||
|
||||
return pClient->clientReply(pResponse);
|
||||
}
|
||||
|
||||
//
|
||||
// Client::Handler
|
||||
//
|
||||
|
||||
Client::Handler::~Handler()
|
||||
{
|
||||
}
|
||||
|
||||
void Client::Handler::reset()
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
96
server/modules/filter/test/mock_dcb.cc
Normal file
96
server/modules/filter/test/mock_dcb.cc
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* 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 "maxscale/mock/dcb.hh"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
void initialize_dcb(DCB* pDcb)
|
||||
{
|
||||
memset(pDcb, 0, sizeof(DCB));
|
||||
|
||||
pDcb->dcb_chk_top = CHK_NUM_DCB;
|
||||
pDcb->fd = DCBFD_CLOSED;
|
||||
pDcb->state = DCB_STATE_ALLOC;
|
||||
pDcb->ssl_state = SSL_HANDSHAKE_UNKNOWN;
|
||||
pDcb->dcb_chk_tail = CHK_NUM_DCB;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
Dcb::Dcb(MXS_SESSION* pSession,
|
||||
const char* zUser,
|
||||
const char* zHost,
|
||||
Handler* pHandler)
|
||||
: m_user(zUser)
|
||||
, m_host(zHost)
|
||||
, m_pHandler(pHandler)
|
||||
{
|
||||
DCB* pDcb = this;
|
||||
initialize_dcb(this);
|
||||
|
||||
pDcb->session = pSession;
|
||||
pDcb->remote = const_cast<char*>(zHost);
|
||||
pDcb->user = const_cast<char*>(zUser);
|
||||
|
||||
pDcb->func.write = &Dcb::write;
|
||||
}
|
||||
|
||||
Dcb::~Dcb()
|
||||
{
|
||||
}
|
||||
|
||||
Dcb::Handler* Dcb::handler() const
|
||||
{
|
||||
return m_pHandler;
|
||||
}
|
||||
|
||||
Dcb::Handler* Dcb::set_handler(Handler* pHandler)
|
||||
{
|
||||
Handler* p = m_pHandler;
|
||||
m_pHandler = pHandler;
|
||||
return p;
|
||||
}
|
||||
|
||||
int32_t Dcb::write(GWBUF* pData)
|
||||
{
|
||||
int32_t rv = 1;
|
||||
|
||||
if (m_pHandler)
|
||||
{
|
||||
rv = m_pHandler->write(pData);
|
||||
}
|
||||
else
|
||||
{
|
||||
gwbuf_free(pData);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
//static
|
||||
int32_t Dcb::write(DCB* pDcb, GWBUF* pData)
|
||||
{
|
||||
return static_cast<Dcb*>(pDcb)->write(pData);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
90
server/modules/filter/test/mock_routersession.cc
Normal file
90
server/modules/filter/test/mock_routersession.cc
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 "maxscale/mock/routersession.hh"
|
||||
#include "maxscale/mock/backend.hh"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
RouterSession::RouterSession(Backend* pBackend)
|
||||
: m_pBackend(pBackend)
|
||||
{
|
||||
memset(&m_instance, 0, sizeof(m_instance));
|
||||
}
|
||||
|
||||
RouterSession::~RouterSession()
|
||||
{
|
||||
}
|
||||
|
||||
void RouterSession::set_as_downstream_on(FilterModule::Session* pFilter_session)
|
||||
{
|
||||
MXS_DOWNSTREAM downstream;
|
||||
downstream.instance = reinterpret_cast<MXS_FILTER*>(&m_instance);
|
||||
downstream.session = reinterpret_cast<MXS_FILTER_SESSION*>(this);
|
||||
downstream.routeQuery = &RouterSession::routeQuery;
|
||||
|
||||
pFilter_session->setDownstream(&downstream);
|
||||
|
||||
m_pUpstream_filter_session = pFilter_session;
|
||||
}
|
||||
|
||||
bool RouterSession::respond()
|
||||
{
|
||||
return m_pBackend->respond(this);
|
||||
}
|
||||
|
||||
bool RouterSession::idle() const
|
||||
{
|
||||
return m_pBackend->idle(this);
|
||||
}
|
||||
|
||||
bool RouterSession::discard_one_response()
|
||||
{
|
||||
return m_pBackend->discard_one_response(this);
|
||||
}
|
||||
|
||||
void RouterSession::discard_all_responses()
|
||||
{
|
||||
return m_pBackend->discard_all_responses(this);
|
||||
}
|
||||
|
||||
int32_t RouterSession::routeQuery(MXS_ROUTER* pInstance, GWBUF* pStatement)
|
||||
{
|
||||
ss_dassert(pInstance == &m_instance);
|
||||
|
||||
m_pBackend->handle_statement(this, pStatement);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int32_t RouterSession::clientReply(GWBUF* pResponse)
|
||||
{
|
||||
return m_pUpstream_filter_session->clientReply(pResponse);
|
||||
}
|
||||
|
||||
//static
|
||||
int32_t RouterSession::routeQuery(MXS_FILTER* pInstance,
|
||||
MXS_FILTER_SESSION* pRouter_session,
|
||||
GWBUF* pStatement)
|
||||
{
|
||||
RouterSession* pThis = reinterpret_cast<RouterSession*>(pRouter_session);
|
||||
|
||||
return pThis->routeQuery(reinterpret_cast<MXS_ROUTER*>(pInstance), pStatement);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
54
server/modules/filter/test/mock_session.cc
Normal file
54
server/modules/filter/test/mock_session.cc
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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 "maxscale/mock/session.hh"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
namespace mock
|
||||
{
|
||||
|
||||
Session::Session(Client* pClient)
|
||||
: m_client(*pClient)
|
||||
, m_client_dcb(this, pClient->user(), pClient->host(), pClient)
|
||||
{
|
||||
MXS_SESSION* pSession = this;
|
||||
|
||||
memset(pSession, 0, sizeof(MXS_SESSION));
|
||||
|
||||
pSession->ses_chk_top = CHK_NUM_SESSION;
|
||||
pSession->state = SESSION_STATE_ALLOC;
|
||||
pSession->ses_chk_tail = CHK_NUM_SESSION;
|
||||
|
||||
pSession->client_dcb = &m_client_dcb;
|
||||
|
||||
memset(&m_mysql_session, 0, sizeof(m_mysql_session));
|
||||
|
||||
strcpy(m_mysql_session.db, "dummy");
|
||||
|
||||
m_client_dcb.data = &m_mysql_session;
|
||||
}
|
||||
|
||||
Session::~Session()
|
||||
{
|
||||
}
|
||||
|
||||
Client& Session::client() const
|
||||
{
|
||||
return m_client;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
146
server/modules/filter/test/module.cc
Normal file
146
server/modules/filter/test/module.cc
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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 "maxscale/module.hh"
|
||||
#include "../../../core/internal/modules.h"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
//static
|
||||
void* Module::load(const char* zName, const char* zType)
|
||||
{
|
||||
return load_module(zName, zType);
|
||||
}
|
||||
|
||||
//static
|
||||
bool Module::process_init()
|
||||
{
|
||||
bool initialized = false;
|
||||
|
||||
MXS_MODULE_ITERATOR i = mxs_module_iterator_get(NULL);
|
||||
MXS_MODULE* module = NULL;
|
||||
|
||||
while ((module = mxs_module_iterator_get_next(&i)) != NULL)
|
||||
{
|
||||
if (module->process_init)
|
||||
{
|
||||
int rc = (module->process_init)();
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (module)
|
||||
{
|
||||
// If module is non-NULL it means that the initialization failed for
|
||||
// that module. We now need to call finish on all modules that were
|
||||
// successfully initialized.
|
||||
MXS_MODULE* failed_module = module;
|
||||
i = mxs_module_iterator_get(NULL);
|
||||
|
||||
while ((module = mxs_module_iterator_get_next(&i)) != failed_module)
|
||||
{
|
||||
if (module->process_finish)
|
||||
{
|
||||
(module->process_finish)();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return initialized;
|
||||
}
|
||||
|
||||
//static
|
||||
void Module::process_finish()
|
||||
{
|
||||
MXS_MODULE_ITERATOR i = mxs_module_iterator_get(NULL);
|
||||
MXS_MODULE* module = NULL;
|
||||
|
||||
while ((module = mxs_module_iterator_get_next(&i)) != NULL)
|
||||
{
|
||||
if (module->process_finish)
|
||||
{
|
||||
(module->process_finish)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
bool Module::thread_init()
|
||||
{
|
||||
bool initialized = false;
|
||||
|
||||
MXS_MODULE_ITERATOR i = mxs_module_iterator_get(NULL);
|
||||
MXS_MODULE* module = NULL;
|
||||
|
||||
while ((module = mxs_module_iterator_get_next(&i)) != NULL)
|
||||
{
|
||||
if (module->thread_init)
|
||||
{
|
||||
int rc = (module->thread_init)();
|
||||
|
||||
if (rc != 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (module)
|
||||
{
|
||||
// If module is non-NULL it means that the initialization failed for
|
||||
// that module. We now need to call finish on all modules that were
|
||||
// successfully initialized.
|
||||
MXS_MODULE* failed_module = module;
|
||||
i = mxs_module_iterator_get(NULL);
|
||||
|
||||
while ((module = mxs_module_iterator_get_next(&i)) != failed_module)
|
||||
{
|
||||
if (module->thread_finish)
|
||||
{
|
||||
(module->thread_finish)();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
return initialized;
|
||||
}
|
||||
|
||||
//static
|
||||
void Module::thread_finish()
|
||||
{
|
||||
MXS_MODULE_ITERATOR i = mxs_module_iterator_get(NULL);
|
||||
MXS_MODULE* module = NULL;
|
||||
|
||||
while ((module = mxs_module_iterator_get_next(&i)) != NULL)
|
||||
{
|
||||
if (module->thread_finish)
|
||||
{
|
||||
(module->thread_finish)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
23
server/modules/filter/test/queryclassifiermodule.cc
Normal file
23
server/modules/filter/test/queryclassifiermodule.cc
Normal file
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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 "maxscale/queryclassifiermodule.hh"
|
||||
#include "../../../core/internal/modules.h"
|
||||
|
||||
namespace maxscale
|
||||
{
|
||||
|
||||
//static
|
||||
const char* QueryClassifierModule::zName = MODULE_QUERY_CLASSIFIER;
|
||||
|
||||
}
|
Reference in New Issue
Block a user