MXS-1196: Handle Oracle EXECUTE and PREPARE statements
- EXECUTE IMMEDIATE ... - PREPARE stmt FROM dynamic string
This commit is contained in:
@ -2153,6 +2153,78 @@ void maxscaleExecute(Parse* pParse, Token* pName, int type_mask)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int32_t type_check_dynamic_string(const Expr* pExpr)
|
||||||
|
{
|
||||||
|
int32_t type_mask = 0;
|
||||||
|
|
||||||
|
if (pExpr)
|
||||||
|
{
|
||||||
|
switch (pExpr->op)
|
||||||
|
{
|
||||||
|
case TK_CONCAT:
|
||||||
|
type_mask |= type_check_dynamic_string(pExpr->pLeft);
|
||||||
|
type_mask |= type_check_dynamic_string(pExpr->pRight);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TK_VARIABLE:
|
||||||
|
ss_dassert(pExpr->u.zToken);
|
||||||
|
{
|
||||||
|
const char* zToken = pExpr->u.zToken;
|
||||||
|
if (zToken[0] == '@')
|
||||||
|
{
|
||||||
|
if (zToken[1] == '@')
|
||||||
|
{
|
||||||
|
type_mask |= QUERY_TYPE_SYSVAR_READ;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
type_mask |= QUERY_TYPE_USERVAR_READ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return type_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
void maxscaleExecuteImmediate(Parse* pParse, Token* pName, ExprSpan* pExprSpan, int type_mask)
|
||||||
|
{
|
||||||
|
QC_TRACE();
|
||||||
|
|
||||||
|
QC_SQLITE_INFO* info = this_thread.info;
|
||||||
|
ss_dassert(info);
|
||||||
|
|
||||||
|
if (this_unit.sql_mode == QC_SQL_MODE_ORACLE)
|
||||||
|
{
|
||||||
|
// This should be "EXECUTE IMMEDIATE ...", but as "IMMEDIATE" is not
|
||||||
|
// checked by the parser we do it here.
|
||||||
|
|
||||||
|
static const char IMMEDIATE[] = "IMMEDIATE";
|
||||||
|
|
||||||
|
if ((pName->n == sizeof(IMMEDIATE) - 1) && (strncasecmp(pName->z, IMMEDIATE, pName->n)) == 0)
|
||||||
|
{
|
||||||
|
info->status = QC_QUERY_PARSED;
|
||||||
|
info->type_mask = (QUERY_TYPE_WRITE | type_mask);
|
||||||
|
info->type_mask |= type_check_dynamic_string(pExprSpan->pExpr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info->status = QC_QUERY_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info->status = QC_QUERY_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
exposed_sqlite3ExprDelete(pParse->db, pExprSpan->pExpr);
|
||||||
|
}
|
||||||
|
|
||||||
void maxscaleExplain(Parse* pParse, Token* pNext)
|
void maxscaleExplain(Parse* pParse, Token* pNext)
|
||||||
{
|
{
|
||||||
QC_TRACE();
|
QC_TRACE();
|
||||||
@ -2519,14 +2591,72 @@ void maxscaleRenameTable(Parse* pParse, SrcList* pTables)
|
|||||||
exposed_sqlite3SrcListDelete(pParse->db, pTables);
|
exposed_sqlite3SrcListDelete(pParse->db, pTables);
|
||||||
}
|
}
|
||||||
|
|
||||||
void maxscalePrepare(Parse* pParse, Token* pName, Token* pStmt)
|
/**
|
||||||
|
* Returns some string from an expression.
|
||||||
|
*
|
||||||
|
* @param pExpr An expression.
|
||||||
|
*
|
||||||
|
* @return Some string referred to in pExpr.
|
||||||
|
*/
|
||||||
|
static const char* find_one_string(Expr* pExpr)
|
||||||
|
{
|
||||||
|
const char* z = NULL;
|
||||||
|
|
||||||
|
if (pExpr->op == TK_STRING)
|
||||||
|
{
|
||||||
|
ss_dassert(pExpr->u.zToken);
|
||||||
|
z = pExpr->u.zToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!z && pExpr->pLeft)
|
||||||
|
{
|
||||||
|
z = find_one_string(pExpr->pLeft);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!z && pExpr->pRight)
|
||||||
|
{
|
||||||
|
z = find_one_string(pExpr->pRight);
|
||||||
|
}
|
||||||
|
|
||||||
|
return z;
|
||||||
|
}
|
||||||
|
|
||||||
|
void maxscalePrepare(Parse* pParse, Token* pName, Expr* pStmt)
|
||||||
{
|
{
|
||||||
QC_TRACE();
|
QC_TRACE();
|
||||||
|
|
||||||
QC_SQLITE_INFO* info = this_thread.info;
|
QC_SQLITE_INFO* info = this_thread.info;
|
||||||
ss_dassert(info);
|
ss_dassert(info);
|
||||||
|
|
||||||
info->status = QC_QUERY_PARSED;
|
// If the mode is MODE_ORACLE then if expression contains simply a string
|
||||||
|
// we can conclude that the statement has been fully parsed, because it will
|
||||||
|
// be sensible to parse the preparable statement. Otherwise we mark the
|
||||||
|
// statement as having been partially parsed, since the preparable statement
|
||||||
|
// will not contain the full statement.
|
||||||
|
if (this_unit.sql_mode == QC_SQL_MODE_ORACLE)
|
||||||
|
{
|
||||||
|
if (pStmt->op == TK_STRING)
|
||||||
|
{
|
||||||
|
info->status = QC_QUERY_PARSED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info->status = QC_QUERY_PARTIALLY_PARSED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If the mode is not MODE_ORACLE, then only a string is acceptable.
|
||||||
|
if (pStmt->op == TK_STRING)
|
||||||
|
{
|
||||||
|
info->status = QC_QUERY_PARSED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info->status = QC_QUERY_INVALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
info->type_mask = QUERY_TYPE_PREPARE_NAMED_STMT;
|
info->type_mask = QUERY_TYPE_PREPARE_NAMED_STMT;
|
||||||
|
|
||||||
// If information is collected in several passes, then we may
|
// If information is collected in several passes, then we may
|
||||||
@ -2540,25 +2670,38 @@ void maxscalePrepare(Parse* pParse, Token* pName, Token* pStmt)
|
|||||||
info->prepare_name[pName->n] = 0;
|
info->prepare_name[pName->n] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t preparable_stmt_len = pStmt->n - 2;
|
// If the expression just contains a string, then zStmt will
|
||||||
size_t payload_len = 1 + preparable_stmt_len;
|
// be that string. Otherwise it will be _some_ string from the
|
||||||
size_t packet_len = MYSQL_HEADER_LEN + payload_len;
|
// expression. In the latter case we've already marked the result
|
||||||
|
// to have been partially parsed.
|
||||||
|
const char* zStmt = find_one_string(pStmt);
|
||||||
|
|
||||||
info->preparable_stmt = gwbuf_alloc(packet_len);
|
if (zStmt)
|
||||||
|
|
||||||
if (info->preparable_stmt)
|
|
||||||
{
|
{
|
||||||
uint8_t* ptr = GWBUF_DATA(info->preparable_stmt);
|
size_t preparable_stmt_len = zStmt ? strlen(zStmt) : 0;
|
||||||
// Payload length
|
size_t payload_len = 1 + preparable_stmt_len;
|
||||||
*ptr++ = payload_len;
|
size_t packet_len = MYSQL_HEADER_LEN + payload_len;
|
||||||
*ptr++ = (payload_len >> 8);
|
|
||||||
*ptr++ = (payload_len >> 16);
|
|
||||||
// Sequence id
|
|
||||||
*ptr++ = 0x00;
|
|
||||||
// Command
|
|
||||||
*ptr++ = MYSQL_COM_QUERY;
|
|
||||||
|
|
||||||
memcpy(ptr, pStmt->z + 1, pStmt->n - 2);
|
info->preparable_stmt = gwbuf_alloc(packet_len);
|
||||||
|
|
||||||
|
if (info->preparable_stmt)
|
||||||
|
{
|
||||||
|
uint8_t* ptr = GWBUF_DATA(info->preparable_stmt);
|
||||||
|
// Payload length
|
||||||
|
*ptr++ = payload_len;
|
||||||
|
*ptr++ = (payload_len >> 8);
|
||||||
|
*ptr++ = (payload_len >> 16);
|
||||||
|
// Sequence id
|
||||||
|
*ptr++ = 0x00;
|
||||||
|
// Command
|
||||||
|
*ptr++ = MYSQL_COM_QUERY;
|
||||||
|
|
||||||
|
memcpy(ptr, zStmt, preparable_stmt_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info->status = QC_QUERY_INVALID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2566,6 +2709,8 @@ void maxscalePrepare(Parse* pParse, Token* pName, Token* pStmt)
|
|||||||
ss_dassert(info->collect != info->collected);
|
ss_dassert(info->collect != info->collected);
|
||||||
ss_dassert(strncmp(info->prepare_name, pName->z, pName->n) == 0);
|
ss_dassert(strncmp(info->prepare_name, pName->z, pName->n) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exposed_sqlite3ExprDelete(pParse->db, pStmt);
|
||||||
}
|
}
|
||||||
|
|
||||||
void maxscalePrivileges(Parse* pParse, int kind)
|
void maxscalePrivileges(Parse* pParse, int kind)
|
||||||
|
@ -114,12 +114,13 @@ extern void maxscaleDeallocate(Parse*, Token* pName);
|
|||||||
extern void maxscaleDo(Parse*, ExprList* pEList);
|
extern void maxscaleDo(Parse*, ExprList* pEList);
|
||||||
extern void maxscaleDrop(Parse*, MxsDrop* pDrop);
|
extern void maxscaleDrop(Parse*, MxsDrop* pDrop);
|
||||||
extern void maxscaleExecute(Parse*, Token* pName, int type_mask);
|
extern void maxscaleExecute(Parse*, Token* pName, int type_mask);
|
||||||
|
extern void maxscaleExecuteImmediate(Parse*, Token* pName, ExprSpan* pExprSpan, int type_mask);
|
||||||
extern void maxscaleExplain(Parse*, Token* pNext);
|
extern void maxscaleExplain(Parse*, Token* pNext);
|
||||||
extern void maxscaleFlush(Parse*, Token* pWhat);
|
extern void maxscaleFlush(Parse*, Token* pWhat);
|
||||||
extern void maxscaleHandler(Parse*, mxs_handler_t, SrcList* pFullName, Token* pName);
|
extern void maxscaleHandler(Parse*, mxs_handler_t, SrcList* pFullName, Token* pName);
|
||||||
extern void maxscaleLoadData(Parse*, SrcList* pFullName);
|
extern void maxscaleLoadData(Parse*, SrcList* pFullName);
|
||||||
extern void maxscaleLock(Parse*, mxs_lock_t, SrcList*);
|
extern void maxscaleLock(Parse*, mxs_lock_t, SrcList*);
|
||||||
extern void maxscalePrepare(Parse*, Token* pName, Token* pStmt);
|
extern void maxscalePrepare(Parse*, Token* pName, Expr* pStmt);
|
||||||
extern void maxscalePrivileges(Parse*, int kind);
|
extern void maxscalePrivileges(Parse*, int kind);
|
||||||
extern void maxscaleRenameTable(Parse*, SrcList* pTables);
|
extern void maxscaleRenameTable(Parse*, SrcList* pTables);
|
||||||
extern void maxscaleSet(Parse*, int scope, mxs_set_t kind, ExprList*);
|
extern void maxscaleSet(Parse*, int scope, mxs_set_t kind, ExprList*);
|
||||||
@ -2857,9 +2858,9 @@ cmd ::= prepare.
|
|||||||
cmd ::= execute.
|
cmd ::= execute.
|
||||||
cmd ::= deallocate.
|
cmd ::= deallocate.
|
||||||
|
|
||||||
prepare ::= PREPARE nm(X) FROM STRING(Y).
|
prepare ::= PREPARE nm(X) FROM expr(Y).
|
||||||
{
|
{
|
||||||
maxscalePrepare(pParse, &X, &Y);
|
maxscalePrepare(pParse, &X, Y.pExpr);
|
||||||
}
|
}
|
||||||
|
|
||||||
%type execute_variable {int}
|
%type execute_variable {int}
|
||||||
@ -2881,6 +2882,10 @@ execute ::= EXECUTE nm(X) execute_variables_opt(Y). {
|
|||||||
maxscaleExecute(pParse, &X, Y);
|
maxscaleExecute(pParse, &X, Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
execute ::= EXECUTE id(X) expr(Y) execute_variables_opt(Z). {
|
||||||
|
maxscaleExecuteImmediate(pParse, &X, &Y, Z);
|
||||||
|
}
|
||||||
|
|
||||||
dod ::= DEALLOCATE.
|
dod ::= DEALLOCATE.
|
||||||
dod ::= DROP.
|
dod ::= DROP.
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user