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:
@ -27,6 +27,11 @@
|
||||
#include <maxscale/spinlock.h>
|
||||
#include <maxscale/jansson.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
#include <tr1/unordered_map>
|
||||
#include <string>
|
||||
#endif
|
||||
|
||||
MXS_BEGIN_DECLS
|
||||
|
||||
struct dcb;
|
||||
@ -120,6 +125,34 @@ typedef struct mxs_upstream
|
||||
int32_t (*error)(void *instance, void *session, void *);
|
||||
} MXS_UPSTREAM;
|
||||
|
||||
/**
|
||||
* Handler function for MaxScale specific session variables.
|
||||
*
|
||||
* Note that the provided value string is exactly as it appears in
|
||||
* the received SET-statement. Only leading and trailing whitespace
|
||||
* has been removed. The handler must itself parse the value string.
|
||||
*
|
||||
* @param context Context provided when handler was registered.
|
||||
* @param name The variable that is being set.
|
||||
* @param value_begin The beginning of the value as specified in the
|
||||
* "set @maxscale.x.y = VALUE" statement.
|
||||
* @param value_end One past the end of the VALUE.
|
||||
*/
|
||||
typedef void (*session_variable_handler_t)(void* context,
|
||||
const char* name,
|
||||
const char* value_begin,
|
||||
const char* value_end);
|
||||
|
||||
#ifdef __cplusplus
|
||||
typedef struct session_variable
|
||||
{
|
||||
session_variable_handler_t handler;
|
||||
void* context;
|
||||
} SESSION_VARIABLE;
|
||||
|
||||
typedef std::tr1::unordered_map<std::string, SESSION_VARIABLE> SessionVarsByName;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* The session status block
|
||||
*
|
||||
@ -154,6 +187,11 @@ typedef struct session
|
||||
const struct server *target; /**< Where the statement was sent */
|
||||
} stmt; /**< Current statement being executed */
|
||||
bool qualifies_for_pooling; /**< Whether this session qualifies for the connection pool */
|
||||
#ifdef __cplusplus
|
||||
SessionVarsByName* variables; /*< @maxscale variables associated with this session */
|
||||
#else
|
||||
void* variables;
|
||||
#endif
|
||||
skygw_chk_t ses_chk_tail;
|
||||
} MXS_SESSION;
|
||||
|
||||
@ -482,4 +520,65 @@ MXS_SESSION* session_get_current();
|
||||
**/
|
||||
uint64_t session_get_current_id();
|
||||
|
||||
/**
|
||||
* @brief Add new MaxScale specific user variable to the session.
|
||||
*
|
||||
* The name of the variable must be of the following format:
|
||||
*
|
||||
* "@maxscale\.[a-zA-Z_]+(\.[a-zA-Z_])*"
|
||||
*
|
||||
* e.g. "@maxscale.cache.enabled". A strong suggestion is that the first
|
||||
* sub-scope is the same as the module name of the component registering the
|
||||
* variable. The sub-scope "core" is reserved by MaxScale.
|
||||
*
|
||||
* The variable name will be converted to all lowercase when added.
|
||||
*
|
||||
* @param session The session in question.
|
||||
* @param name The name of the variable, must start with "@MAXSCALE.".
|
||||
* @param handler The handler function for the variable.
|
||||
* @param context Context that will be passed to the handler function.
|
||||
*
|
||||
* @return True, if the variable could be added, false otherwise.
|
||||
*/
|
||||
bool session_add_variable(MXS_SESSION* session,
|
||||
const char* name,
|
||||
session_variable_handler_t handler,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* @brief Remove MaxScale specific user variable from the session.
|
||||
*
|
||||
* With this function a particular MaxScale specific user variable
|
||||
* can be removed. Note that it is *not* mandatory to remove a
|
||||
* variable when a session is closed, but have to be done in case
|
||||
* the context object must manually be deleted.
|
||||
*
|
||||
* @param session The session in question.
|
||||
* @param name The name of the variable.
|
||||
* @param context On successful return, if non-NULL, the context object
|
||||
* that was provided when the variable was added.
|
||||
*
|
||||
* @return True, if the variable existed, false otherwise.
|
||||
*/
|
||||
bool session_remove_variable(MXS_SESSION* session,
|
||||
const char* name,
|
||||
void** context);
|
||||
/**
|
||||
* @brief Set value of maxscale session variable.
|
||||
*
|
||||
* @param session The session.
|
||||
* @param name_begin Should point to the beginning of the variable name.
|
||||
* @param name_end Should point one past the end of the variable name.
|
||||
* @param value_begin Should point to the beginning of the value.
|
||||
* @param value_end Should point one past the end of the value.
|
||||
*
|
||||
* @note Should only be called from the protocol module that scans
|
||||
* incoming statements.
|
||||
*/
|
||||
void session_set_variable_value(MXS_SESSION* session,
|
||||
const char* name_begin,
|
||||
const char* name_end,
|
||||
const char* value_begin,
|
||||
const char* value_end);
|
||||
|
||||
MXS_END_DECLS
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -917,7 +917,9 @@ void handle_variables(MXS_SESSION* session, GWBUF** read_buffer)
|
||||
break;
|
||||
|
||||
case SetParser::IS_SET_MAXSCALE:
|
||||
// TODO: Handle "set @MAXSCALE...=...";
|
||||
session_set_variable_value(session,
|
||||
result.variable_begin(), result.variable_end(),
|
||||
result.value_begin(), result.value_end());
|
||||
break;
|
||||
|
||||
case SetParser::NOT_RELEVANT:
|
||||
|
Reference in New Issue
Block a user