MXS-2043 Classify SELECT ... FOR UPDATE as QUERY_TYPE_WRITE
SELECT...FOR UPDATE locks the rows for update, but only if autocommit==0 or a transaction is active, so in principle even if it were classified as READ it'd still be sent to master when it actually matters. However, even if autocommit==1 and/or no transaction is active, a slave in read only mode will reject the statement if the user is subject to the read only restriction (a user with super privileges is not), which might be considered a server bug. By classifying the statement as a write, it'll be sent to master and always succeed.
This commit is contained in:
@ -1987,7 +1987,14 @@ public:
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_type_mask = QUERY_TYPE_READ;
|
// Only if the type has explicitly been set to QUERY_TYPE_WRITE
|
||||||
|
// we don't force it to QUERY_TYPE_READ but with other bits we do.
|
||||||
|
// This is something of kludge to ensure continued compatibility
|
||||||
|
// with qc_mysqlembedded.
|
||||||
|
if (m_type_mask != QUERY_TYPE_WRITE)
|
||||||
|
{
|
||||||
|
m_type_mask = QUERY_TYPE_READ;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QcAliases aliases;
|
QcAliases aliases;
|
||||||
@ -3000,6 +3007,12 @@ public:
|
|||||||
m_operation = QUERY_OP_CHANGE_DB;
|
m_operation = QUERY_OP_CHANGE_DB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_type_mask(uint32_t type_mask)
|
||||||
|
{
|
||||||
|
ss_dassert(this_thread.initialized);
|
||||||
|
m_type_mask = type_mask;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QcSqliteInfo(uint32_t cllct)
|
QcSqliteInfo(uint32_t cllct)
|
||||||
: m_status(QC_QUERY_INVALID)
|
: m_status(QC_QUERY_INVALID)
|
||||||
@ -3266,6 +3279,8 @@ extern void maxscaleTruncate(Parse*, Token* pDatabase, Token* pName);
|
|||||||
extern void maxscaleUse(Parse*, Token*);
|
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);
|
||||||
|
// 'unsigned int' and not 'uint32_t' because 'uint32_t' is unknown in sqlite3 context.
|
||||||
|
extern void maxscale_set_type_mask(unsigned int type_mask);
|
||||||
|
|
||||||
extern void maxscaleComment();
|
extern void maxscaleComment();
|
||||||
extern int maxscaleKeyword(int token);
|
extern int maxscaleKeyword(int token);
|
||||||
@ -3649,6 +3664,14 @@ extern void maxscale_update_function_info(const char* name, const Expr* pExpr)
|
|||||||
pInfo->update_function_info(NULL, name, pExpr, NULL);
|
pInfo->update_function_info(NULL, name, pExpr, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void maxscale_set_type_mask(unsigned int type_mask)
|
||||||
|
{
|
||||||
|
QcSqliteInfo* pInfo = this_thread.pInfo;
|
||||||
|
ss_dassert(pInfo);
|
||||||
|
|
||||||
|
pInfo->set_type_mask(type_mask);
|
||||||
|
}
|
||||||
|
|
||||||
static const char* get_token_symbol(int token)
|
static const char* get_token_symbol(int token)
|
||||||
{
|
{
|
||||||
switch (token)
|
switch (token)
|
||||||
|
@ -131,6 +131,7 @@ extern void maxscaleTruncate(Parse*, Token* pDatabase, Token* pName);
|
|||||||
extern void maxscaleUse(Parse*, Token*);
|
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);
|
||||||
|
|
||||||
// Exposed utility functions
|
// Exposed utility functions
|
||||||
void exposed_sqlite3ExprDelete(sqlite3 *db, Expr *pExpr)
|
void exposed_sqlite3ExprDelete(sqlite3 *db, Expr *pExpr)
|
||||||
@ -966,6 +967,10 @@ cmd ::= select(X). {
|
|||||||
%destructor select {sqlite3SelectDelete(pParse->db, $$);}
|
%destructor select {sqlite3SelectDelete(pParse->db, $$);}
|
||||||
%type selectnowith {Select*}
|
%type selectnowith {Select*}
|
||||||
%destructor selectnowith {sqlite3SelectDelete(pParse->db, $$);}
|
%destructor selectnowith {sqlite3SelectDelete(pParse->db, $$);}
|
||||||
|
%ifdef MAXSCALE
|
||||||
|
%type selectnowithsuffix {Select*}
|
||||||
|
%destructor selectnowithsuffix {sqlite3SelectDelete(pParse->db, $$);}
|
||||||
|
%endif
|
||||||
%type oneselect {Select*}
|
%type oneselect {Select*}
|
||||||
%destructor oneselect {sqlite3SelectDelete(pParse->db, $$);}
|
%destructor oneselect {sqlite3SelectDelete(pParse->db, $$);}
|
||||||
|
|
||||||
@ -993,7 +998,12 @@ cmd ::= select(X). {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%ifdef MAXSCALE
|
||||||
|
select(A) ::= with(W) selectnowithsuffix(X). {
|
||||||
|
%endif
|
||||||
|
%ifndef MAXSCALE
|
||||||
select(A) ::= with(W) selectnowith(X). {
|
select(A) ::= with(W) selectnowith(X). {
|
||||||
|
%endif
|
||||||
Select *p = X;
|
Select *p = X;
|
||||||
if( p ){
|
if( p ){
|
||||||
p->pWith = W;
|
p->pWith = W;
|
||||||
@ -1004,6 +1014,15 @@ select(A) ::= with(W) selectnowith(X). {
|
|||||||
A = p;
|
A = p;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
%ifdef MAXSCALE
|
||||||
|
selectnowithsuffix(A) ::= selectnowith(X). {A = X;}
|
||||||
|
|
||||||
|
selectnowithsuffix(A) ::= selectnowith(X) FOR UPDATE. {
|
||||||
|
A = X;
|
||||||
|
maxscale_set_type_mask(QUERY_TYPE_WRITE);
|
||||||
|
}
|
||||||
|
%endif
|
||||||
|
|
||||||
selectnowith(A) ::= oneselect(X). {A = X;}
|
selectnowith(A) ::= oneselect(X). {A = X;}
|
||||||
%ifndef SQLITE_OMIT_COMPOUND_SELECT
|
%ifndef SQLITE_OMIT_COMPOUND_SELECT
|
||||||
selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). {
|
selectnowith(A) ::= selectnowith(X) multiselect_op(Y) oneselect(Z). {
|
||||||
|
Reference in New Issue
Block a user