Merge branch '2.0' into develop
This commit is contained in:
commit
9e4ee0323d
@ -11,7 +11,9 @@
|
||||
as JSON objects (beta level functionality).
|
||||
|
||||
For more details, please refer to:
|
||||
* [MariaDB MaxScale 2.0.2 Release Notes](Release-Notes/MaxScale-2.0.2-Release-Notes.md)
|
||||
* [MariaDB MaxScale 2.0.1 Release Notes](Release-Notes/MaxScale-2.0.1-Release-Notes.md)
|
||||
* [MariaDB MaxScale 2.0.0 Release Notes](Release-Notes/MaxScale-2.0.0-Release-Notes.md)
|
||||
|
||||
## MariaDB MaxScale 1.4
|
||||
* Authentication now allows table level resolution of grants. MaxScale service
|
||||
|
@ -833,6 +833,14 @@ This example configuration requires all connections to this server to be encrypt
|
||||
|
||||
The listener defines a port and protocol pair that is used to listen for connections to a service. A service may have multiple listeners associated with it, either to support multiple protocols or multiple ports. As with other elements of the configuration the section name is the listener name and it can be selected freely. A type parameter is used to identify the section as a listener definition. Address is optional and it allows the user to limit connections to certain interface only. Socket is also optional and used for Unix socket connections.
|
||||
|
||||
The network socket where the listener listens will have a backlog of
|
||||
connections. The size of this backlog is controlled by the
|
||||
net.ipv4.tcp_max_syn_backlog and net.core.somaxconn kernel parameters.
|
||||
|
||||
Increasing the size of the backlog by modifying the kernel parameters
|
||||
helps with sudden connection spikes and rejected connections. For more
|
||||
information see [listen(2)](http://man7.org/linux/man-pages/man2/listen.2.html).
|
||||
|
||||
```
|
||||
[<Listener name>]
|
||||
type=listener
|
||||
|
@ -520,6 +520,10 @@ The status bit that can be controlled are
|
||||
<td>maintenance</td>
|
||||
<td>The server is in maintenance mode. In this mode no new connections will be established to the server. The monitors will also not monitor servers that are in maintenance mode.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>stale</td>
|
||||
<td>The server is a stale master server. Read [MySQL Monitor](../Monitors/MySQL-Monitor.md) documentation for more details.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
62
Documentation/Release-Notes/MaxScale-2.0.2-Release-Notes.md
Normal file
62
Documentation/Release-Notes/MaxScale-2.0.2-Release-Notes.md
Normal file
@ -0,0 +1,62 @@
|
||||
# MariaDB MaxScale 2.0.2 Release Notes
|
||||
|
||||
Release 2.0.2 is a GA release.
|
||||
|
||||
This document describes the changes in release 2.0.2, when compared to
|
||||
release [2.0.1](MaxScale-2.0.1-Release-Notes.md).
|
||||
|
||||
If you are upgrading from release 1.4.4, please also read the release
|
||||
notes of release [2.0.0](./MaxScale-2.0.0-Release-Notes.md) and
|
||||
release [2.0.1](./MaxScale-2.0.1-Release-Notes.md).
|
||||
|
||||
For any problems you encounter, please submit a bug report at
|
||||
[Jira](https://jira.mariadb.org).
|
||||
|
||||
## Updated Features
|
||||
|
||||
### [MXS-978] (https://jira.mariadb.org/browse/MXS-978) Support for stale master in case of restart
|
||||
|
||||
In case where replication monitor gets a stale master status (slaves are down)
|
||||
and maxscale gets restarted, master loses the stale master status and no writes
|
||||
can happen.
|
||||
|
||||
To cater for this situation there is now a `set server <name> stale` command.
|
||||
|
||||
## Bug fixes
|
||||
|
||||
[Here is a list of bugs fixed since the release of MaxScale 2.0.1.](https://jira.mariadb.org/browse/MXS-976?jql=project%20%3D%20MXS%20AND%20issuetype%20%3D%20Bug%20AND%20status%20%3D%20Closed%20AND%20fixVersion%20%3D%202.0.2)
|
||||
|
||||
* [MXS-1018](https://jira.mariadb.org/browse/MXS-1018): Internal connections don't use TLS
|
||||
* [MXS-976](https://jira.mariadb.org/browse/MXS-976): Crash in libqc_sqlite
|
||||
* [MXS-975](https://jira.mariadb.org/browse/MXS-975): TCP backlog is capped at 1280
|
||||
* [MXS-970](https://jira.mariadb.org/browse/MXS-970): A fatal problem with maxscale automatically shut down
|
||||
* [MXS-969](https://jira.mariadb.org/browse/MXS-969): use_sql_variables_in=master can break functionality of important session variables
|
||||
* [MXS-967](https://jira.mariadb.org/browse/MXS-967): setting connection_timeout=value cause error : Not a boolean value
|
||||
* [MXS-965](https://jira.mariadb.org/browse/MXS-965): galeramon erlaubt keine TLS verschlüsselte Verbindung
|
||||
* [MXS-960](https://jira.mariadb.org/browse/MXS-960): MaxScale Binlog Server does not allow comma to be in password
|
||||
* [MXS-957](https://jira.mariadb.org/browse/MXS-957): Temporary table creation from another temporary table isn't detected
|
||||
* [MXS-955](https://jira.mariadb.org/browse/MXS-955): MaxScale 2.0.1 doesn't recognize user and passwd options in .maxadmin file
|
||||
* [MXS-953](https://jira.mariadb.org/browse/MXS-953): Charset error when server configued in utf8mb4
|
||||
* [MXS-942](https://jira.mariadb.org/browse/MXS-942): describe table query not routed to shard that contains the schema
|
||||
* [MXS-917](https://jira.mariadb.org/browse/MXS-917): False error message about master not being in use
|
||||
|
||||
## Known Issues and Limitations
|
||||
|
||||
There are some limitations and known issues within this version of MaxScale.
|
||||
For more information, please refer to the [Limitations](../About/Limitations.md) document.
|
||||
|
||||
## Packaging
|
||||
|
||||
RPM and Debian packages are provided for the Linux distributions supported
|
||||
by MariaDB Enterprise.
|
||||
|
||||
Packages can be downloaded [here](https://mariadb.com/resources/downloads).
|
||||
|
||||
## Source Code
|
||||
|
||||
The source code of MaxScale is tagged at GitHub with a tag, which is derived
|
||||
from the version of MaxScale. For instance, the tag of version `X.Y.Z` of MaxScale
|
||||
is `maxscale-X.Y.Z`.
|
||||
|
||||
The source code is available [here](https://github.com/mariadb-corporation/MaxScale).
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
set(MAXSCALE_VERSION_MAJOR "2" CACHE STRING "Major version")
|
||||
set(MAXSCALE_VERSION_MINOR "0" CACHE STRING "Minor version")
|
||||
set(MAXSCALE_VERSION_PATCH "1" CACHE STRING "Patch version")
|
||||
set(MAXSCALE_VERSION_PATCH "2" CACHE STRING "Patch version")
|
||||
|
||||
# This should only be incremented if a package is rebuilt
|
||||
set(MAXSCALE_BUILD_NUMBER 1 CACHE STRING "Release number")
|
||||
|
@ -134,6 +134,7 @@ typedef enum skygw_chk_t
|
||||
((t) == QUERY_TYPE_UNKNOWN ? "QUERY_TYPE_UNKNOWN" : \
|
||||
((t) == QUERY_TYPE_LOCAL_READ ? "QUERY_TYPE_LOCAL_READ" : \
|
||||
((t) == QUERY_TYPE_MASTER_READ ? "QUERY_TYPE_MASTER_READ" : \
|
||||
((t) == QUERY_TYPE_USERVAR_WRITE ? "QUERY_TYPE_USERVAR_WRITE" : \
|
||||
((t) == QUERY_TYPE_USERVAR_READ ? "QUERY_TYPE_USERVAR_READ" : \
|
||||
((t) == QUERY_TYPE_SYSVAR_READ ? "QUERY_TYPE_SYSVAR_READ" : \
|
||||
((t) == QUERY_TYPE_GSYSVAR_READ ? "QUERY_TYPE_GSYSVAR_READ" : \
|
||||
@ -150,7 +151,7 @@ typedef enum skygw_chk_t
|
||||
((t) == QUERY_TYPE_READ_TMP_TABLE ? "QUERY_TYPE_READ_TMP_TABLE" : \
|
||||
((t) == QUERY_TYPE_SHOW_DATABASES ? "QUERY_TYPE_SHOW_DATABASES" : \
|
||||
((t) == QUERY_TYPE_SHOW_TABLES ? "QUERY_TYPE_SHOW_TABLES" : \
|
||||
"Unknown query type"))))))))))))))))))))))
|
||||
"Unknown query type")))))))))))))))))))))))
|
||||
|
||||
#define STRLOGPRIORITYNAME(n)\
|
||||
((n) == LOG_EMERG ? "LOG_EMERG" : \
|
||||
|
@ -33,8 +33,7 @@ typedef enum qc_query_type
|
||||
QUERY_TYPE_WRITE = 0x000004, /*< Master data will be modified:master */
|
||||
QUERY_TYPE_MASTER_READ = 0x000008, /*< Read from the master:master */
|
||||
QUERY_TYPE_SESSION_WRITE = 0x000010, /*< Session data will be modified:master or all */
|
||||
/** Not implemented yet */
|
||||
//QUERY_TYPE_USERVAR_WRITE = 0x000020, /*< Write a user variable:master or all */
|
||||
QUERY_TYPE_USERVAR_WRITE = 0x000020, /*< Write a user variable:master or all */
|
||||
QUERY_TYPE_USERVAR_READ = 0x000040, /*< Read a user variable:master or any */
|
||||
QUERY_TYPE_SYSVAR_READ = 0x000080, /*< Read a system variable:master or any */
|
||||
/** Not implemented yet */
|
||||
|
@ -89,7 +89,7 @@ typedef struct parsing_info_st
|
||||
static THD* get_or_create_thd_for_parsing(MYSQL* mysql, char* query_str);
|
||||
static unsigned long set_client_flags(MYSQL* mysql);
|
||||
static bool create_parse_tree(THD* thd);
|
||||
static uint32_t resolve_query_type(THD* thd);
|
||||
static uint32_t resolve_query_type(parsing_info_t*, THD* thd);
|
||||
static bool skygw_stmt_causes_implicit_commit(LEX* lex, int* autocommit_stmt);
|
||||
|
||||
static int is_autocommit_stmt(LEX* lex);
|
||||
@ -169,7 +169,7 @@ uint32_t qc_get_type(GWBUF* querybuf)
|
||||
/** Find out the query type */
|
||||
if (mysql != NULL)
|
||||
{
|
||||
qtype = resolve_query_type((THD *) mysql->thd);
|
||||
qtype = resolve_query_type(pi, (THD *) mysql->thd);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -437,9 +437,102 @@ return_here:
|
||||
return failp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sniff whether the statement is
|
||||
*
|
||||
* SET ROLE ...
|
||||
* SET NAMES ...
|
||||
* SET PASSWORD ...
|
||||
* SET CHARACTER ...
|
||||
*
|
||||
* Depending on what kind of SET statement it is, the parser of the embedded
|
||||
* library creates instances of set_var_user, set_var, set_var_password,
|
||||
* set_var_role, etc. that all are derived from set_var_base. However, there
|
||||
* is no type-information available in set_var_base, which is the type of the
|
||||
* instances when accessed from the lexer. Consequently, we cannot know what
|
||||
* kind of statment it is based on that, only whether it is a system variable
|
||||
* or not.
|
||||
*
|
||||
* Consequently, we just look at the string and deduce whether it is a
|
||||
* set [ROLE|NAMES|PASSWORD|CHARACTER] statement.
|
||||
*/
|
||||
bool is_set_specific(const char* s)
|
||||
{
|
||||
bool rv = false;
|
||||
|
||||
// Remove space from the beginning.
|
||||
while (isspace(*s))
|
||||
{
|
||||
++s;
|
||||
}
|
||||
|
||||
const char* token = s;
|
||||
|
||||
// Find next non-space character.
|
||||
while (!isspace(*s) && (*s != 0))
|
||||
{
|
||||
++s;
|
||||
}
|
||||
|
||||
if (s - token == 3) // Might be "set"
|
||||
{
|
||||
if (strncasecmp(token, "set", 3) == 0)
|
||||
{
|
||||
// YES it was!
|
||||
while (isspace(*s))
|
||||
{
|
||||
++s;
|
||||
}
|
||||
|
||||
token = s;
|
||||
|
||||
while (!isspace(*s) && (*s != 0) && (*s != '='))
|
||||
{
|
||||
++s;
|
||||
}
|
||||
|
||||
if (s - token == 4) // Might be "role"
|
||||
{
|
||||
if (strncasecmp(token, "role", 4) == 0)
|
||||
{
|
||||
// YES it was!
|
||||
rv = true;
|
||||
}
|
||||
}
|
||||
else if (s - token == 5) // Might be "names"
|
||||
{
|
||||
if (strncasecmp(token, "names", 5) == 0)
|
||||
{
|
||||
// YES it was!
|
||||
rv = true;
|
||||
}
|
||||
}
|
||||
else if (s - token == 8) // Might be "password
|
||||
{
|
||||
if (strncasecmp(token, "password", 8) == 0)
|
||||
{
|
||||
// YES it was!
|
||||
rv = true;
|
||||
}
|
||||
}
|
||||
else if (s - token == 9) // Might be "character"
|
||||
{
|
||||
if (strncasecmp(token, "character", 9) == 0)
|
||||
{
|
||||
// YES it was!
|
||||
rv = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect query type by examining parsed representation of it.
|
||||
*
|
||||
* @param pi The parsing info.
|
||||
* @param thd MariaDB thread context.
|
||||
*
|
||||
* @return Copy of query type value.
|
||||
@ -451,7 +544,7 @@ return_here:
|
||||
* the resulting type may be different.
|
||||
*
|
||||
*/
|
||||
static uint32_t resolve_query_type(THD* thd)
|
||||
static uint32_t resolve_query_type(parsing_info_t *pi, THD* thd)
|
||||
{
|
||||
qc_query_type_t qtype = QUERY_TYPE_UNKNOWN;
|
||||
uint32_t type = QUERY_TYPE_UNKNOWN;
|
||||
@ -567,7 +660,33 @@ static uint32_t resolve_query_type(THD* thd)
|
||||
else if (lex->sql_command == SQLCOM_SET_OPTION)
|
||||
{
|
||||
/** Either user- or system variable write */
|
||||
type |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
if (is_set_specific(pi->pi_query_plain_str))
|
||||
{
|
||||
type |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
List_iterator<set_var_base> ilist(lex->var_list);
|
||||
size_t n = 0;
|
||||
|
||||
while (set_var_base *var = ilist++)
|
||||
{
|
||||
if (var->is_system())
|
||||
{
|
||||
type |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
type |= QUERY_TYPE_USERVAR_WRITE;
|
||||
}
|
||||
++n;
|
||||
}
|
||||
|
||||
if (n == 0)
|
||||
{
|
||||
type |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
goto return_qtype;
|
||||
@ -822,12 +941,7 @@ static uint32_t resolve_query_type(THD* thd)
|
||||
|
||||
/** User-defined variable modification */
|
||||
case Item_func::SUSERVAR_FUNC:
|
||||
/**
|
||||
* Really it is user variable but we
|
||||
* don't separate sql variables atm.
|
||||
* 15.9.14
|
||||
*/
|
||||
func_qtype |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
func_qtype |= QUERY_TYPE_USERVAR_WRITE;
|
||||
MXS_DEBUG("%lu [resolve_query_type] "
|
||||
"functype SUSERVAR_FUNC, user "
|
||||
"variable write.",
|
||||
@ -1504,6 +1618,15 @@ char** qc_get_database_names(GWBUF* querybuf, int* size)
|
||||
|
||||
while (tbl)
|
||||
{
|
||||
if (lex->sql_command == SQLCOM_SHOW_FIELDS)
|
||||
{
|
||||
// If we are describing, we want the actual table, not the information_schema.
|
||||
if (tbl->schema_select_lex)
|
||||
{
|
||||
tbl = tbl->schema_select_lex->table_list.first;
|
||||
}
|
||||
}
|
||||
|
||||
// The database is sometimes an empty string. So as not to return
|
||||
// an array of empty strings, we need to check for that possibility.
|
||||
if ((strcmp(tbl->db, "skygw_virtual") != 0) && (*tbl->db != 0))
|
||||
|
@ -82,6 +82,7 @@ typedef struct qc_sqlite_info
|
||||
QC_FIELD_INFO *field_infos; // Pointer to array of QC_FIELD_INFOs.
|
||||
size_t field_infos_len; // The used entries in field_infos.
|
||||
size_t field_infos_capacity; // The capacity of the field_infos array.
|
||||
bool initializing; // Whether we are initializing sqlite3.
|
||||
} QC_SQLITE_INFO;
|
||||
|
||||
typedef enum qc_log_level
|
||||
@ -134,7 +135,6 @@ static QC_SQLITE_INFO* info_alloc(void);
|
||||
static void info_finish(QC_SQLITE_INFO* info);
|
||||
static void info_free(QC_SQLITE_INFO* info);
|
||||
static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info);
|
||||
static bool is_submitted_query(const QC_SQLITE_INFO* info, const Parse* pParse);
|
||||
static void log_invalid_data(GWBUF* query, const char* message);
|
||||
static bool parse_query(GWBUF* query);
|
||||
static void parse_query_string(const char* query, size_t len);
|
||||
@ -369,6 +369,7 @@ static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info)
|
||||
info->field_infos = NULL;
|
||||
info->field_infos_len = 0;
|
||||
info->field_infos_capacity = 0;
|
||||
info->initializing = false;
|
||||
|
||||
return info;
|
||||
}
|
||||
@ -563,76 +564,6 @@ static bool query_is_parsed(GWBUF* query)
|
||||
return query && GWBUF_IS_PARSED(query);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that the statement being reported about is the one that initially was
|
||||
* submitted to parse_query_string(...). When sqlite3 is parsing other statements
|
||||
* it may (right after it's used for the first time) parse selects of its own.
|
||||
* We need to detect that, so that the internal stuff is not allowed to interfere
|
||||
* with the parsing of the provided statement.
|
||||
*
|
||||
* @param info The thread specific info structure.
|
||||
* @param pParse Sqlite3's parse context.
|
||||
*
|
||||
* @return True, if the current callback relates to the provided query,
|
||||
* false otherwise.
|
||||
*/
|
||||
static bool is_submitted_query(const QC_SQLITE_INFO* info, const Parse* pParse)
|
||||
{
|
||||
bool rv = false;
|
||||
|
||||
if (*pParse->zTail == 0)
|
||||
{
|
||||
// Everything has been consumed => the callback relates to the statement
|
||||
// that was provided to parse_query_string(...).
|
||||
rv = true;
|
||||
}
|
||||
else if (pParse->sLastToken.z && (*pParse->sLastToken.z == ';'))
|
||||
{
|
||||
// A ';' has been reached, i.e. everything has been consumed => the callback
|
||||
// relates to the statement that was provided to parse_query_string(...).
|
||||
rv = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If info->query contains a trailing ';', pParse->zTail may or may not contain
|
||||
// one also. We need to cater for the situation that the former has one and the
|
||||
// latter does not.
|
||||
const char* i = info->query;
|
||||
const char* end = i + info->query_len;
|
||||
const char* j = pParse->zTail;
|
||||
|
||||
// Walk forward as long as neither has reached the end,
|
||||
// and the characters are the same.
|
||||
while ((i < end) && (*j != 0) && (*i == *j))
|
||||
{
|
||||
++i;
|
||||
++j;
|
||||
}
|
||||
|
||||
if (i == end)
|
||||
{
|
||||
// If i has reached the end, then if j also has reached the end,
|
||||
// both are the same. In this case both either have or have not
|
||||
// a trailing ';'.
|
||||
rv = (*j == 0);
|
||||
}
|
||||
else if (*j == 0)
|
||||
{
|
||||
// Else if j has reached the end, then if i points to ';', both
|
||||
// are the same. In this case info->query contains a trailing ';'
|
||||
// while pParse->zTail does not.
|
||||
rv = (*i == ';');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise they are not the same.
|
||||
rv = false;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs information about invalid data.
|
||||
*
|
||||
@ -906,8 +837,7 @@ static void update_field_infos(QC_SQLITE_INFO* info,
|
||||
{
|
||||
if ((prev_token == TK_EQ) && (pos == QC_TOKEN_LEFT))
|
||||
{
|
||||
// Yes, QUERY_TYPE_USERVAR_WRITE is currently not available.
|
||||
info->types |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
info->types |= QUERY_TYPE_USERVAR_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1454,7 +1384,7 @@ void mxs_sqlite3EndTable(Parse *pParse, /* Parse context */
|
||||
QC_SQLITE_INFO* info = this_thread.info;
|
||||
ss_dassert(info);
|
||||
|
||||
if (is_submitted_query(info, pParse))
|
||||
if (!info->initializing)
|
||||
{
|
||||
if (pSelect)
|
||||
{
|
||||
@ -1553,10 +1483,7 @@ int mxs_sqlite3Select(Parse* pParse, Select* p, SelectDest* pDest)
|
||||
QC_SQLITE_INFO* info = this_thread.info;
|
||||
ss_dassert(info);
|
||||
|
||||
// Check whether the statement being parsed is the one that was passed
|
||||
// to sqlite3_prepare in parse_query_string(). During inserts, sqlite may
|
||||
// parse selects of its own.
|
||||
if (is_submitted_query(info, pParse))
|
||||
if (!info->initializing)
|
||||
{
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->operation = QUERY_OP_SELECT;
|
||||
@ -1585,7 +1512,7 @@ void mxs_sqlite3StartTable(Parse *pParse, /* Parser context */
|
||||
QC_SQLITE_INFO* info = this_thread.info;
|
||||
ss_dassert(info);
|
||||
|
||||
if (is_submitted_query(info, pParse))
|
||||
if (!info->initializing)
|
||||
{
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->operation = QUERY_OP_CREATE;
|
||||
@ -1834,7 +1761,7 @@ void maxscaleExplain(Parse* pParse, SrcList* pName)
|
||||
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->types = QUERY_TYPE_READ;
|
||||
update_names(info, "information_schema", "COLUMNS");
|
||||
update_names(info, pName->a[0].zDatabase, pName->a[0].zName);
|
||||
uint32_t u = QC_USED_IN_SELECT;
|
||||
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_DEFAULT", u, NULL);
|
||||
update_field_info(info, "information_schema", "COLUMNS", "COLUMN_KEY", u, NULL);
|
||||
@ -2259,6 +2186,7 @@ void maxscaleSet(Parse* pParse, int scope, mxs_set_t kind, ExprList* pList)
|
||||
ss_dassert(info);
|
||||
|
||||
info->status = QC_QUERY_PARSED;
|
||||
info->types = 0; // Reset what was set in maxscaleKeyword
|
||||
|
||||
switch (kind)
|
||||
{
|
||||
@ -2276,10 +2204,6 @@ void maxscaleSet(Parse* pParse, int scope, mxs_set_t kind, ExprList* pList)
|
||||
|
||||
case MXS_SET_VARIABLES:
|
||||
{
|
||||
// TODO: qc_mysqlembedded sets this bit on, without checking what
|
||||
// TODO: kind of variable it is.
|
||||
info->types = QUERY_TYPE_GSYSVAR_WRITE;
|
||||
|
||||
for (int i = 0; i < pList->nExpr; ++i)
|
||||
{
|
||||
const struct ExprList_item* pItem = &pList->a[i];
|
||||
@ -2288,19 +2212,48 @@ void maxscaleSet(Parse* pParse, int scope, mxs_set_t kind, ExprList* pList)
|
||||
{
|
||||
case TK_CHARACTER:
|
||||
case TK_NAMES:
|
||||
info->types |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
break;
|
||||
|
||||
case TK_EQ:
|
||||
{
|
||||
const Expr* pEq = pItem->pExpr;
|
||||
const Expr* pVariable = pEq->pLeft;
|
||||
const Expr* pVariable;
|
||||
const Expr* pValue = pEq->pRight;
|
||||
|
||||
// pVariable is either TK_DOT, TK_VARIABLE or TK_ID. If it's TK_DOT,
|
||||
// then pVariable->pLeft is either TK_VARIABLE or TK_ID and pVariable->pRight
|
||||
// pEq->pLeft is either TK_DOT, TK_VARIABLE or TK_ID. If it's TK_DOT,
|
||||
// then pEq->pLeft->pLeft is either TK_VARIABLE or TK_ID and pEq->pLeft->pRight
|
||||
// is either TK_DOT, TK_VARIABLE or TK_ID.
|
||||
|
||||
// Find the left-most part.
|
||||
pVariable = pEq->pLeft;
|
||||
while (pVariable->op == TK_DOT)
|
||||
{
|
||||
pVariable = pVariable->pLeft;
|
||||
ss_dassert(pVariable);
|
||||
}
|
||||
|
||||
// Check what kind of variable it is.
|
||||
size_t n_at = 0;
|
||||
const char* zName = pVariable->u.zToken;
|
||||
|
||||
while (*zName == '@')
|
||||
{
|
||||
++n_at;
|
||||
++zName;
|
||||
}
|
||||
|
||||
if (n_at == 1)
|
||||
{
|
||||
info->types |= QUERY_TYPE_USERVAR_WRITE;
|
||||
}
|
||||
else
|
||||
{
|
||||
info->types |= QUERY_TYPE_GSYSVAR_WRITE;
|
||||
}
|
||||
|
||||
// Set pVariable to point to the rightmost part of the name.
|
||||
pVariable = pEq->pLeft;
|
||||
while (pVariable->op == TK_DOT)
|
||||
{
|
||||
pVariable = pVariable->pRight;
|
||||
@ -2308,54 +2261,59 @@ void maxscaleSet(Parse* pParse, int scope, mxs_set_t kind, ExprList* pList)
|
||||
|
||||
ss_dassert((pVariable->op == TK_VARIABLE) || (pVariable->op == TK_ID));
|
||||
|
||||
const char* zName = pVariable->u.zToken;
|
||||
|
||||
while (*zName == '@')
|
||||
if (n_at != 1)
|
||||
{
|
||||
++zName;
|
||||
}
|
||||
// If it's not a user-variable we need to check whether it might
|
||||
// be 'autocommit'.
|
||||
const char* zName = pVariable->u.zToken;
|
||||
|
||||
// As pVariable points to the rightmost part, we'll catch both
|
||||
// "autocommit" and "@@global.autocommit".
|
||||
if (strcasecmp(zName, "autocommit") == 0)
|
||||
{
|
||||
int enable = -1;
|
||||
|
||||
switch (pValue->op)
|
||||
while (*zName == '@')
|
||||
{
|
||||
case TK_INTEGER:
|
||||
if (pValue->u.iValue == 1)
|
||||
{
|
||||
enable = 1;
|
||||
}
|
||||
else if (pValue->u.iValue == 0)
|
||||
{
|
||||
enable = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case TK_ID:
|
||||
enable = string_to_truth(pValue->u.zToken);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
++zName;
|
||||
}
|
||||
|
||||
switch (enable)
|
||||
// As pVariable points to the rightmost part, we'll catch both
|
||||
// "autocommit" and "@@global.autocommit".
|
||||
if (strcasecmp(zName, "autocommit") == 0)
|
||||
{
|
||||
case 0:
|
||||
info->types |= QUERY_TYPE_BEGIN_TRX;
|
||||
info->types |= QUERY_TYPE_DISABLE_AUTOCOMMIT;
|
||||
break;
|
||||
int enable = -1;
|
||||
|
||||
case 1:
|
||||
info->types |= QUERY_TYPE_ENABLE_AUTOCOMMIT;
|
||||
info->types |= QUERY_TYPE_COMMIT;
|
||||
break;
|
||||
switch (pValue->op)
|
||||
{
|
||||
case TK_INTEGER:
|
||||
if (pValue->u.iValue == 1)
|
||||
{
|
||||
enable = 1;
|
||||
}
|
||||
else if (pValue->u.iValue == 0)
|
||||
{
|
||||
enable = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
case TK_ID:
|
||||
enable = string_to_truth(pValue->u.zToken);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (enable)
|
||||
{
|
||||
case 0:
|
||||
info->types |= QUERY_TYPE_BEGIN_TRX;
|
||||
info->types |= QUERY_TYPE_DISABLE_AUTOCOMMIT;
|
||||
break;
|
||||
|
||||
case 1:
|
||||
info->types |= QUERY_TYPE_ENABLE_AUTOCOMMIT;
|
||||
info->types |= QUERY_TYPE_COMMIT;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2705,14 +2663,13 @@ static bool qc_sqlite_init(const char* args)
|
||||
|
||||
if (sqlite3_initialize() == 0)
|
||||
{
|
||||
init_builtin_functions();
|
||||
|
||||
this_unit.initialized = true;
|
||||
this_unit.log_level = log_level;
|
||||
|
||||
if (qc_sqlite_thread_init())
|
||||
{
|
||||
init_builtin_functions();
|
||||
|
||||
this_unit.log_level = log_level;
|
||||
|
||||
if (log_level != QC_LOG_NOTHING)
|
||||
{
|
||||
const char* message;
|
||||
@ -2780,6 +2737,36 @@ static bool qc_sqlite_thread_init(void)
|
||||
|
||||
MXS_INFO("In-memory sqlite database successfully opened for thread %lu.",
|
||||
(unsigned long) pthread_self());
|
||||
|
||||
QC_SQLITE_INFO* info = info_alloc();
|
||||
|
||||
if (info)
|
||||
{
|
||||
this_thread.info = info;
|
||||
|
||||
// With this statement we cause sqlite3 to initialize itself, so that it
|
||||
// is not done as part of the actual classification of data.
|
||||
const char* s = "CREATE TABLE __maxscale__internal__ (int field UNIQUE)";
|
||||
size_t len = strlen(s);
|
||||
|
||||
this_thread.info->query = s;
|
||||
this_thread.info->query_len = len;
|
||||
this_thread.info->initializing = true;
|
||||
parse_query_string(s, len);
|
||||
this_thread.info->initializing = false;
|
||||
this_thread.info->query = NULL;
|
||||
this_thread.info->query_len = 0;
|
||||
|
||||
info_free(this_thread.info);
|
||||
this_thread.info = NULL;
|
||||
|
||||
this_thread.initialized = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sqlite3_close(this_thread.db);
|
||||
this_thread.db = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -17,9 +17,16 @@ if (BUILD_QC_MYSQLEMBEDDED)
|
||||
endif()
|
||||
|
||||
add_executable(classify classify.c)
|
||||
add_executable(compare compare.cc)
|
||||
target_link_libraries(classify maxscale-common)
|
||||
|
||||
add_executable(compare compare.cc)
|
||||
target_link_libraries(compare maxscale-common)
|
||||
|
||||
add_executable(crash_qc_sqlite crash_qc_sqlite.c)
|
||||
target_link_libraries(crash_qc_sqlite maxscale-common)
|
||||
|
||||
add_test(TestQC_Crash_qcsqlite crash_qc_sqlite)
|
||||
|
||||
add_test(TestQC_MySQLEmbedded classify qc_mysqlembedded ${CMAKE_CURRENT_SOURCE_DIR}/input.sql ${CMAKE_CURRENT_SOURCE_DIR}/expected.sql)
|
||||
add_test(TestQC_SqLite classify qc_sqlite ${CMAKE_CURRENT_SOURCE_DIR}/input.sql ${CMAKE_CURRENT_SOURCE_DIR}/expected.sql)
|
||||
|
||||
|
66
query_classifier/test/crash_qc_sqlite.c
Normal file
66
query_classifier/test/crash_qc_sqlite.c
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
* 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/bsl.
|
||||
*
|
||||
* Change Date: 2019-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 <stdio.h>
|
||||
#include <maxscale/buffer.h>
|
||||
#include <maxscale/gwdirs.h>
|
||||
#include <maxscale/query_classifier.h>
|
||||
|
||||
#define MYSQL_HEADER_LEN 4
|
||||
|
||||
GWBUF* create_gwbuf(const char* s, size_t len)
|
||||
{
|
||||
size_t payload_len = len + 1;
|
||||
size_t gwbuf_len = MYSQL_HEADER_LEN + payload_len;
|
||||
|
||||
GWBUF* gwbuf = gwbuf_alloc(gwbuf_len);
|
||||
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf))) = payload_len;
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 1)) = (payload_len >> 8);
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 2)) = (payload_len >> 16);
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 3)) = 0x00;
|
||||
*((unsigned char*)((char*)GWBUF_DATA(gwbuf) + 4)) = 0x03;
|
||||
memcpy((char*)GWBUF_DATA(gwbuf) + 5, s, len);
|
||||
|
||||
return gwbuf;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int rv = EXIT_FAILURE;
|
||||
|
||||
set_libdir(strdup("../qc_sqlite"));
|
||||
|
||||
if (qc_init("qc_sqlite", NULL))
|
||||
{
|
||||
const char s[] = "SELECT @@global.max_allowed_packet";
|
||||
|
||||
GWBUF *stmt = create_gwbuf(s, sizeof(s)); // Include superfluous NULL.
|
||||
|
||||
// In 2.0.1 this crashed due to is_submitted_query() in qc_sqlite.c
|
||||
// being of the opinion that the statement was not the one to be
|
||||
// classified and hence an alien parse-tree being passed to sqlite3's
|
||||
// code generator.
|
||||
qc_parse(stmt);
|
||||
|
||||
qc_end();
|
||||
|
||||
rv = EXIT_SUCCESS;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "error: Could not load query classifier.");
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
@ -4,7 +4,6 @@ QUERY_TYPE_WRITE
|
||||
QUERY_TYPE_WRITE
|
||||
QUERY_TYPE_WRITE|QUERY_TYPE_COMMIT
|
||||
QUERY_TYPE_WRITE|QUERY_TYPE_CREATE_TMP_TABLE
|
||||
QUERY_TYPE_GSYSVAR_WRITE
|
||||
QUERY_TYPE_READ|QUERY_TYPE_SYSVAR_READ
|
||||
QUERY_TYPE_READ|QUERY_TYPE_USERVAR_READ
|
||||
QUERY_TYPE_GSYSVAR_WRITE|QUERY_TYPE_ENABLE_AUTOCOMMIT|QUERY_TYPE_COMMIT
|
||||
|
@ -4,7 +4,6 @@ insert into tst values ("Jane","Doe"),("Daisy","Duck"),("Marie","Curie");
|
||||
update tst set fname="Farmer", lname="McDonald" where lname="%Doe" and fname="John";
|
||||
create table tmp as select * from t1;
|
||||
create temporary table tmp as select * from t1;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
select @@server_id;
|
||||
select @OLD_SQL_NOTES;
|
||||
SET autocommit=1;
|
||||
|
@ -23,5 +23,14 @@ SET @x:= (SELECT h FROM t1 WHERE (a,b,c,d,e,f,g)=(1,2,3,4,5,6,7));
|
||||
|
||||
insert into t1 values (2, 2) on duplicate key update data= data + 10;
|
||||
# Problem: warning: [qc_sqlite] Statement was only partially parsed (Sqlite3 error: SQL logic error
|
||||
# or missing database, near "on": syntax error): "insert into t1 values (2, 2) on duplicate
|
||||
# key update data= data + 10;"
|
||||
# or missing database, near "on": syntax error): "insert into t1 values (2, 2) on duplicate
|
||||
# key update data= data + 10;"
|
||||
|
||||
SET @`a b`='hello';
|
||||
set @`test`=1;
|
||||
set @"tEST"=3;
|
||||
set @`TeST`=4;
|
||||
# warning: qc_sqlite: Statement was classified only based on keywords
|
||||
# (Sqlite3 error: SQL logic error or missing database, unrecognized token: "@"): "set @=4"
|
||||
#
|
||||
# sqlite3GetToken needs to be modified to accept a quoted variable name.
|
||||
|
@ -271,7 +271,8 @@ SET timestamp=UNIX_TIMESTAMP('2014-09-30 08:00:00');
|
||||
SET ROLE role_1;
|
||||
SET ROLE NONE;
|
||||
SET @sum=0;
|
||||
SET @old_debug= @@session.debug;
|
||||
# MXS Embedded parser is not aware of the 'debug' variable.
|
||||
# MXS SET @old_debug= @@session.debug;
|
||||
set debug_dbug='+d,send_kill_after_delete';
|
||||
set debug_dbug=@old_debug;
|
||||
set local sql_mode="";
|
||||
@ -398,7 +399,8 @@ SET collation_connection=gb2312_chinese_ci;
|
||||
set names gb2312;
|
||||
set collation_connection=gb2312_bin;
|
||||
SET NAMES gbk;
|
||||
SET @`tcontent`:=_binary 0x50434B000900000000000000E9000000 COLLATE `binary`/*!*/;
|
||||
# MXSTODO qc_sqlite can not parse quoted variables.
|
||||
# MXSTODO SET @`tcontent`:=_binary 0x50434B000900000000000000E9000000 COLLATE `binary`/*!*/;
|
||||
SET @test_character_set= 'gbk';
|
||||
SET @test_collation= 'gbk_chinese_ci';
|
||||
SET NAMES gbk;
|
||||
@ -1487,7 +1489,8 @@ set global event_scheduler=on;
|
||||
set global event_scheduler=off;
|
||||
set global event_scheduler=original;
|
||||
set global event_scheduler=on;
|
||||
SET @event_scheduler=@@global.event_scheduler;
|
||||
# MXS Embedded parser is not aware of the 'global.event_scheduler' variable.
|
||||
# MXS SET @event_scheduler=@@global.event_scheduler;
|
||||
SET GLOBAL event_scheduler=OFF;
|
||||
SET GLOBAL event_scheduler=OFF;
|
||||
SET GLOBAL event_scheduler=1;
|
||||
@ -1500,7 +1503,8 @@ SET GLOBAL event_scheduler=2;
|
||||
SET GLOBAL event_scheduler=5;
|
||||
SET GLOBAL event_scheduler=ON;
|
||||
SET GLOBAL event_scheduler=@event_scheduler;
|
||||
SET @old_event_scheduler=@@event_scheduler;
|
||||
# MXS Embedded parser is not aware of the 'global.event_scheduler' variable.
|
||||
# MXS SET @old_event_scheduler=@@event_scheduler;
|
||||
SET GLOBAL event_scheduler=on;
|
||||
SET GLOBAL event_scheduler=off;
|
||||
SET GLOBAL event_scheduler=on;
|
||||
@ -1574,7 +1578,8 @@ set time_zone= @@global.time_zone;
|
||||
set @a:=0;
|
||||
SET @xml='<a aa1="aa1" aa2="aa2">a1<b ba1="ba1">b1<c>c1</c>b2</b>a2</a>';
|
||||
SET sql_mode=STRICT_TRANS_TABLES;
|
||||
SET @old_debug= @@session.debug;
|
||||
# MXS Embedded parser is not aware of the 'debug' variable.
|
||||
# MXS SET @old_debug= @@session.debug;
|
||||
SET session debug_dbug= '+d,alloc_sort_buffer_fail';
|
||||
SET session debug_dbug= @old_debug;
|
||||
SET DEBUG_SYNC='filesort_start SIGNAL filesort_started WAIT_FOR filesort_killed';
|
||||
@ -1587,7 +1592,8 @@ set global expire_logs_days = 0;
|
||||
SET AUTOCOMMIT=0;
|
||||
SET AUTOCOMMIT=1;
|
||||
set sql_mode="";
|
||||
SET @old_innodb_file_per_table= @@GLOBAL.innodb_file_per_table;
|
||||
# MXS Embedded parser is not aware of innodb.
|
||||
# MXS SET @old_innodb_file_per_table= @@GLOBAL.innodb_file_per_table;
|
||||
SET GLOBAL innodb_file_per_table= 1;
|
||||
SET @export = 10;
|
||||
SET GLOBAL innodb_file_per_table= @old_innodb_file_per_table;
|
||||
@ -2267,7 +2273,8 @@ set join_cache_level= @tmp_mdev5037;
|
||||
set @@join_cache_level= @save_join_cache_level;
|
||||
set storage_engine=@save_storage_engine;
|
||||
set optimizer_switch=@innodb_mrr_cpk_tmp;
|
||||
set @old_innodb_lock_wait_timeout=@@global.innodb_lock_wait_timeout;
|
||||
# MXS Embedded parser is not aware of innodb.
|
||||
# MXS SET set @old_innodb_lock_wait_timeout=@@global.innodb_lock_wait_timeout;
|
||||
set global innodb_lock_wait_timeout=300;
|
||||
set session innodb_lock_wait_timeout=300;
|
||||
set @@autocommit=0;
|
||||
@ -2414,8 +2421,10 @@ set @value= "1aa";
|
||||
set @value= "aa1";
|
||||
set @value= "1e+1111111111a";
|
||||
set @value= "-1e+1111111111a";
|
||||
set @value= 1e+1111111111;
|
||||
set @value= -1e+1111111111;
|
||||
# MXS ERROR 1367 (22007): Illegal double '1e+1111111111' value found during parsing
|
||||
# MXS set @value= 1e+1111111111;
|
||||
# MXS ERROR 1367 (22007): Illegal double '1e+1111111111' value found during parsing
|
||||
# MXS set @value= -1e+1111111111;
|
||||
set @value= 1e+111;
|
||||
set @value= -1e+111;
|
||||
set @value= 1;
|
||||
@ -3389,7 +3398,8 @@ SET DEBUG_SYNC= 'RESET';
|
||||
set @default_storage_engine= @@global.storage_engine;
|
||||
set global storage_engine=myisam;
|
||||
set session storage_engine=myisam;
|
||||
SET @orig_debug=@@debug;
|
||||
# MXS Embedded parser is not aware of the 'debug' variable.
|
||||
# MXS SET @orig_debug=@@debug;
|
||||
SET GLOBAL debug_dbug="+d,myisam_pretend_crashed_table_on_open";
|
||||
SET GLOBAL debug_dbug=@orig_debug;
|
||||
set global storage_engine=@default_storage_engine;
|
||||
@ -3416,7 +3426,8 @@ SET NAMES latin1;
|
||||
set autocommit=0;
|
||||
set autocommit=1;
|
||||
set @mrr_icp_extra_tmp=@@optimizer_switch;
|
||||
SET @aux = @@session.debug;
|
||||
# MXS Embedded parser is not aware of the 'debug' variable.
|
||||
# MXS SET @aux = @@session.debug;
|
||||
set @d=4;
|
||||
set sql_safe_updates=1;
|
||||
set sql_safe_updates=0;
|
||||
@ -3774,15 +3785,19 @@ SET GLOBAL general_log = 0;
|
||||
SET @@global.general_log = @old_general_log_state;
|
||||
SET @old_default_storage_engine = @@default_storage_engine;
|
||||
SET @@default_storage_engine = 'InnoDB';
|
||||
SET @save_innodb_stats_on_metadata=@@global.innodb_stats_on_metadata;
|
||||
# MXS Embedded parser is not aware of innodb.
|
||||
# MXS SET SET @save_innodb_stats_on_metadata=@@global.innodb_stats_on_metadata;
|
||||
SET @@global.innodb_stats_on_metadata=ON;
|
||||
SET @@global.innodb_stats_on_metadata=@save_innodb_stats_on_metadata;
|
||||
SET @@default_storage_engine = @old_default_storage_engine;
|
||||
set sql_mode="";
|
||||
set sql_mode=default;
|
||||
SET @old_innodb_file_format = @@global.innodb_file_format;
|
||||
SET @old_innodb_file_per_table = @@global.innodb_file_per_table;
|
||||
SET @old_innodb_strict_mode = @@global.innodb_strict_mode;
|
||||
# MXS Embedded parser is not aware of innodb.
|
||||
# MXS SET SET @old_innodb_file_format = @@global.innodb_file_format;
|
||||
# MXS Embedded parser is not aware of innodb.
|
||||
# MXS SET SET @old_innodb_file_per_table = @@global.innodb_file_per_table;
|
||||
# MXS Embedded parser is not aware of innodb.
|
||||
# MXS SET SET @old_innodb_strict_mode = @@global.innodb_strict_mode;
|
||||
SET @@global.innodb_file_format = Barracuda,
|
||||
@@global.innodb_file_per_table = ON,
|
||||
@@global.innodb_strict_mode = ON;
|
||||
@ -3817,8 +3832,10 @@ SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
|
||||
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;
|
||||
set global default_storage_engine='innodb';
|
||||
set session default_storage_engine='innodb';
|
||||
SET @old_innodb_thread_concurrency := @@innodb_thread_concurrency;
|
||||
SET @old_innodb_thread_sleep_delay := @@innodb_thread_sleep_delay;
|
||||
# MXS Embedded parser is not aware of innodb.
|
||||
# MXS SET SET @old_innodb_thread_concurrency := @@innodb_thread_concurrency;
|
||||
# MXS Embedded parser is not aware of innodb.
|
||||
# MXS SET SET @old_innodb_thread_sleep_delay := @@innodb_thread_sleep_delay;
|
||||
SET GLOBAL innodb_thread_concurrency = 1;
|
||||
SET GLOBAL innodb_thread_concurrency = @old_innodb_thread_concurrency;
|
||||
SET GLOBAL innodb_thread_sleep_delay = @old_innodb_thread_sleep_delay;
|
||||
@ -4825,7 +4842,8 @@ SET optimizer_switch=@save_optimizer_switch;
|
||||
SET @pass='my_pw';
|
||||
SET @wrong='incorrect';
|
||||
set sql_mode="";
|
||||
set @save_debug_dbug= @@debug_dbug;
|
||||
# MXS ERROR 1193 (HY000): Unknown system variable 'debug_dbug'
|
||||
# MXS set @save_debug_dbug= @@debug_dbug;
|
||||
set @@debug_dbug= @save_debug_dbug;
|
||||
set @@debug_dbug= @save_debug_dbug;
|
||||
set gtid_domain_id = 10;
|
||||
@ -4943,7 +4961,8 @@ SET NAMES latin1;
|
||||
SET NAMES latin1;
|
||||
SET NAMES utf8;
|
||||
SET NAMES latin1;
|
||||
SET @old_debug= @@session.debug;
|
||||
# MXS Embedded parser is not aware of the 'debug' variable.
|
||||
# MXS SET @old_debug= @@session.debug;
|
||||
set debug_sync='RESET';
|
||||
set debug_dbug='+d,show_explain_probe_delete_exec_start';
|
||||
set @show_explain_probe_select_id=1;
|
||||
@ -4954,9 +4973,11 @@ set debug_sync='RESET';
|
||||
set @show_explain_probe_select_id=1;
|
||||
set debug_dbug='d,show_explain_probe_join_exec_start';
|
||||
set debug_dbug='';
|
||||
SET @old_debug= @@session.debug;
|
||||
# MXS Embedded parser is not aware of the 'debug' variable.
|
||||
# MXS SET @old_debug= @@session.debug;
|
||||
set debug_sync='RESET';
|
||||
SET @old_debug= @@session.debug;
|
||||
# MXS Embedded parser is not aware of the 'debug' variable.
|
||||
# MXS SET @old_debug= @@session.debug;
|
||||
set @show_explain_probe_select_id=1;
|
||||
set debug_dbug='+d,show_explain_probe_join_exec_start';
|
||||
set @show_expl_tmp= @@optimizer_switch;
|
||||
@ -5169,7 +5190,8 @@ set @@max_sp_recursion_depth= 20;
|
||||
set @@max_sp_recursion_depth= 0;
|
||||
SET sql_mode=ONLY_FULL_GROUP_BY;
|
||||
SET @lock_wait_timeout_saved= @@lock_wait_timeout;
|
||||
SET @innodb_lock_wait_timeout_saved= @@innodb_lock_wait_timeout;
|
||||
# MXS Embedded parser is not aware of innodb.
|
||||
# MXS SET SET @innodb_lock_wait_timeout_saved= @@innodb_lock_wait_timeout;
|
||||
SET @@lock_wait_timeout= 1;
|
||||
SET @@innodb_lock_wait_timeout= 1;
|
||||
SET AUTOCOMMIT= 0;
|
||||
@ -5815,7 +5837,8 @@ set @@optimizer_switch= default;
|
||||
set optimizer_switch='subquery_cache=on';
|
||||
SET optimizer_switch=@save_optimizer_switch;
|
||||
set @@optimizer_switch= default;
|
||||
SET @orig_debug=@@debug;
|
||||
# MXS Embedded parser is not aware of the 'debug' variable.
|
||||
# MXS SET @orig_debug=@@debug;
|
||||
SET GLOBAL debug_dbug="d,subselect_exec_fail";
|
||||
SET GLOBAL debug_dbug=@orig_debug;
|
||||
set @subselect_mat_cost=@@optimizer_switch;
|
||||
@ -6886,7 +6909,8 @@ set @@autocommit=0;
|
||||
set @@autocommit=1;
|
||||
set @@global.general_log=@save_general_log;
|
||||
SET TIMESTAMP=10000;
|
||||
SET @`a b`='hello';
|
||||
# MXSTODO qc_sqlite can not parse quoted variables.
|
||||
# MXSTODO SET @`a b`='hello';
|
||||
set @var1= "';aaa";
|
||||
SET @var2=char(ascii('a'));
|
||||
set @a := foo;
|
||||
@ -6899,7 +6923,8 @@ set @a=_latin2'test';
|
||||
set @a=_latin2'test' collate latin2_general_ci;
|
||||
set @var= NULL ;
|
||||
set @v1=null, @v2=1, @v3=1.1, @v4=now();
|
||||
set session @honk=99;
|
||||
# 'set session @honk = 99' is not legal.
|
||||
# MXS set session @honk=99;
|
||||
set @first_var= NULL;
|
||||
set @first_var= cast(NULL as signed integer);
|
||||
set @first_var= NULL;
|
||||
@ -6928,7 +6953,8 @@ SET @aux = NULL;
|
||||
SET @bug12408412=1;
|
||||
SET @var=NULL;
|
||||
set @var= repeat('a',20000);
|
||||
set @my_slave_net_timeout =@@global.slave_net_timeout;
|
||||
# MXS Embedded parser is not aware of the 'global.slave_net_timeout' variable.
|
||||
# MXS SET set @my_slave_net_timeout =@@global.slave_net_timeout;
|
||||
set global slave_net_timeout=100;
|
||||
set global sql_slave_skip_counter=100;
|
||||
set global slave_net_timeout=default;
|
||||
@ -6987,10 +7013,13 @@ set @my_max_allowed_packet =@@global.max_allowed_packet;
|
||||
set @my_delay_key_write =@@global.delay_key_write;
|
||||
set @my_join_buffer_size =@@global.join_buffer_size;
|
||||
set @my_log_warnings =@@global.log_warnings;
|
||||
set @`test`=1;
|
||||
# MXSTODO qc_sqlite can not parse quoted variables.
|
||||
# MXSTODO set @`test`=1;
|
||||
set @TEST=2;
|
||||
set @"tEST"=3;
|
||||
set @`TeST`=4;
|
||||
# MXSTODO qc_sqlite can not parse quoted variables.
|
||||
# MXSTODO set @"tEST"=3;
|
||||
# MXSTODO qc_sqlite can not parse quoted variables.
|
||||
# MXSTODO set @`TeST`=4;
|
||||
set @select=2,@t5=1.23456;
|
||||
set @test_int=10,@test_double=1e-10,@test_string="abcdeghi",@test_string2="abcdefghij",@select=NULL;
|
||||
set @test_int="hello",@test_double="hello",@test_string="hello",@test_string2="hello";
|
||||
|
@ -3294,7 +3294,14 @@ dcb_listen(DCB *listener, const char *config, const char *protocol_name)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(listener_socket, 10 * SOMAXCONN) != 0)
|
||||
/**
|
||||
* The use of INT_MAX for backlog length in listen() allows the end-user to
|
||||
* control the backlog length with the net.ipv4.tcp_max_syn_backlog kernel
|
||||
* option since the parameter is silently truncated to the configured value.
|
||||
*
|
||||
* @see man 2 listen
|
||||
*/
|
||||
if (listen(listener_socket, INT_MAX) != 0)
|
||||
{
|
||||
char errbuf[MXS_STRERROR_BUFLEN];
|
||||
MXS_ERROR("Failed to start listening on '%s' with protocol '%s': %d, %s",
|
||||
|
@ -456,8 +456,14 @@ struct type_name_info type_to_type_name_info(qc_query_type_t type)
|
||||
}
|
||||
break;
|
||||
|
||||
/** Not implemented yet */
|
||||
//case QUERY_TYPE_USERVAR_WRITE:
|
||||
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";
|
||||
@ -615,8 +621,7 @@ static const qc_query_type_t QUERY_TYPES[] =
|
||||
QUERY_TYPE_WRITE,
|
||||
QUERY_TYPE_MASTER_READ,
|
||||
QUERY_TYPE_SESSION_WRITE,
|
||||
/** Not implemented yet */
|
||||
//QUERY_TYPE_USERVAR_WRITE,
|
||||
QUERY_TYPE_USERVAR_WRITE,
|
||||
QUERY_TYPE_USERVAR_READ,
|
||||
QUERY_TYPE_SYSVAR_READ,
|
||||
/** Not implemented yet */
|
||||
|
@ -1031,6 +1031,7 @@ static struct
|
||||
{ "ndb", SERVER_NDB },
|
||||
{ "maintenance", SERVER_MAINT },
|
||||
{ "maint", SERVER_MAINT },
|
||||
{ "stale", SERVER_STALE_STATUS },
|
||||
{ NULL, 0 }
|
||||
};
|
||||
|
||||
|
@ -156,4 +156,8 @@ null_auth_is_client_ssl_capable(DCB *dcb)
|
||||
* @param dcb Request handler DCB connected to the client
|
||||
*/
|
||||
static void
|
||||
null_auth_free_client_data(DCB *dcb) {}
|
||||
null_auth_free_client_data(DCB *dcb)
|
||||
{
|
||||
free(dcb->data);
|
||||
dcb->data = NULL;
|
||||
}
|
||||
|
@ -40,9 +40,9 @@
|
||||
#include <maxscale/log_manager.h>
|
||||
#include <maxscale/resultset.h>
|
||||
|
||||
/* @see function load_module in load_utils.c for explanation of the following
|
||||
* lint directives.
|
||||
*/
|
||||
/* @see function load_module in load_utils.c for explanation of the following
|
||||
* lint directives.
|
||||
*/
|
||||
/*lint -e14 */
|
||||
MODULE_INFO info =
|
||||
{
|
||||
@ -148,9 +148,9 @@ static int httpd_read_event(DCB* dcb)
|
||||
SESSION *session = dcb->session;
|
||||
|
||||
int numchars = 1;
|
||||
char buf[HTTPD_REQUESTLINE_MAXLEN-1] = "";
|
||||
char buf[HTTPD_REQUESTLINE_MAXLEN - 1] = "";
|
||||
char *query_string = NULL;
|
||||
char method[HTTPD_METHOD_MAXLEN-1] = "";
|
||||
char method[HTTPD_METHOD_MAXLEN - 1] = "";
|
||||
char url[HTTPD_SMALL_BUFFER] = "";
|
||||
size_t i, j;
|
||||
int headers_read = 0;
|
||||
@ -166,11 +166,13 @@ static int httpd_read_event(DCB* dcb)
|
||||
|
||||
numchars = httpd_get_line(dcb->fd, buf, sizeof(buf));
|
||||
|
||||
i = 0; j = 0;
|
||||
i = 0;
|
||||
j = 0;
|
||||
while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
|
||||
{
|
||||
method[i] = buf[j];
|
||||
i++; j++;
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
method[i] = '\0';
|
||||
|
||||
@ -193,7 +195,8 @@ static int httpd_read_event(DCB* dcb)
|
||||
while ((j < sizeof(buf) - 1) && !ISspace(buf[j]) && (i < sizeof(url) - 1))
|
||||
{
|
||||
url[i] = buf[j];
|
||||
i++; j++;
|
||||
i++;
|
||||
j++;
|
||||
}
|
||||
|
||||
url[i] = '\0';
|
||||
@ -233,7 +236,7 @@ static int httpd_read_event(DCB* dcb)
|
||||
{
|
||||
*value = '\0';
|
||||
value++;
|
||||
end = &value[strlen(value) -1];
|
||||
end = &value[strlen(value) - 1];
|
||||
*end = '\0';
|
||||
|
||||
if (strncasecmp(buf, "Hostname", 6) == 0)
|
||||
|
@ -801,8 +801,8 @@ typedef void *(*STATSFUNC)();
|
||||
*/
|
||||
static struct
|
||||
{
|
||||
char *name;
|
||||
int type;
|
||||
char *name;
|
||||
int type;
|
||||
STATSFUNC func;
|
||||
} variables[] =
|
||||
{
|
||||
@ -820,10 +820,9 @@ static struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int index;
|
||||
int index;
|
||||
char *like;
|
||||
} VARCONTEXT;
|
||||
|
||||
/**
|
||||
* Callback function to populate rows of the show variable
|
||||
* command
|
||||
@ -860,10 +859,14 @@ variable_row(RESULTSET *result, void *data)
|
||||
(long)(*variables[context->index].func)());
|
||||
resultset_row_set(row, 1, buf);
|
||||
break;
|
||||
default:
|
||||
ss_dassert(!true);
|
||||
}
|
||||
context->index++;
|
||||
return row;
|
||||
}
|
||||
// We only get to this point once all variables have been printed
|
||||
MXS_FREE(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -909,15 +912,19 @@ RESULTSET *
|
||||
maxinfo_variables()
|
||||
{
|
||||
RESULTSET *result;
|
||||
static VARCONTEXT context;
|
||||
|
||||
context.like = NULL;
|
||||
context.index = 0;
|
||||
|
||||
if ((result = resultset_create(variable_row, &context)) == NULL)
|
||||
VARCONTEXT *context;
|
||||
if ((context = MXS_MALLOC(sizeof(VARCONTEXT))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
context->like = NULL;
|
||||
context->index = 0;
|
||||
|
||||
if ((result = resultset_create(variable_row, context)) == NULL)
|
||||
{
|
||||
MXS_FREE(context);
|
||||
return NULL;
|
||||
}
|
||||
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);
|
||||
resultset_add_column(result, "Value", 40, COL_TYPE_VARCHAR);
|
||||
return result;
|
||||
@ -1138,10 +1145,14 @@ status_row(RESULTSET *result, void *data)
|
||||
(long)(*status[context->index].func)());
|
||||
resultset_row_set(row, 1, buf);
|
||||
break;
|
||||
default:
|
||||
ss_dassert(!true);
|
||||
}
|
||||
context->index++;
|
||||
return row;
|
||||
}
|
||||
// We only get to this point once all status elements have been printed
|
||||
MXS_FREE(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1186,16 +1197,20 @@ exec_show_status(DCB *dcb, MAXINFO_TREE *filter)
|
||||
RESULTSET *
|
||||
maxinfo_status()
|
||||
{
|
||||
RESULTSET *result;
|
||||
static VARCONTEXT context;
|
||||
|
||||
context.like = NULL;
|
||||
context.index = 0;
|
||||
|
||||
if ((result = resultset_create(status_row, &context)) == NULL)
|
||||
RESULTSET *result;
|
||||
VARCONTEXT *context;
|
||||
if ((context = MXS_MALLOC(sizeof(VARCONTEXT))) == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
context->like = NULL;
|
||||
context->index = 0;
|
||||
|
||||
if ((result = resultset_create(status_row, context)) == NULL)
|
||||
{
|
||||
MXS_FREE(context);
|
||||
return NULL;
|
||||
}
|
||||
resultset_add_column(result, "Variable_name", 40, COL_TYPE_VARCHAR);
|
||||
resultset_add_column(result, "Value", 40, COL_TYPE_VARCHAR);
|
||||
return result;
|
||||
@ -1220,14 +1235,13 @@ exec_select(DCB *dcb, MAXINFO_TREE *tree)
|
||||
*
|
||||
* @param pattern Pattern to match
|
||||
* @param str String to match against pattern
|
||||
* @return Zero on match
|
||||
* @return Zero on match
|
||||
*/
|
||||
static int
|
||||
maxinfo_pattern_match(char *pattern, char *str)
|
||||
{
|
||||
int anchor = 0, len, trailing;
|
||||
char *fixed;
|
||||
extern char *strcasestr();
|
||||
|
||||
if (*pattern != '%')
|
||||
{
|
||||
|
@ -718,7 +718,7 @@ return_succp:
|
||||
* if the query would otherwise be routed to slave.
|
||||
*/
|
||||
route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
qc_query_type_t qtype, HINT *hint)
|
||||
qc_query_type_t qtype, HINT *hint)
|
||||
{
|
||||
bool trx_active = rses->rses_transaction_active;
|
||||
bool load_active = rses->rses_load_active;
|
||||
@ -735,9 +735,10 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
*/
|
||||
else if (!load_active &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
||||
/** Configured to allow writing variables to all nodes */
|
||||
/** Configured to allow writing user variables to all nodes */
|
||||
(use_sql_variables_in == TYPE_ALL &&
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE)) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_USERVAR_WRITE)) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE) ||
|
||||
/** enable or disable autocommit are always routed to all */
|
||||
qc_query_is_type(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT)))
|
||||
@ -777,44 +778,29 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
* Hints may affect on routing of the following queries
|
||||
*/
|
||||
else if (!trx_active && !load_active &&
|
||||
!qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) &&
|
||||
!qc_query_is_type(qtype, QUERY_TYPE_WRITE) &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_READ) || /*< any SELECT */
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || /*< 'SHOW TABLES' */
|
||||
qc_query_is_type(qtype,
|
||||
QUERY_TYPE_USERVAR_READ) || /*< read user var */
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) || /*< read sys var */
|
||||
qc_query_is_type(qtype,
|
||||
QUERY_TYPE_EXEC_STMT) || /*< prepared stmt exec */
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) ||
|
||||
qc_query_is_type(qtype,
|
||||
QUERY_TYPE_GSYSVAR_READ))) /*< read global sys var */
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ)))
|
||||
{
|
||||
/** First set expected targets before evaluating hints */
|
||||
if (!qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || /*< 'SHOW TABLES' */
|
||||
/** Configured to allow reading variables from slaves */
|
||||
(use_sql_variables_in == TYPE_ALL &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ)))))
|
||||
if (qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ))
|
||||
{
|
||||
if (use_sql_variables_in == TYPE_ALL)
|
||||
{
|
||||
target = TARGET_SLAVE;
|
||||
}
|
||||
}
|
||||
else if (qc_query_is_type(qtype, QUERY_TYPE_READ) || // Normal read
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SHOW_TABLES) || // SHOW TABLES
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ) || // System variable
|
||||
qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_READ)) // Global system variable
|
||||
{
|
||||
target = TARGET_SLAVE;
|
||||
}
|
||||
|
||||
if (qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_NAMED_STMT) ||
|
||||
/** Configured not to allow reading variables from slaves */
|
||||
(use_sql_variables_in == TYPE_MASTER &&
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SYSVAR_READ))))
|
||||
{
|
||||
target = TARGET_MASTER;
|
||||
}
|
||||
|
||||
/** If nothing matches then choose the master */
|
||||
if ((target & (TARGET_ALL | TARGET_SLAVE | TARGET_MASTER)) == 0)
|
||||
{
|
||||
@ -823,8 +809,7 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
}
|
||||
else
|
||||
{
|
||||
/** hints don't affect on routing */
|
||||
ss_dassert(trx_active ||
|
||||
ss_dassert(trx_active || load_active ||
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_WRITE) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_MASTER_READ) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_SESSION_WRITE) ||
|
||||
@ -836,6 +821,8 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
use_sql_variables_in == TYPE_MASTER) ||
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_GSYSVAR_WRITE) &&
|
||||
use_sql_variables_in == TYPE_MASTER) ||
|
||||
(qc_query_is_type(qtype, QUERY_TYPE_USERVAR_WRITE) &&
|
||||
use_sql_variables_in == TYPE_MASTER) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_BEGIN_TRX) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_ENABLE_AUTOCOMMIT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_DISABLE_AUTOCOMMIT) ||
|
||||
@ -844,7 +831,10 @@ route_target_t get_route_target(ROUTER_CLIENT_SES *rses,
|
||||
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_CREATE_TMP_TABLE) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_READ_TMP_TABLE) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_UNKNOWN)));
|
||||
qc_query_is_type(qtype, QUERY_TYPE_UNKNOWN)) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_EXEC_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_STMT) ||
|
||||
qc_query_is_type(qtype, QUERY_TYPE_PREPARE_NAMED_STMT));
|
||||
target = TARGET_MASTER;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user