Merge branch '2.3' into develop
This commit is contained in:
		@ -12,21 +12,39 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// All log messages from this module are prefixed with this
 | 
			
		||||
#define MXS_MODULE_NAME "examplefilter"
 | 
			
		||||
#define MXS_MODULE_NAME "examplecppfilter"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * To use the filter in a configuration, add the following section to the config file:
 | 
			
		||||
 * [ExampleFilter]
 | 
			
		||||
 * type=filter
 | 
			
		||||
 * module=examplecppfilter
 | 
			
		||||
 * global_counts=true
 | 
			
		||||
 *
 | 
			
		||||
 * Then add the filter to a service:
 | 
			
		||||
 * [Read-Write-Service]
 | 
			
		||||
 * .
 | 
			
		||||
 * .
 | 
			
		||||
 * filters=ExampleFilter
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "examplefilter.hh"
 | 
			
		||||
 | 
			
		||||
static const char CN_COUNT_GLOBALS[] = "global_counts";
 | 
			
		||||
 | 
			
		||||
// This declares a module in MaxScale
 | 
			
		||||
extern "C" MXS_MODULE* MXS_CREATE_MODULE()
 | 
			
		||||
{
 | 
			
		||||
    static const char DESC[] = "An example filter that counts the number of queries and replies "
 | 
			
		||||
                               "it has routed";
 | 
			
		||||
    static MXS_MODULE info =
 | 
			
		||||
    {
 | 
			
		||||
        MXS_MODULE_API_FILTER,
 | 
			
		||||
        MXS_MODULE_IN_DEVELOPMENT,
 | 
			
		||||
        MXS_FILTER_VERSION,
 | 
			
		||||
        "An example filter that does nothing",
 | 
			
		||||
        DESC,
 | 
			
		||||
        "V1.0.0",
 | 
			
		||||
        RCAP_TYPE_NONE,
 | 
			
		||||
        RCAP_TYPE_STMT_INPUT,                   // See getCapabilities() below
 | 
			
		||||
        &ExampleFilter::s_object,               // This is defined in the MaxScale filter template
 | 
			
		||||
        NULL,                                   /* Process init. */
 | 
			
		||||
        NULL,                                   /* Process finish. */
 | 
			
		||||
@ -34,6 +52,7 @@ extern "C" MXS_MODULE* MXS_CREATE_MODULE()
 | 
			
		||||
        NULL,                                   /* Thread finish. */
 | 
			
		||||
        {
 | 
			
		||||
            {"an_example_parameter",          MXS_MODULE_PARAM_STRING, "a-default-value"},
 | 
			
		||||
            {CN_COUNT_GLOBALS,                MXS_MODULE_PARAM_BOOL,   "true"},
 | 
			
		||||
            {MXS_END_MODULE_PARAMS}
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
@ -41,8 +60,9 @@ extern "C" MXS_MODULE* MXS_CREATE_MODULE()
 | 
			
		||||
    return &info;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ExampleFilter::ExampleFilter()
 | 
			
		||||
ExampleFilter::ExampleFilter(const MXS_CONFIG_PARAMETER* pParams)
 | 
			
		||||
{
 | 
			
		||||
    m_collect_global_counts = config_get_bool(pParams, CN_COUNT_GLOBALS);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ExampleFilter::~ExampleFilter()
 | 
			
		||||
@ -52,27 +72,58 @@ ExampleFilter::~ExampleFilter()
 | 
			
		||||
// static
 | 
			
		||||
ExampleFilter* ExampleFilter::create(const char* zName, MXS_CONFIG_PARAMETER* pParams)
 | 
			
		||||
{
 | 
			
		||||
    return new ExampleFilter();
 | 
			
		||||
    return new ExampleFilter(pParams);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ExampleFilterSession* ExampleFilter::newSession(MXS_SESSION* pSession)
 | 
			
		||||
{
 | 
			
		||||
    return ExampleFilterSession::create(pSession, this);
 | 
			
		||||
    return ExampleFilterSession::create(pSession, *this);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// static
 | 
			
		||||
void ExampleFilter::diagnostics(DCB* pDcb) const
 | 
			
		||||
{
 | 
			
		||||
    int queries = m_total_queries.load(std::memory_order_relaxed);
 | 
			
		||||
    int replies = m_total_replies.load(std::memory_order_relaxed);
 | 
			
		||||
    dcb_printf(pDcb, "\t\tTotal queries            %i\n", queries);
 | 
			
		||||
    dcb_printf(pDcb, "\t\tTotal replies            %i\n", replies);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// static
 | 
			
		||||
json_t* ExampleFilter::diagnostics_json() const
 | 
			
		||||
{
 | 
			
		||||
    return NULL;
 | 
			
		||||
    json_t* rval = json_object();
 | 
			
		||||
    int queries = m_total_queries.load(std::memory_order_relaxed);
 | 
			
		||||
    int replies = m_total_replies.load(std::memory_order_relaxed);
 | 
			
		||||
    json_object_set_new(rval, "total_queries", json_integer(queries));
 | 
			
		||||
    json_object_set_new(rval, "total_replies", json_integer(replies));
 | 
			
		||||
    return rval;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// static
 | 
			
		||||
uint64_t ExampleFilter::getCapabilities()
 | 
			
		||||
{
 | 
			
		||||
    return RCAP_TYPE_NONE;
 | 
			
		||||
    // Tells the protocol that the filter expects complete queries from client, that is, a query cannot be
 | 
			
		||||
    // sent in parts.
 | 
			
		||||
    return RCAP_TYPE_STMT_INPUT;
 | 
			
		||||
 | 
			
		||||
    // Try the following to also expect replies to be complete. This can cause problems if the server sends
 | 
			
		||||
    // a really big (e.g. 1 GB) resultset.
 | 
			
		||||
    // return RCAP_TYPE_STMT_INPUT | RCAP_TYPE_RESULTSET_OUTPUT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ExampleFilter::query_seen()
 | 
			
		||||
{
 | 
			
		||||
    if (m_collect_global_counts)
 | 
			
		||||
    {
 | 
			
		||||
        m_total_queries.fetch_add(1, std::memory_order_relaxed);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ExampleFilter::reply_seen()
 | 
			
		||||
{
 | 
			
		||||
    if (m_collect_global_counts)
 | 
			
		||||
    {
 | 
			
		||||
        m_total_replies.fetch_add(1, std::memory_order_relaxed);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -13,9 +13,16 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <maxscale/ccdefs.hh>
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <maxscale/filter.hh>
 | 
			
		||||
#include "examplefiltersession.hh"
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Defines general data for the filter. This object is generated when MaxScale starts and deleted at
 | 
			
		||||
 * shutdown. When MaxScale is routing queries, this object may be accessed from multiple threads
 | 
			
		||||
 * concurrently. This should be considered if the object contains fields that are unsafe to
 | 
			
		||||
 * access/modify concurrently.
 | 
			
		||||
 */
 | 
			
		||||
class ExampleFilter : public maxscale::Filter<ExampleFilter, ExampleFilterSession>
 | 
			
		||||
{
 | 
			
		||||
    // Prevent copy-constructor and assignment operator usage
 | 
			
		||||
@ -25,22 +32,63 @@ class ExampleFilter : public maxscale::Filter<ExampleFilter, ExampleFilterSessio
 | 
			
		||||
public:
 | 
			
		||||
    ~ExampleFilter();
 | 
			
		||||
 | 
			
		||||
    // Creates a new filter instance
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a new filter instance. A separate function from the ctor is used so that NULL can be
 | 
			
		||||
     * returned on failure.
 | 
			
		||||
     *
 | 
			
		||||
     * @param zName The name given to the filter in the configuration file. Can be stored if required for
 | 
			
		||||
     * e.g. log messages.
 | 
			
		||||
     * @param ppParams Configuration parameters parsed from the configuration file
 | 
			
		||||
     * @return The object on success, NULL on failure. Failure is typically caused by an invalid
 | 
			
		||||
     * configuration parameter.
 | 
			
		||||
     */
 | 
			
		||||
    static ExampleFilter* create(const char* zName, MXS_CONFIG_PARAMETER* ppParams);
 | 
			
		||||
 | 
			
		||||
    // Creates a new session for this filter
 | 
			
		||||
    /*
 | 
			
		||||
     * Creates a new session for this filter. This is called when a new client connects.
 | 
			
		||||
     *
 | 
			
		||||
     * @param pSession The generic MaxScale session object.
 | 
			
		||||
     * @return The new session, or NULL on failure.
 | 
			
		||||
     */
 | 
			
		||||
    ExampleFilterSession* newSession(MXS_SESSION* pSession);
 | 
			
		||||
 | 
			
		||||
    // Print diagnostics to a DCB
 | 
			
		||||
    /*
 | 
			
		||||
     * Print diagnostics to a DCB. This is called when the admin tool MaxAdmin asks for the status of this
 | 
			
		||||
     * filter. Run MaxAdmin with "./maxadmin show filters" in the MaxScale binary directory.
 | 
			
		||||
     *
 | 
			
		||||
     * @param pDcb The connection descriptor to print diagnostic to
 | 
			
		||||
     */
 | 
			
		||||
    void diagnostics(DCB* pDcb) const;
 | 
			
		||||
 | 
			
		||||
    // Returns JSON form diagnostic data
 | 
			
		||||
    /*
 | 
			
		||||
     * Returns JSON form diagnostic data. This is called when the admin tool MaxCtrl asks for the status
 | 
			
		||||
     * of this filter. Run MaxCtrl with "./maxctrl show filters" in the MaxScale binary directory.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Json object
 | 
			
		||||
     */
 | 
			
		||||
    json_t* diagnostics_json() const;
 | 
			
		||||
 | 
			
		||||
    // Get filter capabilities
 | 
			
		||||
    /*
 | 
			
		||||
     * Get filter capabilities. This is used by protocol code to find out what kind of data the filter
 | 
			
		||||
     * expects.
 | 
			
		||||
     *
 | 
			
		||||
     * @return Capabilities as a bitfield
 | 
			
		||||
     */
 | 
			
		||||
    uint64_t getCapabilities();
 | 
			
		||||
 | 
			
		||||
    // Specific to ExampleFilter. Called by a session when it sees a query.
 | 
			
		||||
    void query_seen();
 | 
			
		||||
 | 
			
		||||
    // Specific to ExampleFilter. Called by a session when it sees a reply.
 | 
			
		||||
    void reply_seen();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    // Used in the create function
 | 
			
		||||
    ExampleFilter();
 | 
			
		||||
    // Used by the create function
 | 
			
		||||
    ExampleFilter(const MXS_CONFIG_PARAMETER* pParams);
 | 
			
		||||
 | 
			
		||||
    // The fields are specific to ExampleFilter.
 | 
			
		||||
    std::atomic<int> m_total_queries {0}; /**< How many queries has this filter seen */
 | 
			
		||||
    std::atomic<int> m_total_replies {0}; /**< How many replies has this filter seen */
 | 
			
		||||
 | 
			
		||||
    bool m_collect_global_counts {false}; /**< Should sessions manipulate the total counts */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -17,8 +17,10 @@
 | 
			
		||||
#include "examplefiltersession.hh"
 | 
			
		||||
#include "examplefilter.hh"
 | 
			
		||||
 | 
			
		||||
ExampleFilterSession::ExampleFilterSession(MXS_SESSION* pSession)
 | 
			
		||||
ExampleFilterSession::ExampleFilterSession(MXS_SESSION* pSession, ExampleFilter& filter)
 | 
			
		||||
    : mxs::FilterSession(pSession)
 | 
			
		||||
    , m_filter(filter)
 | 
			
		||||
    , m_session_id(pSession->ses_id)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -27,21 +29,31 @@ ExampleFilterSession::~ExampleFilterSession()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// static
 | 
			
		||||
ExampleFilterSession* ExampleFilterSession::create(MXS_SESSION* pSession, const ExampleFilter* pFilter)
 | 
			
		||||
ExampleFilterSession* ExampleFilterSession::create(MXS_SESSION* pSession, ExampleFilter& filter)
 | 
			
		||||
{
 | 
			
		||||
    return new ExampleFilterSession(pSession);
 | 
			
		||||
    return new ExampleFilterSession(pSession, filter);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ExampleFilterSession::close()
 | 
			
		||||
{
 | 
			
		||||
    // When the session is closed, report the numbers to the log.
 | 
			
		||||
    MXS_NOTICE("Session %lu routed %i queries and %i replies.", m_session_id, m_queries, m_replies);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ExampleFilterSession::routeQuery(GWBUF* pPacket)
 | 
			
		||||
{
 | 
			
		||||
    m_queries++;
 | 
			
		||||
    m_filter.query_seen();
 | 
			
		||||
 | 
			
		||||
    // Pass the query forward.
 | 
			
		||||
    return mxs::FilterSession::routeQuery(pPacket);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int ExampleFilterSession::clientReply(GWBUF* pPacket)
 | 
			
		||||
{
 | 
			
		||||
    m_replies++;
 | 
			
		||||
    m_filter.reply_seen();
 | 
			
		||||
 | 
			
		||||
    // Pass the reply forward.
 | 
			
		||||
    return mxs::FilterSession::clientReply(pPacket);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,11 @@
 | 
			
		||||
 | 
			
		||||
class ExampleFilter;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Defines session-specific data for this filter. An object of this class is created when a client connects
 | 
			
		||||
 * and deleted on disconnect. The object is only accessed from one thread because sessions are locked to
 | 
			
		||||
 * a thread when created.
 | 
			
		||||
 */
 | 
			
		||||
class ExampleFilterSession : public maxscale::FilterSession
 | 
			
		||||
{
 | 
			
		||||
    // Prevent copy-constructor and assignment operator usage
 | 
			
		||||
@ -26,19 +31,48 @@ class ExampleFilterSession : public maxscale::FilterSession
 | 
			
		||||
public:
 | 
			
		||||
    ~ExampleFilterSession();
 | 
			
		||||
 | 
			
		||||
    // Called when a client session has been closed
 | 
			
		||||
    // Called when a client session has been closed. Destructor will be called right after.
 | 
			
		||||
    void close();
 | 
			
		||||
 | 
			
		||||
    // Create a new filter session
 | 
			
		||||
    static ExampleFilterSession* create(MXS_SESSION* pSession, const ExampleFilter* pFilter);
 | 
			
		||||
    /**
 | 
			
		||||
     * Called by ExampleFilter::newSession() to create the session.
 | 
			
		||||
     *
 | 
			
		||||
     * @param pSession pSession The generic MaxScale session object
 | 
			
		||||
     * @param pFilter The shared filter object
 | 
			
		||||
     * @return A new session or NULL on failure
 | 
			
		||||
     */
 | 
			
		||||
    static ExampleFilterSession* create(MXS_SESSION* pSession, ExampleFilter& pFilter);
 | 
			
		||||
 | 
			
		||||
    // Handle a query from the client
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle a query from the client. This is called when the client sends a query and the query has not
 | 
			
		||||
     * been blocked by any previous component in the query processing chain. The filter should do its own
 | 
			
		||||
     * processing and then send the query to the next component. If the query comes in multiple packets,
 | 
			
		||||
     * this is called for each packet.
 | 
			
		||||
     *
 | 
			
		||||
     * @param pPacket Packet containing the query, or at least a part of it
 | 
			
		||||
     * @return 0 on success. This typically depends on the later stages of the query processing chain.
 | 
			
		||||
     */
 | 
			
		||||
    int routeQuery(GWBUF* pPacket);
 | 
			
		||||
 | 
			
		||||
    // Handle a reply from server
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Handle a reply from server. The reply typically contains a resultset or a response to a command.
 | 
			
		||||
     * The filter should do its own processing and then send the query to the next component.
 | 
			
		||||
     * If the reply comes in multiple packets, this is called for each packet. The processing chain for
 | 
			
		||||
     * replies is the same as for queries, just walked in the opposite direction.
 | 
			
		||||
     *
 | 
			
		||||
     * @param pPacket Packet containing results
 | 
			
		||||
     * @return 0 on success. This typically depends on the later stages of the reply processing chain.
 | 
			
		||||
     */
 | 
			
		||||
    int clientReply(GWBUF* pPacket);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    // Used in the create function
 | 
			
		||||
    ExampleFilterSession(MXS_SESSION* pSession);
 | 
			
		||||
    ExampleFilterSession(MXS_SESSION* pSession, ExampleFilter& filter);
 | 
			
		||||
 | 
			
		||||
    ExampleFilter& m_filter;   /**< Shared filter data */
 | 
			
		||||
 | 
			
		||||
    uint64_t m_session_id {0}; /**< Session id */
 | 
			
		||||
    int      m_queries {0};    /**< How many queries has this session seen */
 | 
			
		||||
    int      m_replies {0};    /**< How many replies has this session seen */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -255,21 +255,27 @@ int sqlite3GetToken(const unsigned char *z, int *tokenType){
 | 
			
		||||
        return 1;
 | 
			
		||||
      }
 | 
			
		||||
#ifdef MAXSCALE
 | 
			
		||||
      if ( z[2] == '!' ){
 | 
			
		||||
      if ( z[2]=='!' || (z[2]=='M' && z[3]=='!')){
 | 
			
		||||
        int j = (z[2]=='M' ? 4 : 3);
 | 
			
		||||
        // MySQL-specific code
 | 
			
		||||
        for (i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
 | 
			
		||||
        for (i=j, c=z[j-1]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){}
 | 
			
		||||
        if (c=='*' && z[i]=='/'){
 | 
			
		||||
          if (sqlite3Isdigit(z[3])) {
 | 
			
		||||
            // A version specific executable comment, e.g. "/*!99999 ..." => never parsed.
 | 
			
		||||
          if (sqlite3Isdigit(z[j])) {
 | 
			
		||||
            // A version specific executable comment,
 | 
			
		||||
            // e.g. "/*!99999 ..." or "/*M!99999 ..." => never parsed.
 | 
			
		||||
            extern void maxscaleSetStatusCap(int);
 | 
			
		||||
            maxscaleSetStatusCap(2); // QC_QUERY_PARTIALLY_PARSED, see query_classifier.h:qc_parse_result
 | 
			
		||||
            ++i; // Next after the trailing '/'
 | 
			
		||||
          }
 | 
			
		||||
          else {
 | 
			
		||||
            // A non-version specific executable comment, e.g. "/*! select 1 */ => always parsed.
 | 
			
		||||
            // A non-version specific executable comment,
 | 
			
		||||
            // e.g."/*! select 1 */ or "/*M! select 1 */ => always parsed.
 | 
			
		||||
            char* znc = (char*) z;
 | 
			
		||||
            znc[0]=znc[1]=znc[2]=znc[i-1]=znc[i]=' '; // Remove comment chars, i.e. "/*!" and "*/".
 | 
			
		||||
            for (i=3; sqlite3Isspace(z[i]); ++i){} // Jump over any space.
 | 
			
		||||
            if (j==4){
 | 
			
		||||
              znc[3]=0; // It wasn't "/*!" but "/*M!".
 | 
			
		||||
            }
 | 
			
		||||
            for (i=j; sqlite3Isspace(z[i]); ++i){} // Jump over any space.
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user