MXS-1196: Handle Oracle EXECUTE and PREPARE statements

- EXECUTE IMMEDIATE ...
- PREPARE stmt FROM dynamic string
This commit is contained in:
Johan Wikman 2017-05-22 12:55:47 +03:00
parent 7c1afe5d2c
commit 116e2cd974
2 changed files with 171 additions and 21 deletions

View File

@ -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)
{
QC_TRACE();
@ -2519,14 +2591,72 @@ void maxscaleRenameTable(Parse* pParse, SrcList* 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_SQLITE_INFO* info = this_thread.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;
// 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;
}
size_t preparable_stmt_len = pStmt->n - 2;
size_t payload_len = 1 + preparable_stmt_len;
size_t packet_len = MYSQL_HEADER_LEN + payload_len;
// If the expression just contains a string, then zStmt will
// be that string. Otherwise it will be _some_ string from the
// 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 (info->preparable_stmt)
if (zStmt)
{
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;
size_t preparable_stmt_len = zStmt ? strlen(zStmt) : 0;
size_t payload_len = 1 + preparable_stmt_len;
size_t packet_len = MYSQL_HEADER_LEN + payload_len;
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
@ -2566,6 +2709,8 @@ void maxscalePrepare(Parse* pParse, Token* pName, Token* pStmt)
ss_dassert(info->collect != info->collected);
ss_dassert(strncmp(info->prepare_name, pName->z, pName->n) == 0);
}
exposed_sqlite3ExprDelete(pParse->db, pStmt);
}
void maxscalePrivileges(Parse* pParse, int kind)

View File

@ -114,12 +114,13 @@ extern void maxscaleDeallocate(Parse*, Token* pName);
extern void maxscaleDo(Parse*, ExprList* pEList);
extern void maxscaleDrop(Parse*, MxsDrop* pDrop);
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 maxscaleFlush(Parse*, Token* pWhat);
extern void maxscaleHandler(Parse*, mxs_handler_t, SrcList* pFullName, Token* pName);
extern void maxscaleLoadData(Parse*, SrcList* pFullName);
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 maxscaleRenameTable(Parse*, SrcList* pTables);
extern void maxscaleSet(Parse*, int scope, mxs_set_t kind, ExprList*);
@ -2857,9 +2858,9 @@ cmd ::= prepare.
cmd ::= execute.
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}
@ -2881,6 +2882,10 @@ execute ::= EXECUTE nm(X) execute_variables_opt(Y). {
maxscaleExecute(pParse, &X, Y);
}
execute ::= EXECUTE id(X) expr(Y) execute_variables_opt(Z). {
maxscaleExecuteImmediate(pParse, &X, &Y, Z);
}
dod ::= DEALLOCATE.
dod ::= DROP.