MXS-1275: Make sql_mode a property of the current context

The sql mode is now a property of the info object used for storing
parsing related information. It is initialized with the value of the
sql mode qc_sqlite was initialized with.

This will be further changed so that the mode can be adjusted at
runtime so that the sql mode can be the property of a session. That
is, once set it will affect all future parsing for that session.
This commit is contained in:
Johan Wikman
2017-05-26 11:01:43 +03:00
parent cc0c193d2e
commit 94b0b082e4

View File

@ -49,6 +49,26 @@ static inline bool qc_info_was_parsed(qc_parse_result_t status)
return status == QC_QUERY_PARSED;
}
typedef enum qc_log_level
{
QC_LOG_NOTHING = 0,
QC_LOG_NON_PARSED,
QC_LOG_NON_PARTIALLY_PARSED,
QC_LOG_NON_TOKENIZED,
} qc_log_level_t;
typedef enum qc_parse_as
{
QC_PARSE_AS_DEFAULT, // Parse as embedded lib does before 10.3
QC_PARSE_AS_103 // Parse as embedded lib does in 10.3
} qc_parse_as_t;
typedef enum qc_sql_mode
{
QC_SQL_MODE_DEFAULT,
QC_SQL_MODE_ORACLE
} qc_sql_mode_t;
/**
* Contains information about a particular query.
*/
@ -85,28 +105,9 @@ typedef struct qc_sqlite_info
size_t function_infos_len; // The used entries in function_infos.
size_t function_infos_capacity; // The capacity of the function_infos array.
bool initializing; // Whether we are initializing sqlite3.
qc_sql_mode_t sql_mode; // The current sql_mode.
} QC_SQLITE_INFO;
typedef enum qc_log_level
{
QC_LOG_NOTHING = 0,
QC_LOG_NON_PARSED,
QC_LOG_NON_PARTIALLY_PARSED,
QC_LOG_NON_TOKENIZED,
} qc_log_level_t;
typedef enum qc_parse_as
{
QC_PARSE_AS_DEFAULT, // Parse as embedded lib does before 10.3
QC_PARSE_AS_103 // Parse as embedded lib does in 10.3
} qc_parse_as_t;
typedef enum qc_sql_mode
{
QC_SQL_MODE_DEFAULT,
QC_SQL_MODE_ORACLE
} qc_sql_mode_t;
/**
* Defines what a particular name should be mapped to.
*/
@ -180,10 +181,11 @@ static QC_SQLITE_INFO* info_alloc(uint32_t collect);
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, uint32_t collect);
static bool is_sequence_related_field(const char* database,
static bool is_sequence_related_field(QC_SQLITE_INFO* info,
const char* database,
const char* table,
const char* column);
static bool is_sequence_related_function(const char* func_name);
static bool is_sequence_related_function(QC_SQLITE_INFO* info, const char* func_name);
static void log_invalid_data(GWBUF* query, const char* message);
static const char* map_function_name(const char* name);
static bool parse_query(GWBUF* query, uint32_t collect);
@ -441,6 +443,7 @@ static QC_SQLITE_INFO* info_init(QC_SQLITE_INFO* info, uint32_t collect)
info->function_infos_len = 0;
info->function_infos_capacity = 0;
info->initializing = false;
info->sql_mode = this_unit.sql_mode;
return info;
}
@ -671,31 +674,34 @@ static bool query_is_parsed(GWBUF* query, uint32_t collect)
/**
* Returns whether a field is sequence related.
*
* @param database The database/schema or NULL.
* @param table The table or NULL.
* @param column The column.
* @param info Current info object
* @param database The database/schema or NULL.
* @param table The table or NULL.
* @param column The column.
*
* @return True, if the field is sequence related, false otherwise.
*/
static bool is_sequence_related_field(const char* database,
static bool is_sequence_related_field(QC_SQLITE_INFO* info,
const char* database,
const char* table,
const char* column)
{
return is_sequence_related_function(column);
return is_sequence_related_function(info, column);
}
/**
* Returns whether a function is sequence related.
*
* @param func_name A function.
* @param info Current info object
* @param func_name A function name.
*
* @return True, if the function is sequence related, false otherwise.
*/
static bool is_sequence_related_function(const char* func_name)
static bool is_sequence_related_function(QC_SQLITE_INFO* info, const char* func_name)
{
bool rv = false;
if (this_unit.sql_mode == QC_SQL_MODE_ORACLE)
if (info->sql_mode == QC_SQL_MODE_ORACLE)
{
// In Oracle mode we ignore the pseudocolumns "currval" and "nextval".
// We also exclude "lastval", the 10.3 equivalent of "currval".
@ -820,7 +826,7 @@ static void update_field_info(QC_SQLITE_INFO* info,
{
ss_dassert(column);
if (is_sequence_related_field(database, table, column))
if (is_sequence_related_field(info, database, table, column))
{
info->type_mask |= QUERY_TYPE_WRITE;
return;
@ -1235,7 +1241,7 @@ static void update_field_infos(QC_SQLITE_INFO* info,
break;
case TK_REM:
if (this_unit.sql_mode == QC_SQL_MODE_ORACLE)
if (info->sql_mode == QC_SQL_MODE_ORACLE)
{
if ((pLeft && (pLeft->op == TK_ID)) &&
(pRight && (pRight->op == TK_ID)) &&
@ -1284,12 +1290,12 @@ static void update_field_infos(QC_SQLITE_INFO* info,
{
info->type_mask |= (QUERY_TYPE_READ | QUERY_TYPE_MASTER_READ);
}
else if (is_sequence_related_function(zToken))
else if (is_sequence_related_function(info, zToken))
{
info->type_mask |= QUERY_TYPE_WRITE;
ignore_exprlist = true;
}
else if (!is_builtin_readonly_function(zToken, this_unit.sql_mode == QC_SQL_MODE_ORACLE))
else if (!is_builtin_readonly_function(zToken, info->sql_mode == QC_SQL_MODE_ORACLE))
{
info->type_mask |= QUERY_TYPE_WRITE;
}
@ -1577,7 +1583,7 @@ void mxs_sqlite3BeginTransaction(Parse* pParse, int token, int type)
QC_SQLITE_INFO* info = this_thread.info;
ss_dassert(info);
if ((this_unit.sql_mode != QC_SQL_MODE_ORACLE) || (token == TK_START))
if ((info->sql_mode != QC_SQL_MODE_ORACLE) || (token == TK_START))
{
info->status = QC_QUERY_PARSED;
info->type_mask = QUERY_TYPE_BEGIN_TRX | type;
@ -2216,7 +2222,7 @@ void maxscaleDeclare(Parse* pParse)
QC_SQLITE_INFO* info = this_thread.info;
ss_dassert(info);
if (this_unit.sql_mode != QC_SQL_MODE_ORACLE)
if (info->sql_mode != QC_SQL_MODE_ORACLE)
{
info->status = QC_QUERY_INVALID;
}
@ -2369,7 +2375,7 @@ void maxscaleExecuteImmediate(Parse* pParse, Token* pName, ExprSpan* pExprSpan,
QC_SQLITE_INFO* info = this_thread.info;
ss_dassert(info);
if (this_unit.sql_mode == QC_SQL_MODE_ORACLE)
if (info->sql_mode == QC_SQL_MODE_ORACLE)
{
// This should be "EXECUTE IMMEDIATE ...", but as "IMMEDIATE" is not
// checked by the parser we do it here.
@ -2527,7 +2533,7 @@ int maxscaleTranslateKeyword(int token)
case TK_CHARSET:
case TK_DO:
case TK_HANDLER:
if (this_unit.sql_mode == QC_SQL_MODE_ORACLE)
if (info->sql_mode == QC_SQL_MODE_ORACLE)
{
// The keyword is translated, but only if it not used
// as the first keyword. Matters for DO and HANDLER.
@ -2585,7 +2591,7 @@ int maxscaleKeyword(int token)
case TK_BEGIN:
case TK_DECLARE:
case TK_FOR:
if (this_unit.sql_mode == QC_SQL_MODE_ORACLE)
if (info->sql_mode == QC_SQL_MODE_ORACLE)
{
// The beginning of a BLOCK. We'll assume it is in a single
// COM_QUERY packet and hence one GWBUF.
@ -2857,7 +2863,7 @@ void maxscalePrepare(Parse* pParse, Token* pName, Expr* pStmt)
// 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 (info->sql_mode == QC_SQL_MODE_ORACLE)
{
if (pStmt->op == TK_STRING)
{