Commit e7fcd496 authored by Oleg Smirnov's avatar Oleg Smirnov Committed by Sergei Petrunia

MDEV-27021 Implement SHOW ANALYZE command

parent 32868483
This diff is collapsed.
#
# Tests for SHOW ANALYZE FOR functionality
#
--source include/have_debug.inc
--source include/have_debug_sync.inc
--source include/have_innodb.inc
# Using valgrind can cause 'reap' to fail. See comment below
--source include/not_valgrind.inc
--disable_warnings
drop table if exists t0, t1, t2, t3, t4;
drop view if exists v1;
--enable_warnings
SET @old_debug= @@session.debug;
#
# Testcases in this file do not work with embedded server. The reason for this
# is that we use the following commands for synchronization:
#
# set @show_explain_probe_select_id=1;
# SET debug_dbug='d,show_explain_probe_join_exec_start';
# send select count(*) from t1 where a < 100000;
#
# When ran with mysqltest_embedded, this translates into:
#
# Thread1> DBUG_PUSH("d,show_explain_probe_join_exec_start");
# Thread1> create another thread for doing "send ... reap"
# Thread2> mysql_parse("select count(*) from t1 where a < 100000");
#
# That is, "select count(*) ..." is ran in a thread for which DBUG_PUSH(...)
# has not been called. As a result, show_explain_probe_join_exec_start does not fire, and
# "select count(*) ..." does not wait till its SHOW ANALYZE command, and the
# test fails.
#
-- source include/not_embedded.inc
set debug_sync='RESET';
create table t0 (a int);
insert into t0 values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
create table t1 (a int);
alter table t1 add b int, add c int, add filler char(32);
insert into t1 select A.a, 10*B.a, 100*C.a, 'foo filler' from t0 A, t0 B, t0 C;
alter table t1 add key(a), add key(b);
analyze table t1;
#
# Try SHOW ANALYZE for a non-existent thread
#
--error ER_NO_SUCH_THREAD
show analyze for 1001;
--error ER_SET_CONSTANTS_ONLY
show analyze for (select a from t0 limit 1);
#
# Setup two threads and their ids
#
let $thr1=`select connection_id()`;
connect (con1, localhost, root,,);
connection con1;
let $thr2=`select connection_id()`;
SET @old_debug= @@session.debug;
connection default;
# SHOW ANALYZE FOR <idle thread>
--error ER_TARGET_NOT_EXPLAINABLE
evalp show analyze for $thr2;
# SHOW ANALYZE FOR <ourselves>
--error ER_TARGET_NOT_EXPLAINABLE
evalp show analyze for $thr1;
let $wait_condition= select State='show_explain_trap' from information_schema.processlist where id=$thr2;
#
# Test SHOW ANALYZE for simple queries
#
connection con1;
set @show_explain_probe_select_id=1;
SET debug_dbug='+d,show_explain_probe_join_exec_end';
send select count(*) from t1 where c < 500;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
send select max(c) from t1 where c < 10;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
--echo # We can catch ANALYZE too.
send analyze select max(c) from t1 where a < 10;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
--echo # UNION, select, first branch
set @show_explain_probe_select_id=1;
SET debug_dbug='+d,show_explain_probe_join_exec_end';
send select max(a) from t0 A where a<=5 union select max(a+1) from t0 B where a>=9;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
--echo # UNION, select, second branch
set @show_explain_probe_select_id=2; # <--- Second branch
SET debug_dbug='+d,show_explain_probe_join_exec_end';
send select max(a) from t0 A where a<=5 union select max(a+1) from t0 B where a>=9;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
--echo # UNION, analyze, first branch
set @show_explain_probe_select_id=1;
SET debug_dbug='+d,show_explain_probe_join_exec_end';
send analyze select a from t0 A where a<=5 union select a+1 from t0 B where a>=9;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
--echo # UNION, analyze, second branch
set @show_explain_probe_select_id=2;
send analyze select a from t0 A where a<=5 union select a+1 from t0 B where a>=9;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
--echo # Uncorrelated subquery, select
set @show_explain_probe_select_id=1;
SET debug_dbug='+d,show_explain_probe_join_exec_end';
send select a, (select max(a) from t0 B where a>6) from t0 A where a<2;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
--echo # Uncorrelated subquery, analyze
set @show_explain_probe_select_id=1;
SET debug_dbug='+d,show_explain_probe_join_exec_end';
send analyze select a, (select max(a) from t0 B where a>6) from t0 A where a<2;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
--echo # correlated subquery, select, before execution start
set @show_explain_probe_select_id=1;
SET debug_dbug='+d,show_explain_probe_join_exec_start';
send select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<2;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
--echo # correlated subquery, select, after execution
set @show_explain_probe_select_id=1;
SET debug_dbug='+d,show_explain_probe_join_exec_end';
send select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<2;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
--echo # correlated subquery, analyze
set @show_explain_probe_select_id=1;
SET debug_dbug='+d,show_explain_probe_join_exec_end';
send analyze select a, (select max(a) from t0 b where b.a+a.a<10) from t0 a where a<2;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
--echo # Try to do SHOW ANALYZE for a query that runs a SET command:
--echo #
set @show_explain_probe_select_id=2; # <---
SET debug_dbug='+d,show_explain_probe_join_exec_start';
send set @foo= (select max(a) from t0 where sin(a) >0);
connection default;
--source include/wait_condition.inc
--error ER_TARGET_NOT_EXPLAINABLE
evalp show analyze for $thr2;
evalp kill query $thr2;
connection con1;
--error ER_QUERY_INTERRUPTED
reap;
SET debug_dbug=@old_debug;
--echo #
--echo # Attempt SHOW ANALYZE for an UPDATE
--echo #
create table t2 as select a as a, a as dummy from t0 limit 2;
set @show_explain_probe_select_id=2;
SET debug_dbug='+d,show_explain_probe_join_exec_start';
send update t2 set dummy=0 where (select max(a) from t0 where t2.a + t0.a <3) >3 ;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
drop table t2;
SET debug_dbug=@old_debug;
--echo #
--echo # Attempt SHOW ANALYZE for a DELETE
--echo #
create table t2 as select a as a, a as dummy from t0 limit 2;
set @show_explain_probe_select_id=2;
SET debug_dbug='+d,show_explain_probe_join_exec_start';
send delete from t2 where (select max(a) from t0 where t2.a + t0.a <3) >3 ;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
drop table t2;
SET debug_dbug=@old_debug;
--echo #
--echo # Multiple SHOW ANALYZE calls for one select
--echo #
create table t2 as select a as a, a as dummy from t0 limit 3;
set @show_explain_probe_select_id=2;
SET debug_dbug='+d,show_explain_probe_join_exec_start';
send select t2.a, ((select max(a) from t0 where t2.a + t0.a <3) >3) as SUBQ from t2;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
--source include/wait_condition.inc
evalp show analyze for $thr2;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
drop table t2;
--echo #
--echo # SHOW ANALYZE for SELECT ... ORDER BY with "Using filesort"
--echo #
SET debug_dbug='+d,show_explain_probe_join_exec_end';
set @show_explain_probe_select_id=1;
send select * from t0 order by a;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
--echo #
--echo # SHOW ANALYZE for SELECT ... with "Using temporary"
--echo #
connection con1;
SET debug_dbug='+d,show_explain_probe_join_exec_end';
set @show_explain_probe_select_id=1;
send select distinct a from t0;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
--echo #
--echo # SHOW ANALYZE for SELECT ... with "Using temporary; Using filesort"
--echo #
SET debug_dbug='+d,show_explain_probe_join_exec_end';
set @show_explain_probe_select_id=1;
send select distinct a from t0;
connection default;
--source include/wait_condition.inc
evalp show analyze for $thr2;
connection con1;
reap;
SET debug_dbug=@old_debug;
drop table t0,t1;
--echo # End
connection default;
disconnect con1;
set debug_sync='RESET';
\ No newline at end of file
......@@ -26,13 +26,13 @@ connection con1;
SET @old_debug= @@session.debug;
connection default;
show explain for $thr2;
ERROR HY000: Target is not running an EXPLAINable command
ERROR HY000: Target is not executing an operation with a query plan
explain for connection $thr2;
ERROR HY000: Target is not running an EXPLAINable command
ERROR HY000: Target is not executing an operation with a query plan
show explain for $thr1;
ERROR HY000: Target is not running an EXPLAINable command
ERROR HY000: Target is not executing an operation with a query plan
explain for connection $thr1;
ERROR HY000: Target is not running an EXPLAINable command
ERROR HY000: Target is not executing an operation with a query plan
connection con1;
set @show_explain_probe_select_id=1;
SET debug_dbug='+d,show_explain_probe_join_exec_start';
......@@ -248,7 +248,7 @@ SET debug_dbug='+d,show_explain_probe_join_exec_start';
set @foo= (select max(a) from t0 where sin(a) >0);
connection default;
show explain for $thr2;
ERROR HY000: Target is not running an EXPLAINable command
ERROR HY000: Target is not executing an operation with a query plan
kill query $thr2;
connection con1;
ERROR 70100: Query execution was interrupted
......@@ -572,7 +572,7 @@ SET debug_dbug='+d,show_explain_probe_join_exec_end';
SELECT * FROM v1, t2;
connection default;
show explain for $thr2;
ERROR HY000: Target is not running an EXPLAINable command
ERROR HY000: Target is not executing an operation with a query plan
kill query $thr2;
connection con1;
ERROR 70100: Query execution was interrupted
......
......@@ -1025,6 +1025,7 @@ enum enum_schema_tables
SCH_EVENTS,
SCH_EXPLAIN_TABULAR,
SCH_EXPLAIN_JSON,
SCH_ANALYZE,
SCH_FILES,
SCH_GLOBAL_STATUS,
SCH_GLOBAL_VARIABLES,
......
......@@ -3507,6 +3507,7 @@ SHOW_VAR com_status_vars[]= {
{"show_errors", STMT_STATUS(SQLCOM_SHOW_ERRORS)},
{"show_events", STMT_STATUS(SQLCOM_SHOW_EVENTS)},
{"show_explain", STMT_STATUS(SQLCOM_SHOW_EXPLAIN)},
{"show_analyze", STMT_STATUS(SQLCOM_SHOW_ANALYZE)},
{"show_fields", STMT_STATUS(SQLCOM_SHOW_FIELDS)},
#ifndef DBUG_OFF
{"show_function_code", STMT_STATUS(SQLCOM_SHOW_FUNC_CODE)},
......
......@@ -634,6 +634,7 @@ constexpr privilege_t PRIV_STMT_DROP_SERVER= FEDERATED_ADMIN_ACL | SUPER_ACL;
/* Privileges related to processes */
constexpr privilege_t PRIV_COM_PROCESS_INFO= PROCESS_ACL;
// This privilege applies both for SHOW EXPLAIN and SHOW ANALYZE
constexpr privilege_t PRIV_STMT_SHOW_EXPLAIN= PROCESS_ACL;
constexpr privilege_t PRIV_STMT_SHOW_ENGINE_STATUS= PROCESS_ACL;
constexpr privilege_t PRIV_STMT_SHOW_ENGINE_MUTEX= PROCESS_ACL;
......
......@@ -8725,9 +8725,9 @@ ER_NO_SUCH_TABLE_IN_ENGINE 42S02
spa "La tabla '%-.192s.%-.192s' no existe en el motor"
swe "Det finns ingen tabell som heter '%-.192s.%-.192s' i handlern"
ER_TARGET_NOT_EXPLAINABLE
eng "Target is not executing an operation with a query plan"
chi "目标未运行可解释的命令"
eng "Target is not running an EXPLAINable command"
spa "El objetivo no está ejecutando un comando EXPLAINable"
spa "El objetivo no está ejecutando una operación con un plan de consulta (query)"
ER_CONNECTION_ALREADY_EXISTS
chi "连接'%.*s'与现有连接'%.*s'冲突"
eng "Connection '%.*s' conflicts with existing connection '%.*s'"
......
......@@ -221,6 +221,7 @@ sp_get_flags_for_command(LEX *lex)
case SQLCOM_SHOW_DATABASES:
case SQLCOM_SHOW_ERRORS:
case SQLCOM_SHOW_EXPLAIN:
case SQLCOM_SHOW_ANALYZE:
case SQLCOM_SHOW_FIELDS:
case SQLCOM_SHOW_FUNC_CODE:
case SQLCOM_SHOW_GENERIC:
......
......@@ -88,7 +88,8 @@ enum enum_sql_command {
SQLCOM_SHOW_RELAYLOG_EVENTS,
SQLCOM_GET_DIAGNOSTICS,
SQLCOM_SLAVE_ALL_START, SQLCOM_SLAVE_ALL_STOP,
SQLCOM_SHOW_EXPLAIN, SQLCOM_SHUTDOWN,
SQLCOM_SHOW_EXPLAIN,
SQLCOM_SHOW_ANALYZE, SQLCOM_SHUTDOWN,
SQLCOM_CREATE_ROLE, SQLCOM_DROP_ROLE, SQLCOM_GRANT_ROLE, SQLCOM_REVOKE_ROLE,
SQLCOM_COMPOUND,
SQLCOM_SHOW_GENERIC,
......
......@@ -665,6 +665,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_SHOW_ENGINE_MUTEX]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ENGINE_LOGS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_EXPLAIN]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_ANALYZE]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_PROCESSLIST]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_GRANTS]= CF_STATUS_COMMAND;
sql_command_flags[SQLCOM_SHOW_CREATE_USER]= CF_STATUS_COMMAND;
......@@ -3879,6 +3880,7 @@ mysql_execute_command(THD *thd, bool is_called_from_prepared_stmt)
break;
}
case SQLCOM_SHOW_EXPLAIN:
case SQLCOM_SHOW_ANALYZE:
{
if (!thd->security_ctx->priv_user[0] &&
check_global_access(thd, PRIV_STMT_SHOW_EXPLAIN))
......
......@@ -3000,8 +3000,8 @@ void Show_explain_request::call_in_target_thread()
DBUG_ASSERT(current_thd == target_thd);
set_current_thd(request_thd);
if (target_thd->lex->print_explain(explain_buf, 0 /* explain flags*/,
false /*TODO: analyze? */,
is_json_format, &printed_anything))
is_analyze, is_json_format,
&printed_anything))
{
failed_to_produce= TRUE;
}
......@@ -3119,16 +3119,16 @@ void select_result_text_buffer::save_to(String *res)
/*
Store the SHOW EXPLAIN output in the temporary table.
Store the SHOW EXPLAIN/SHOW ANALYZE output in the temporary table.
*/
int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond,
bool json_format)
int fill_show_explain_or_analyze(THD *thd, TABLE_LIST *table, COND *cond,
bool json_format, bool is_analyze)
{
const char *calling_user;
THD *tmp;
my_thread_id thread_id;
DBUG_ENTER("fill_show_explain");
DBUG_ENTER("fill_show_explain_or_analyze");
DBUG_ASSERT(cond==NULL);
thread_id= thd->lex->value_list.head()->val_int();
......@@ -3140,10 +3140,10 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond,
Security_context *tmp_sctx= tmp->security_ctx;
/*
If calling_user==NULL, calling thread has SUPER or PROCESS
privilege, and so can do SHOW EXPLAIN on any user.
privilege, and so can do SHOW EXPLAIN/SHOW ANALYZE on any user.
if calling_user!=NULL, he's only allowed to view SHOW EXPLAIN on
his own threads.
if calling_user!=NULL, he's only allowed to view
SHOW EXPLAIN/SHOW ANALYZE on his own threads.
*/
if (calling_user && (!tmp_sctx->user || strcmp(calling_user,
tmp_sctx->user)))
......@@ -3163,7 +3163,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond,
bool bres;
/*
Ok we've found the thread of interest and it won't go away because
we're holding its LOCK_thd_kill. Post it a SHOW EXPLAIN request.
we're holding its LOCK_thd_kill. Post it a SHOW EXPLAIN/SHOW ANALYZE request.
*/
bool timed_out;
int timeout_sec= 30;
......@@ -3173,6 +3173,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond,
explain_buf= new select_result_explain_buffer(thd, table->table);
explain_req.is_analyze= is_analyze;
explain_req.explain_buf= explain_buf;
explain_req.target_thd= tmp;
explain_req.request_thd= thd;
......@@ -3233,13 +3234,28 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond,
int fill_show_explain_tabular(THD *thd, TABLE_LIST *table, COND *cond)
{
return fill_show_explain(thd, table, cond, false /*json_format*/);
return fill_show_explain_or_analyze(
thd, table, cond, FALSE /* json_format */, FALSE /* is_analyze */);
}
int fill_show_explain_json(THD *thd, TABLE_LIST *table, COND *cond)
{
return fill_show_explain(thd, table, cond, true /*json_format*/);
return fill_show_explain_or_analyze(
thd, table, cond, TRUE /* json_format */, TRUE /* is_analyze */);
int fill_show_analyze_tabular(THD * thd, TABLE_LIST * table, COND * cond)
{
return fill_show_explain_or_analyze(
thd, table, cond, FALSE /* json_format */, TRUE /* is_analyze */);
}
int fill_show_analyze_json(THD * thd, TABLE_LIST * table, COND * cond)
{
return fill_show_explain_or_analyze(
thd, table, cond, TRUE /* json_format */, TRUE /* is_analyze */);
}
......@@ -9660,6 +9676,29 @@ ST_FIELD_INFO show_explain_json_fields_info[]=
};
ST_FIELD_INFO show_analyze_fields_info[]=
{
Column("id", SLonglong(3), NULLABLE, "id"),
Column("select_type", Varchar(19), NOT_NULL, "select_type"),
Column("table", Name(), NULLABLE, "table"),
Column("type", Varchar(15), NULLABLE, "type"),
Column("possible_keys",Varchar(NAME_CHAR_LEN*MAX_KEY), NULLABLE, "possible_keys"),
Column("key", Varchar(NAME_CHAR_LEN*MAX_KEY), NULLABLE, "key"),
Column("key_len", Varchar(NAME_CHAR_LEN*MAX_KEY), NULLABLE, "key_len"),
Column("ref", Varchar(NAME_CHAR_LEN*MAX_REF_PARTS),NULLABLE, "ref"),
Column("rows", SLonglong(10), NULLABLE, "rows"),
Column("r_rows", Varchar(NAME_CHAR_LEN), NULLABLE, "r_rows"),
/* Fields of type DECIMAL(5,2) to represent percentage.
See Show::Type::decimal_precision() and Show::Type::decimal_scale() to learn
how 502 converts to precision and scale (5 and 2)*/
Column("filtered", Decimal(502), NULLABLE, "filtered"),
Column("r_filtered", Decimal(502), NULLABLE, "r_filtered"),
Column("Extra", Varchar(255), NOT_NULL, "Extra"),
CEnd()
};
ST_FIELD_INFO check_constraints_fields_info[]=
{
Column("CONSTRAINT_CATALOG", Catalog(), NOT_NULL, OPEN_FULL_TABLE),
......@@ -9724,6 +9763,8 @@ ST_SCHEMA_TABLE schema_tables[]=
make_old_format, 0, -1, -1, TRUE /*hidden*/ , 0},
{"EXPLAIN_JSON", Show::show_explain_json_fields_info, 0, fill_show_explain_json,
make_old_format, 0, -1, -1, TRUE /*hidden*/, 0},
{"ANALYZE", Show::show_analyze_fields_info, 0, fill_show_analyze_tabular,
make_old_format, 0, -1, -1, TRUE /*hidden*/, 0},
{"FILES", Show::files_fields_info, 0,
hton_fill_schema_table, 0, 0, -1, -1, 0, 0},
{"GLOBAL_STATUS", Show::variables_fields_info, 0,
......
......@@ -155,14 +155,14 @@ THD *find_thread_by_id(longlong id, bool query_id= false);
class select_result_explain_buffer;
/*
SHOW EXPLAIN request object.
SHOW EXPLAIN/SHOW ANALYZE request object.
*/
class Show_explain_request : public Apc_target::Apc_call
{
public:
THD *target_thd; /* thd that we're running SHOW EXPLAIN for */
THD *request_thd; /* thd that run SHOW EXPLAIN command */
THD *target_thd; /* thd that we're running SHOW EXPLAIN/ANALYZE for */
THD *request_thd; /* thd that run SHOW EXPLAIN/ANALYZE command */
/*
Set to TRUE if you need the result in JSON format,
......@@ -170,19 +170,22 @@ class Show_explain_request : public Apc_target::Apc_call
*/
bool is_json_format= false;
/* FALSE for SHOW EXPLAIN, TRUE - for SHOW ANALYZE*/
bool is_analyze;
/* If true, there was some error when producing EXPLAIN output. */
bool failed_to_produce;
/* SHOW EXPLAIN will be stored here */
/* SHOW EXPLAIN/ANALYZE will be stored here */
select_result_explain_buffer *explain_buf;
/* Query that we've got SHOW EXPLAIN for */
/* Query that we've got SHOW EXPLAIN/ANALYZE for */
String query_str;
/* Overloaded virtual function */
void call_in_target_thread();
void call_in_target_thread() override;
};
/**
Condition pushdown used for INFORMATION_SCHEMA / SHOW queries.
This structure is to implement an optimization when
......
......@@ -13948,6 +13948,13 @@ show_param:
MYSQL_YYABORT;
add_value_to_list(thd, $4);
}
| ANALYZE_SYM FOR_SYM expr
{
Lex->sql_command= SQLCOM_SHOW_ANALYZE;
if (unlikely(prepare_schema_table(thd, Lex, 0, SCH_ANALYZE)))
MYSQL_YYABORT;
add_value_to_list(thd, $3);
}
| IDENT_sys remember_tok_start wild_and_where
{
LEX *lex= Lex;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment