MXS-1475 Enable MaxScale specific user variables

With the changes in this commit it is possible to add and remove
MaxScale specific user variables. A MaxScale specific user variable
is a user variable that is interpreted by MaxScale and that
potentially changes the behaviour of MaxScale.

MaxScale specific user variables are of the format "@maxscale.x.y"
where "@maxscale" is a mandatory prefix, x a scope identifying the
component that handles the variable and y the component specific
variable. So, a variable might be called e.g. "@maxscale.cache.enabled".
The scope "core" is reserved (although not enforced yet) to MaxScale
itself.

The idea is that although MaxScale catches these, they are passed
through to the server. The benefit of this is that we do not need to
detect e.g. "SELECT @maxscale.cache.enabled", but can let the result
be returned from the server.

The interpretation of a provided value is handled by the component that
adds the variable. In a subsequent commit, it will be possible for a
component to reject a value, which will then cause an error to be
returned to the client.

There are 3 new functions:

- session_add_variable() using which a variable is added,
- session_remove_variable() using which a variable is removed, and
- session_set_variable_value().

The two former ones are to be called by components, the last one by
the protocol that catches the "set @maxscale..." statements.
This commit is contained in:
Johan Wikman
2018-02-26 16:07:58 +02:00
parent 5dfa0c1226
commit 872a51a376
3 changed files with 195 additions and 12 deletions

View File

@ -22,6 +22,7 @@
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <algorithm>
#include <string>
#include <sstream>
@ -91,25 +92,24 @@ session_initialize(MXS_SESSION *session)
MXS_SESSION* session_alloc(SERVICE *service, DCB *client_dcb)
{
MXS_SESSION *session = (MXS_SESSION *)(MXS_MALLOC(sizeof(*session)));
if (NULL == session)
{
return NULL;
}
session_initialize(session);
session->ses_id = session_get_next_id();
return session_alloc_body(service, client_dcb, session);
return session_alloc_with_id(service, client_dcb, session_get_next_id());
}
MXS_SESSION* session_alloc_with_id(SERVICE *service, DCB *client_dcb, uint64_t id)
{
MXS_SESSION *session = (MXS_SESSION *)(MXS_MALLOC(sizeof(*session)));
if (session == NULL)
SessionVarsByName *session_variables = new (std::nothrow) SessionVarsByName;
if ((session == NULL) || (session_variables == NULL))
{
MXS_FREE(session);
delete session_variables;
return NULL;
}
session->variables = session_variables;
session_initialize(session);
session->ses_id = id;
return session_alloc_body(service, client_dcb, session);
@ -389,6 +389,7 @@ static void
session_final_free(MXS_SESSION *session)
{
gwbuf_free(session->stmt.buffer);
delete session->variables;
MXS_FREE(session);
}
@ -1105,3 +1106,84 @@ uint64_t session_get_current_id()
return session ? session->ses_id : 0;
}
bool session_add_variable(MXS_SESSION* session,
const char* name,
session_variable_handler_t handler,
void* context)
{
bool added = false;
static const char PREFIX[] = "@MAXSCALE.";
if (strncasecmp(name, PREFIX, sizeof(PREFIX) - 1) == 0)
{
string key(name);
std::transform(key.begin(), key.end(), key.begin(), toupper);
if (session->variables->find(key) != session->variables->end())
{
SESSION_VARIABLE variable;
variable.handler = handler;
variable.context = context;
session->variables->insert(std::make_pair(key, variable));
added = true;
}
else
{
MXS_ERROR("Session variable '%s' has been added already.", name);
}
}
else
{
MXS_ERROR("Session variable '%s' is not of the correct format.", name);
}
return added;
}
void session_set_variable_value(MXS_SESSION* session,
const char* name_begin,
const char* name_end,
const char* value_begin,
const char* value_end)
{
string key(name_begin, name_end - name_begin);
transform(key.begin(), key.end(), key.begin(), toupper);
SessionVarsByName::iterator i = session->variables->find(key);
if (i != session->variables->end())
{
i->second.handler(i->second.context, key.c_str(), value_begin, value_end);
}
}
bool session_remove_variable(MXS_SESSION* session,
const char* name,
void** context)
{
bool removed = false;
string key(name);
transform(key.begin(), key.end(), key.begin(), toupper);
SessionVarsByName::iterator i = session->variables->find(key);
if (i != session->variables->end())
{
if (context)
{
*context = i->second.context;
}
session->variables->erase(i);
removed = true;
}
return removed;
}