
Minor cleanup: - Unit variables places in anonymous namespace. - Unit variables accessed via this_unit struct. This is the first commit of many where the mapping from canonical statement to query classification result is introduced. The mapping is placed above the actual query classifier, so that all query classifiers will benefit from it. The mapping will be maintained by thread so that there will not be any synchronization issues. Further, initially a simple map without upper boundary will be used; if this is found to provide measurable benefits, the map will then be replaced with an LRU mechanism so that it becomes possible to specify just how much memory the mapping may use.
843 lines
18 KiB
C++
843 lines
18 KiB
C++
/*
|
|
* 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: 2022-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 "internal/query_classifier.h"
|
|
#include <maxscale/log_manager.h>
|
|
#include <maxscale/modutil.h>
|
|
#include <maxscale/alloc.h>
|
|
#include <maxscale/platform.h>
|
|
#include <maxscale/pcre2.h>
|
|
#include <maxscale/utils.h>
|
|
|
|
#include "internal/modules.h"
|
|
#include "internal/trxboundaryparser.hh"
|
|
|
|
//#define QC_TRACE_ENABLED
|
|
#undef QC_TRACE_ENABLED
|
|
|
|
#if defined(QC_TRACE_ENABLED)
|
|
#define QC_TRACE() MXS_NOTICE(__func__)
|
|
#else
|
|
#define QC_TRACE()
|
|
#endif
|
|
|
|
namespace
|
|
{
|
|
|
|
struct type_name_info
|
|
{
|
|
const char* name;
|
|
size_t name_len;
|
|
};
|
|
|
|
const char DEFAULT_QC_NAME[] = "qc_sqlite";
|
|
const char QC_TRX_PARSE_USING[] = "QC_TRX_PARSE_USING";
|
|
|
|
struct this_unit
|
|
{
|
|
QUERY_CLASSIFIER* classifier;
|
|
qc_trx_parse_using_t qc_trx_parse_using;
|
|
} this_unit =
|
|
{
|
|
nullptr,
|
|
QC_TRX_PARSE_USING_PARSER
|
|
};
|
|
|
|
}
|
|
|
|
|
|
bool qc_setup(const char* plugin_name, qc_sql_mode_t sql_mode, const char* plugin_args)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(!this_unit.classifier);
|
|
|
|
if (!plugin_name || (*plugin_name == 0))
|
|
{
|
|
MXS_NOTICE("No query classifier specified, using default '%s'.", DEFAULT_QC_NAME);
|
|
plugin_name = DEFAULT_QC_NAME;
|
|
}
|
|
|
|
int32_t rv = QC_RESULT_ERROR;
|
|
this_unit.classifier = qc_load(plugin_name);
|
|
|
|
if (this_unit.classifier)
|
|
{
|
|
rv = this_unit.classifier->qc_setup(sql_mode, plugin_args);
|
|
|
|
if (rv != QC_RESULT_OK)
|
|
{
|
|
qc_unload(this_unit.classifier);
|
|
}
|
|
}
|
|
|
|
return (rv == QC_RESULT_OK) ? true : false;
|
|
}
|
|
|
|
bool qc_process_init(uint32_t kind)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
const char* parse_using = getenv(QC_TRX_PARSE_USING);
|
|
|
|
if (parse_using)
|
|
{
|
|
if (strcmp(parse_using, "QC_TRX_PARSE_USING_QC") == 0)
|
|
{
|
|
this_unit.qc_trx_parse_using = QC_TRX_PARSE_USING_QC;
|
|
MXS_NOTICE("Transaction detection using QC.");
|
|
}
|
|
else if (strcmp(parse_using, "QC_TRX_PARSE_USING_PARSER") == 0)
|
|
{
|
|
this_unit.qc_trx_parse_using = QC_TRX_PARSE_USING_PARSER;
|
|
MXS_NOTICE("Transaction detection using custom PARSER.");
|
|
}
|
|
else
|
|
{
|
|
MXS_NOTICE("QC_TRX_PARSE_USING set, but the value %s is not known. "
|
|
"Parsing using QC.", parse_using);
|
|
}
|
|
}
|
|
|
|
bool rc = qc_thread_init(QC_INIT_SELF);
|
|
|
|
if (rc)
|
|
{
|
|
if (kind & QC_INIT_PLUGIN)
|
|
{
|
|
rc = this_unit.classifier->qc_process_init() == 0;
|
|
|
|
if (!rc)
|
|
{
|
|
qc_thread_end(QC_INIT_SELF);
|
|
}
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void qc_process_end(uint32_t kind)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
if (kind & QC_INIT_PLUGIN)
|
|
{
|
|
this_unit.classifier->qc_process_end();
|
|
}
|
|
|
|
qc_thread_end(QC_INIT_SELF);
|
|
}
|
|
|
|
QUERY_CLASSIFIER* qc_load(const char* plugin_name)
|
|
{
|
|
void* module = load_module(plugin_name, MODULE_QUERY_CLASSIFIER);
|
|
|
|
if (module)
|
|
{
|
|
MXS_INFO("%s loaded.", plugin_name);
|
|
}
|
|
else
|
|
{
|
|
MXS_ERROR("Could not load %s.", plugin_name);
|
|
}
|
|
|
|
return (QUERY_CLASSIFIER*) module;
|
|
}
|
|
|
|
void qc_unload(QUERY_CLASSIFIER* classifier)
|
|
{
|
|
// TODO: The module loading/unloading needs an overhaul before we
|
|
// TODO: actually can unload something.
|
|
classifier = NULL;
|
|
}
|
|
|
|
bool qc_thread_init(uint32_t kind)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
bool rc = true;
|
|
|
|
if (kind & QC_INIT_PLUGIN)
|
|
{
|
|
rc = this_unit.classifier->qc_thread_init() == 0;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
void qc_thread_end(uint32_t kind)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
if (kind & QC_INIT_PLUGIN)
|
|
{
|
|
this_unit.classifier->qc_thread_end();
|
|
}
|
|
}
|
|
|
|
qc_parse_result_t qc_parse(GWBUF* query, uint32_t collect)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
int32_t result = QC_QUERY_INVALID;
|
|
|
|
this_unit.classifier->qc_parse(query, collect, &result);
|
|
|
|
return (qc_parse_result_t)result;
|
|
}
|
|
|
|
uint32_t qc_get_type_mask(GWBUF* query)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
uint32_t type_mask = QUERY_TYPE_UNKNOWN;
|
|
|
|
this_unit.classifier->qc_get_type_mask(query, &type_mask);
|
|
|
|
return type_mask;
|
|
}
|
|
|
|
qc_query_op_t qc_get_operation(GWBUF* query)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
int32_t op = QUERY_OP_UNDEFINED;
|
|
|
|
this_unit.classifier->qc_get_operation(query, &op);
|
|
|
|
return (qc_query_op_t)op;
|
|
}
|
|
|
|
char* qc_get_created_table_name(GWBUF* query)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
char* name = NULL;
|
|
|
|
this_unit.classifier->qc_get_created_table_name(query, &name);
|
|
|
|
return name;
|
|
}
|
|
|
|
bool qc_is_drop_table_query(GWBUF* query)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
int32_t is_drop_table = 0;
|
|
|
|
this_unit.classifier->qc_is_drop_table_query(query, &is_drop_table);
|
|
|
|
return (is_drop_table != 0) ? true : false;
|
|
}
|
|
|
|
char** qc_get_table_names(GWBUF* query, int* tblsize, bool fullnames)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
char** names = NULL;
|
|
*tblsize = 0;
|
|
|
|
this_unit.classifier->qc_get_table_names(query, fullnames, &names, tblsize);
|
|
|
|
return names;
|
|
}
|
|
|
|
char* qc_get_canonical(GWBUF* query)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
char *rval;
|
|
|
|
if (this_unit.classifier->qc_get_canonical)
|
|
{
|
|
this_unit.classifier->qc_get_canonical(query, &rval);
|
|
}
|
|
else
|
|
{
|
|
rval = modutil_get_canonical(query);
|
|
}
|
|
|
|
if (rval)
|
|
{
|
|
squeeze_whitespace(rval);
|
|
}
|
|
|
|
return rval;
|
|
}
|
|
|
|
bool qc_query_has_clause(GWBUF* query)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
int32_t has_clause = 0;
|
|
|
|
this_unit.classifier->qc_query_has_clause(query, &has_clause);
|
|
|
|
return (has_clause != 0) ? true : false;
|
|
}
|
|
|
|
void qc_get_field_info(GWBUF* query, const QC_FIELD_INFO** infos, size_t* n_infos)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
*infos = NULL;
|
|
|
|
uint32_t n = 0;
|
|
|
|
this_unit.classifier->qc_get_field_info(query, infos, &n);
|
|
|
|
*n_infos = n;
|
|
}
|
|
|
|
void qc_get_function_info(GWBUF* query, const QC_FUNCTION_INFO** infos, size_t* n_infos)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
*infos = NULL;
|
|
|
|
uint32_t n = 0;
|
|
|
|
this_unit.classifier->qc_get_function_info(query, infos, &n);
|
|
|
|
*n_infos = n;
|
|
}
|
|
|
|
char** qc_get_database_names(GWBUF* query, int* sizep)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
char** names = NULL;
|
|
*sizep = 0;
|
|
|
|
this_unit.classifier->qc_get_database_names(query, &names, sizep);
|
|
|
|
return names;
|
|
}
|
|
|
|
char* qc_get_prepare_name(GWBUF* query)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
char* name = NULL;
|
|
|
|
this_unit.classifier->qc_get_prepare_name(query, &name);
|
|
|
|
return name;
|
|
}
|
|
|
|
GWBUF* qc_get_preparable_stmt(GWBUF* stmt)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
GWBUF* preparable_stmt = NULL;
|
|
|
|
this_unit.classifier->qc_get_preparable_stmt(stmt, &preparable_stmt);
|
|
|
|
return preparable_stmt;
|
|
}
|
|
|
|
const char* qc_op_to_string(qc_query_op_t op)
|
|
{
|
|
switch (op)
|
|
{
|
|
case QUERY_OP_UNDEFINED:
|
|
return "QUERY_OP_UNDEFINED";
|
|
|
|
case QUERY_OP_ALTER:
|
|
return "QUERY_OP_ALTER";
|
|
|
|
case QUERY_OP_CALL:
|
|
return "QUERY_OP_CALL";
|
|
|
|
case QUERY_OP_CHANGE_DB:
|
|
return "QUERY_OP_CHANGE_DB";
|
|
|
|
case QUERY_OP_CREATE:
|
|
return "QUERY_OP_CREATE";
|
|
|
|
case QUERY_OP_DELETE:
|
|
return "QUERY_OP_DELETE";
|
|
|
|
case QUERY_OP_DROP:
|
|
return "QUERY_OP_DROP";
|
|
|
|
case QUERY_OP_EXPLAIN:
|
|
return "QUERY_OP_EXPLAIN";
|
|
|
|
case QUERY_OP_GRANT:
|
|
return "QUERY_OP_GRANT";
|
|
|
|
case QUERY_OP_INSERT:
|
|
return "QUERY_OP_INSERT";
|
|
|
|
case QUERY_OP_LOAD:
|
|
return "QUERY_OP_LOAD";
|
|
|
|
case QUERY_OP_LOAD_LOCAL:
|
|
return "QUERY_OP_LOAD_LOCAL";
|
|
|
|
case QUERY_OP_REVOKE:
|
|
return "QUERY_OP_REVOKE";
|
|
|
|
case QUERY_OP_SELECT:
|
|
return "QUERY_OP_SELECT";
|
|
|
|
case QUERY_OP_SHOW:
|
|
return "QUERY_OP_SHOW";
|
|
|
|
case QUERY_OP_TRUNCATE:
|
|
return "QUERY_OP_TRUNCATE";
|
|
|
|
case QUERY_OP_UPDATE:
|
|
return "QUERY_OP_UPDATE";
|
|
|
|
default:
|
|
return "UNKNOWN_QUERY_OP";
|
|
}
|
|
}
|
|
|
|
struct type_name_info type_to_type_name_info(qc_query_type_t type)
|
|
{
|
|
struct type_name_info info;
|
|
|
|
switch (type)
|
|
{
|
|
case QUERY_TYPE_UNKNOWN:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_UNKNOWN";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_LOCAL_READ:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_LOCAL_READ";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_READ:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_READ";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_WRITE:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_WRITE";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_MASTER_READ:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_MASTER_READ";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_SESSION_WRITE:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_SESSION_WRITE";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_USERVAR_WRITE:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_USERVAR_WRITE";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_USERVAR_READ:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_USERVAR_READ";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_SYSVAR_READ:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_SYSVAR_READ";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
/** Not implemented yet */
|
|
//case QUERY_TYPE_SYSVAR_WRITE:
|
|
case QUERY_TYPE_GSYSVAR_READ:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_GSYSVAR_READ";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_GSYSVAR_WRITE:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_GSYSVAR_WRITE";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_BEGIN_TRX:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_BEGIN_TRX";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_ENABLE_AUTOCOMMIT:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_ENABLE_AUTOCOMMIT";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_DISABLE_AUTOCOMMIT:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_DISABLE_AUTOCOMMIT";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_ROLLBACK:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_ROLLBACK";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_COMMIT:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_COMMIT";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_PREPARE_NAMED_STMT:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_PREPARE_NAMED_STMT";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_PREPARE_STMT:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_PREPARE_STMT";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_EXEC_STMT:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_EXEC_STMT";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_CREATE_TMP_TABLE:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_CREATE_TMP_TABLE";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_READ_TMP_TABLE:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_READ_TMP_TABLE";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_SHOW_DATABASES:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_SHOW_DATABASES";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_SHOW_TABLES:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_SHOW_TABLES";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
case QUERY_TYPE_DEALLOC_PREPARE:
|
|
{
|
|
static const char name[] = "QUERY_TYPE_DEALLOC_PREPARE";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
static const char name[] = "UNKNOWN_QUERY_TYPE";
|
|
info.name = name;
|
|
info.name_len = sizeof(name) - 1;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
|
|
const char* qc_type_to_string(qc_query_type_t type)
|
|
{
|
|
return type_to_type_name_info(type).name;
|
|
}
|
|
|
|
static const qc_query_type_t QUERY_TYPES[] =
|
|
{
|
|
/* Excluded by design */
|
|
//QUERY_TYPE_UNKNOWN,
|
|
QUERY_TYPE_LOCAL_READ,
|
|
QUERY_TYPE_READ,
|
|
QUERY_TYPE_WRITE,
|
|
QUERY_TYPE_MASTER_READ,
|
|
QUERY_TYPE_SESSION_WRITE,
|
|
QUERY_TYPE_USERVAR_WRITE,
|
|
QUERY_TYPE_USERVAR_READ,
|
|
QUERY_TYPE_SYSVAR_READ,
|
|
/** Not implemented yet */
|
|
//QUERY_TYPE_SYSVAR_WRITE,
|
|
QUERY_TYPE_GSYSVAR_READ,
|
|
QUERY_TYPE_GSYSVAR_WRITE,
|
|
QUERY_TYPE_BEGIN_TRX,
|
|
QUERY_TYPE_ENABLE_AUTOCOMMIT,
|
|
QUERY_TYPE_DISABLE_AUTOCOMMIT,
|
|
QUERY_TYPE_ROLLBACK,
|
|
QUERY_TYPE_COMMIT,
|
|
QUERY_TYPE_PREPARE_NAMED_STMT,
|
|
QUERY_TYPE_PREPARE_STMT,
|
|
QUERY_TYPE_EXEC_STMT,
|
|
QUERY_TYPE_CREATE_TMP_TABLE,
|
|
QUERY_TYPE_READ_TMP_TABLE,
|
|
QUERY_TYPE_SHOW_DATABASES,
|
|
QUERY_TYPE_SHOW_TABLES,
|
|
QUERY_TYPE_DEALLOC_PREPARE,
|
|
};
|
|
|
|
static const int N_QUERY_TYPES = sizeof(QUERY_TYPES) / sizeof(QUERY_TYPES[0]);
|
|
static const int QUERY_TYPE_MAX_LEN = 29; // strlen("QUERY_TYPE_PREPARE_NAMED_STMT");
|
|
|
|
char* qc_typemask_to_string(uint32_t types)
|
|
{
|
|
int len = 0;
|
|
|
|
// First calculate how much space will be needed.
|
|
for (int i = 0; i < N_QUERY_TYPES; ++i)
|
|
{
|
|
if (types & QUERY_TYPES[i])
|
|
{
|
|
if (len != 0)
|
|
{
|
|
++len; // strlen("|");
|
|
}
|
|
|
|
len += QUERY_TYPE_MAX_LEN;
|
|
}
|
|
}
|
|
|
|
++len;
|
|
|
|
// Then make one allocation and build the string.
|
|
char* s = (char*) MXS_MALLOC(len);
|
|
|
|
if (s)
|
|
{
|
|
if (len > 1)
|
|
{
|
|
char* p = s;
|
|
|
|
for (int i = 0; i < N_QUERY_TYPES; ++i)
|
|
{
|
|
qc_query_type_t type = QUERY_TYPES[i];
|
|
|
|
if (types & type)
|
|
{
|
|
if (p != s)
|
|
{
|
|
strcpy(p, "|");
|
|
++p;
|
|
}
|
|
|
|
struct type_name_info info = type_to_type_name_info(type);
|
|
|
|
strcpy(p, info.name);
|
|
p += info.name_len;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*s = 0;
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
static uint32_t qc_get_trx_type_mask_using_qc(GWBUF* stmt)
|
|
{
|
|
uint32_t type_mask = qc_get_type_mask(stmt);
|
|
|
|
if (qc_query_is_type(type_mask, QUERY_TYPE_WRITE) &&
|
|
qc_query_is_type(type_mask, QUERY_TYPE_COMMIT))
|
|
{
|
|
// This is a commit reported for "CREATE TABLE...",
|
|
// "DROP TABLE...", etc. that cause an implicit commit.
|
|
type_mask = 0;
|
|
}
|
|
else
|
|
{
|
|
// Only START TRANSACTION can be explicitly READ or WRITE.
|
|
if (!(type_mask & QUERY_TYPE_BEGIN_TRX))
|
|
{
|
|
// So, strip them away for everything else.
|
|
type_mask &= ~(QUERY_TYPE_WRITE | QUERY_TYPE_READ);
|
|
}
|
|
|
|
// Then leave only the bits related to transaction and
|
|
// autocommit state.
|
|
type_mask &= (QUERY_TYPE_BEGIN_TRX |
|
|
QUERY_TYPE_WRITE |
|
|
QUERY_TYPE_READ |
|
|
QUERY_TYPE_COMMIT |
|
|
QUERY_TYPE_ROLLBACK |
|
|
QUERY_TYPE_ENABLE_AUTOCOMMIT |
|
|
QUERY_TYPE_DISABLE_AUTOCOMMIT);
|
|
}
|
|
|
|
return type_mask;
|
|
}
|
|
|
|
static uint32_t qc_get_trx_type_mask_using_parser(GWBUF* stmt)
|
|
{
|
|
maxscale::TrxBoundaryParser parser;
|
|
|
|
return parser.type_mask_of(stmt);
|
|
}
|
|
|
|
uint32_t qc_get_trx_type_mask_using(GWBUF* stmt, qc_trx_parse_using_t use)
|
|
{
|
|
uint32_t type_mask = 0;
|
|
|
|
switch (use)
|
|
{
|
|
case QC_TRX_PARSE_USING_QC:
|
|
type_mask = qc_get_trx_type_mask_using_qc(stmt);
|
|
break;
|
|
|
|
case QC_TRX_PARSE_USING_PARSER:
|
|
type_mask = qc_get_trx_type_mask_using_parser(stmt);
|
|
break;
|
|
|
|
default:
|
|
ss_dassert(!true);
|
|
}
|
|
|
|
return type_mask;
|
|
}
|
|
|
|
uint32_t qc_get_trx_type_mask(GWBUF* stmt)
|
|
{
|
|
return qc_get_trx_type_mask_using(stmt, this_unit.qc_trx_parse_using);
|
|
}
|
|
|
|
void qc_set_server_version(uint64_t version)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
this_unit.classifier->qc_set_server_version(version);
|
|
}
|
|
|
|
uint64_t qc_get_server_version()
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
uint64_t version;
|
|
|
|
this_unit.classifier->qc_get_server_version(&version);
|
|
|
|
return version;
|
|
}
|
|
|
|
qc_sql_mode_t qc_get_sql_mode()
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
qc_sql_mode_t sql_mode;
|
|
|
|
ss_debug(int32_t rv = ) this_unit.classifier->qc_get_sql_mode(&sql_mode);
|
|
ss_dassert(rv == QC_RESULT_OK);
|
|
|
|
return sql_mode;
|
|
}
|
|
|
|
void qc_set_sql_mode(qc_sql_mode_t sql_mode)
|
|
{
|
|
QC_TRACE();
|
|
ss_dassert(this_unit.classifier);
|
|
|
|
ss_debug(int32_t rv = ) this_unit.classifier->qc_set_sql_mode(sql_mode);
|
|
ss_dassert(rv == QC_RESULT_OK);
|
|
}
|