Merge branch '2.3' into 2.4
This commit is contained in:
@ -1807,13 +1807,30 @@ int32_t qc_mysql_query_has_clause(GWBUF* buf, int32_t* has_clause)
|
|||||||
|
|
||||||
if (lex)
|
if (lex)
|
||||||
{
|
{
|
||||||
if (!lex->describe && !is_show_command(lex->sql_command))
|
int cmd = lex->sql_command;
|
||||||
|
|
||||||
|
if (!lex->describe
|
||||||
|
&& !is_show_command(cmd)
|
||||||
|
&& (cmd != SQLCOM_ALTER_PROCEDURE)
|
||||||
|
&& (cmd != SQLCOM_ALTER_TABLE)
|
||||||
|
&& (cmd != SQLCOM_CALL)
|
||||||
|
&& (cmd != SQLCOM_CREATE_PROCEDURE)
|
||||||
|
&& (cmd != SQLCOM_CREATE_TABLE)
|
||||||
|
&& (cmd != SQLCOM_DROP_FUNCTION)
|
||||||
|
&& (cmd != SQLCOM_DROP_PROCEDURE)
|
||||||
|
&& (cmd != SQLCOM_DROP_TABLE)
|
||||||
|
&& (cmd != SQLCOM_DROP_VIEW)
|
||||||
|
&& (cmd != SQLCOM_FLUSH)
|
||||||
|
&& (cmd != SQLCOM_ROLLBACK)
|
||||||
|
)
|
||||||
{
|
{
|
||||||
SELECT_LEX* current = lex->all_selects_list;
|
SELECT_LEX* current = lex->all_selects_list;
|
||||||
|
|
||||||
while (current && !*has_clause)
|
while (current && !*has_clause)
|
||||||
{
|
{
|
||||||
if (current->where || current->having)
|
if (current->where || current->having ||
|
||||||
|
((cmd == SQLCOM_SELECT || cmd == SQLCOM_DELETE || cmd == SQLCOM_UPDATE)
|
||||||
|
&& current->select_limit))
|
||||||
{
|
{
|
||||||
*has_clause = true;
|
*has_clause = true;
|
||||||
}
|
}
|
||||||
@ -2934,6 +2951,7 @@ typedef enum collect_source
|
|||||||
COLLECT_WHERE,
|
COLLECT_WHERE,
|
||||||
COLLECT_HAVING,
|
COLLECT_HAVING,
|
||||||
COLLECT_GROUP_BY,
|
COLLECT_GROUP_BY,
|
||||||
|
COLLECT_ORDER_BY
|
||||||
} collect_source_t;
|
} collect_source_t;
|
||||||
|
|
||||||
static void update_field_infos(parsing_info_t* pi,
|
static void update_field_infos(parsing_info_t* pi,
|
||||||
@ -2974,6 +2992,7 @@ static bool should_function_be_ignored(parsing_info_t* pi, const char* func_name
|
|||||||
|| (strcasecmp(func_name, "get_user_var") == 0)
|
|| (strcasecmp(func_name, "get_user_var") == 0)
|
||||||
|| (strcasecmp(func_name, "get_system_var") == 0)
|
|| (strcasecmp(func_name, "get_system_var") == 0)
|
||||||
|| (strcasecmp(func_name, "not") == 0)
|
|| (strcasecmp(func_name, "not") == 0)
|
||||||
|
|| (strcasecmp(func_name, "collate") == 0)
|
||||||
|| (strcasecmp(func_name, "set_user_var") == 0)
|
|| (strcasecmp(func_name, "set_user_var") == 0)
|
||||||
|| (strcasecmp(func_name, "set_system_var") == 0))
|
|| (strcasecmp(func_name, "set_system_var") == 0))
|
||||||
{
|
{
|
||||||
@ -3327,6 +3346,19 @@ static void update_field_infos(parsing_info_t* pi,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (select->order_list.first)
|
||||||
|
{
|
||||||
|
ORDER* order = select->order_list.first;
|
||||||
|
while (order)
|
||||||
|
{
|
||||||
|
Item* item = *order->item;
|
||||||
|
|
||||||
|
update_field_infos(pi, select, COLLECT_ORDER_BY, item, &select->item_list);
|
||||||
|
|
||||||
|
order = order->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (select->where)
|
if (select->where)
|
||||||
{
|
{
|
||||||
update_field_infos(pi,
|
update_field_infos(pi,
|
||||||
|
@ -87,7 +87,7 @@ static const char* BUILTIN_FUNCTIONS[] =
|
|||||||
"subtime",
|
"subtime",
|
||||||
"sysdate",
|
"sysdate",
|
||||||
"time",
|
"time",
|
||||||
"timediff"
|
"timediff",
|
||||||
"timestamp",
|
"timestamp",
|
||||||
"timestampadd",
|
"timestampadd",
|
||||||
"timestampdiff",
|
"timestampdiff",
|
||||||
|
@ -1264,6 +1264,18 @@ public:
|
|||||||
const ExprList* pExclude,
|
const ExprList* pExclude,
|
||||||
compound_approach_t compound_approach = ANALYZE_COMPOUND_SELECTS)
|
compound_approach_t compound_approach = ANALYZE_COMPOUND_SELECTS)
|
||||||
{
|
{
|
||||||
|
if (pSelect->pLimit)
|
||||||
|
{
|
||||||
|
// In case there is an ORDER BY statement without a LIMIT, which is
|
||||||
|
// not accepted by sqlite, a pseudo LIMIT with the value of -1 is injected.
|
||||||
|
// We need to detect that so as not to incorrectly claim that there is
|
||||||
|
// a clause. See maxscale_create_pseudo_limit() in parse.y.
|
||||||
|
if (pSelect->pLimit->op != TK_INTEGER || pSelect->pLimit->u.iValue != -1)
|
||||||
|
{
|
||||||
|
m_has_clause = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (pSelect->pSrc)
|
if (pSelect->pSrc)
|
||||||
{
|
{
|
||||||
const SrcList* pSrc = pSelect->pSrc;
|
const SrcList* pSrc = pSelect->pSrc;
|
||||||
@ -1330,6 +1342,14 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pSelect->pOrderBy)
|
||||||
|
{
|
||||||
|
update_field_infos_from_exprlist(&aliases,
|
||||||
|
context,
|
||||||
|
pSelect->pOrderBy,
|
||||||
|
pSelect->pEList);
|
||||||
|
}
|
||||||
|
|
||||||
if (pSelect->pWith)
|
if (pSelect->pWith)
|
||||||
{
|
{
|
||||||
update_field_infos_from_with(&aliases, context, pSelect->pWith);
|
update_field_infos_from_with(&aliases, context, pSelect->pWith);
|
||||||
@ -1872,6 +1892,9 @@ public:
|
|||||||
QcAliases aliases;
|
QcAliases aliases;
|
||||||
uint32_t context = 0;
|
uint32_t context = 0;
|
||||||
update_field_infos_from_select(aliases, context, pSelect, NULL);
|
update_field_infos_from_select(aliases, context, pSelect, NULL);
|
||||||
|
|
||||||
|
// Non-sensical to claim that a "CREATE ... SELECT ... WHERE ..." statement has a clause.
|
||||||
|
m_has_clause = false;
|
||||||
}
|
}
|
||||||
else if (pOldTable)
|
else if (pOldTable)
|
||||||
{
|
{
|
||||||
@ -2038,7 +2061,7 @@ public:
|
|||||||
m_type_mask = QUERY_TYPE_WRITE;
|
m_type_mask = QUERY_TYPE_WRITE;
|
||||||
m_operation = QUERY_OP_UPDATE;
|
m_operation = QUERY_OP_UPDATE;
|
||||||
update_names_from_srclist(&aliases, pTabList);
|
update_names_from_srclist(&aliases, pTabList);
|
||||||
m_has_clause = (pWhere ? true : false);
|
m_has_clause = ((pWhere && pWhere->op != TK_IN) ? true : false);
|
||||||
|
|
||||||
if (pChanges)
|
if (pChanges)
|
||||||
{
|
{
|
||||||
|
@ -135,6 +135,8 @@ extern void maxscaleUse(Parse*, Token*);
|
|||||||
extern void maxscale_update_function_info(const char* name, const Expr* pExpr);
|
extern void maxscale_update_function_info(const char* name, const Expr* pExpr);
|
||||||
extern void maxscale_set_type_mask(unsigned int type_mask);
|
extern void maxscale_set_type_mask(unsigned int type_mask);
|
||||||
|
|
||||||
|
static Expr* maxscale_create_pseudo_limit(Parse* pParse, Token* pToken, ExprSpan* pLimit);
|
||||||
|
|
||||||
// Exposed utility functions
|
// Exposed utility functions
|
||||||
void exposed_sqlite3ExprDelete(sqlite3 *db, Expr *pExpr)
|
void exposed_sqlite3ExprDelete(sqlite3 *db, Expr *pExpr)
|
||||||
{
|
{
|
||||||
@ -1528,12 +1530,14 @@ cmd ::= with(C) DELETE FROM fullname(X) indexed_opt(I) where_opt(W)
|
|||||||
sqlite3WithPush(pParse, C, 1);
|
sqlite3WithPush(pParse, C, 1);
|
||||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||||
#ifdef MAXSCALE
|
#ifdef MAXSCALE
|
||||||
// We are not interested in the order by or limit information.
|
Token token;
|
||||||
// Thus we simply delete it. sqlite also has some limitations, which
|
ExprSpan limit;
|
||||||
// will not bother us if we hide the information from it.
|
if (O && !L.pLimit) {
|
||||||
sqlite3ExprListDelete(pParse->db, O);
|
L.pLimit = maxscale_create_pseudo_limit(pParse, &token, &limit);
|
||||||
sqlite3ExprDelete(pParse->db, L.pLimit);
|
|
||||||
sqlite3ExprDelete(pParse->db, L.pOffset);
|
sqlite3ExprDelete(pParse->db, L.pOffset);
|
||||||
|
L.pOffset = 0;
|
||||||
|
}
|
||||||
|
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE");
|
||||||
mxs_sqlite3DeleteFrom(pParse,X,W,0);
|
mxs_sqlite3DeleteFrom(pParse,X,W,0);
|
||||||
#else
|
#else
|
||||||
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE");
|
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "DELETE");
|
||||||
@ -1690,12 +1694,15 @@ cmd ::= with(C) UPDATE orconf(R) fullname(X) indexed_opt(I) SET setlist(Y)
|
|||||||
sqlite3SrcListIndexedBy(pParse, X, &I);
|
sqlite3SrcListIndexedBy(pParse, X, &I);
|
||||||
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
sqlite3ExprListCheckLength(pParse,Y,"set list");
|
||||||
#ifdef MAXSCALE
|
#ifdef MAXSCALE
|
||||||
// We are not interested in the order by or limit information.
|
Token token;
|
||||||
// Thus we simply delete it. sqlite also has some limitations, which
|
ExprSpan limit;
|
||||||
// will not bother us if we hide the information from it.
|
if (O && !L.pLimit) {
|
||||||
sqlite3ExprListDelete(pParse->db, O);
|
L.pLimit = maxscale_create_pseudo_limit(pParse, &token, &limit);
|
||||||
sqlite3ExprDelete(pParse->db, L.pLimit);
|
|
||||||
sqlite3ExprDelete(pParse->db, L.pOffset);
|
sqlite3ExprDelete(pParse->db, L.pOffset);
|
||||||
|
L.pOffset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE");
|
||||||
mxs_sqlite3Update(pParse,X,Y,W,R);
|
mxs_sqlite3Update(pParse,X,Y,W,R);
|
||||||
#else
|
#else
|
||||||
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE");
|
W = sqlite3LimitWhere(pParse, X, W, O, L.pLimit, L.pOffset, "UPDATE");
|
||||||
@ -3525,3 +3532,21 @@ cmd ::= DECLARE. {
|
|||||||
}
|
}
|
||||||
|
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%include {
|
||||||
|
|
||||||
|
static Expr* maxscale_create_pseudo_limit(Parse* pParse, Token* pToken, ExprSpan* pLimit)
|
||||||
|
{
|
||||||
|
// sqlite3 does not accept a ORDER BY without LIMIT, but MariaDB
|
||||||
|
// does. Thus, to make sqlite3LimitWhere return non-NULL we need
|
||||||
|
// to inject a LIMIT if there is none. We use a value of -1 to
|
||||||
|
// tell update_field_infos_from_select() that this LIMIT should
|
||||||
|
// not be counted as a limiting clause.
|
||||||
|
static char one[] = "-1";
|
||||||
|
pToken->z = one;
|
||||||
|
pToken->n = 1;
|
||||||
|
spanExpr(pLimit, pParse, TK_INTEGER, pToken);
|
||||||
|
return pLimit->pExpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -76,6 +76,10 @@ char* get_types_as_string(uint32_t types)
|
|||||||
{
|
{
|
||||||
s = append(s, "QUERY_TYPE_SESSION_WRITE", &len);
|
s = append(s, "QUERY_TYPE_SESSION_WRITE", &len);
|
||||||
}
|
}
|
||||||
|
if (types & QUERY_TYPE_USERVAR_WRITE)
|
||||||
|
{
|
||||||
|
s = append(s, "QUERY_TYPE_USERVAR_WRITE", &len);
|
||||||
|
}
|
||||||
if (types & QUERY_TYPE_USERVAR_READ)
|
if (types & QUERY_TYPE_USERVAR_READ)
|
||||||
{
|
{
|
||||||
s = append(s, "QUERY_TYPE_USERVAR_READ", &len);
|
s = append(s, "QUERY_TYPE_USERVAR_READ", &len);
|
||||||
|
@ -28,3 +28,7 @@ QUERY_TYPE_WRITE
|
|||||||
QUERY_TYPE_WRITE
|
QUERY_TYPE_WRITE
|
||||||
QUERY_TYPE_WRITE
|
QUERY_TYPE_WRITE
|
||||||
QUERY_TYPE_GSYSVAR_WRITE
|
QUERY_TYPE_GSYSVAR_WRITE
|
||||||
|
QUERY_TYPE_READ
|
||||||
|
QUERY_TYPE_READ
|
||||||
|
QUERY_TYPE_USERVAR_WRITE
|
||||||
|
QUERY_TYPE_READ|QUERY_TYPE_MASTER_READ
|
||||||
|
@ -25,6 +25,10 @@ SELECT IS_USED_LOCK('lock1');
|
|||||||
SELECT RELEASE_LOCK('lock1');
|
SELECT RELEASE_LOCK('lock1');
|
||||||
deallocate prepare select_stmt;
|
deallocate prepare select_stmt;
|
||||||
SELECT a FROM tbl FOR UPDATE;
|
SELECT a FROM tbl FOR UPDATE;
|
||||||
SELECT a INTO OUTFILE 'out.txt'
|
SELECT a INTO OUTFILE 'out.txt';
|
||||||
SELECT a INTO DUMPFILE 'dump.txt'
|
SELECT a INTO DUMPFILE 'dump.txt';
|
||||||
SELECT a INTO @var;
|
SELECT a INTO @var;
|
||||||
|
select timediff(cast('2004-12-30 12:00:00' as time), '12:00:00');
|
||||||
|
(select 1 as a from t1) union all (select 1 from dual) limit 1;
|
||||||
|
SET @saved_cs_client= @@character_set_client;
|
||||||
|
SELECT 1 AS c1 FROM t1 ORDER BY ( SELECT 1 AS c2 FROM t1 GROUP BY GREATEST(LAST_INSERT_ID(), t1.a) ORDER BY GREATEST(LAST_INSERT_ID(), t1.a) LIMIT 1);
|
||||||
|
Reference in New Issue
Block a user