Commit 194027d5 authored by sergefp@mysql.com's avatar sergefp@mysql.com

Merged

parents 7d9a9fd9 a314cbef
drop table if exists t1,t2;
create table t1
(
a int primary key,
b char(10),
);
insert into t1 values (1,'one');
insert into t1 values (2,'two');
insert into t1 values (3,'three');
insert into t1 values (4,'four');
set @a=2;
prepare stmt1 from 'select * from t1 where a <= ?';
execute stmt1 using @a;
a b
1 one
2 two
set @a=3;
execute stmt1 using @a;
a b
1 one
2 two
3 three
deallocate prepare no_such_statement;
ERROR HY000: Undefined prepared statement
execute stmt1;
ERROR HY000: Wrong arguments to mysql_execute
prepare stmt2 from 'prepare nested_stmt from "select 1"';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '"select 1"' at line 1
prepare stmt2 from 'execute stmt1';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'stmt1' at line 1
prepare stmt2 from 'deallocate prepare z';
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'z' at line 1
prepare stmt3 from 'insert into t1 values (?,?)';
set @arg1=5, @arg2='five';
execute stmt3 using @arg1, @arg2;
select * from t1 where a>3;
a b
4 four
5 five
prepare stmt4 from 'update t1 set a=? where b=?';
set @arg1=55, @arg2='five';
execute stmt4 using @arg1, @arg2;
select * from t1 where a>3;
a b
4 four
55 five
prepare stmt4 from 'create table t2 (a int)';
execute stmt4;
prepare stmt4 from 'drop table t2';
execute stmt4;
execute stmt4;
ERROR 42S02: Unknown table 't2'
prepare stmt5 from 'select ? + a from t1';
set @a=1;
execute stmt5 using @a;
? + a
2
3
4
5
56
execute stmt5 using @no_such_var;
? + a
NULL
NULL
NULL
NULL
NULL
set @nullvar=1;
set @nullvar=NULL;
execute stmt5 using @nullvar;
? + a
NULL
NULL
NULL
NULL
NULL
set @nullvar2=NULL;
execute stmt5 using @nullvar2;
? + a
NULL
NULL
NULL
NULL
NULL
drop table t1;
#
# SQL Syntax for Prepared Statements test
#
--disable_warnings
drop table if exists t1,t2;
--enable_warnings
create table t1
(
a int primary key,
b char(10),
);
insert into t1 values (1,'one');
insert into t1 values (2,'two');
insert into t1 values (3,'three');
insert into t1 values (4,'four');
# basic functionality
set @a=2;
prepare stmt1 from 'select * from t1 where a <= ?';
execute stmt1 using @a;
set @a=3;
execute stmt1 using @a;
# non-existant statement
--error 1243
deallocate prepare no_such_statement;
--error 1210
execute stmt1;
# Nesting ps commands is not allowed:
--error 1064
prepare stmt2 from 'prepare nested_stmt from "select 1"';
--error 1064
prepare stmt2 from 'execute stmt1';
--error 1064
prepare stmt2 from 'deallocate prepare z';
# PS insert
prepare stmt3 from 'insert into t1 values (?,?)';
set @arg1=5, @arg2='five';
execute stmt3 using @arg1, @arg2;
select * from t1 where a>3;
# PS update
prepare stmt4 from 'update t1 set a=? where b=?';
set @arg1=55, @arg2='five';
execute stmt4 using @arg1, @arg2;
select * from t1 where a>3;
# PS create/delete
prepare stmt4 from 'create table t2 (a int)';
execute stmt4;
prepare stmt4 from 'drop table t2';
execute stmt4;
# Do something that will cause error
--error 1051
execute stmt4;
# placeholders in result field names.
prepare stmt5 from 'select ? + a from t1';
set @a=1;
execute stmt5 using @a;
execute stmt5 using @no_such_var;
set @nullvar=1;
set @nullvar=NULL;
execute stmt5 using @nullvar;
set @nullvar2=NULL;
execute stmt5 using @nullvar2;
drop table t1;
...@@ -635,16 +635,21 @@ void Item_param::set_double(double value) ...@@ -635,16 +635,21 @@ void Item_param::set_double(double value)
} }
void Item_param::set_value(const char *str, uint length) void Item_param::set_value(const char *str, uint length, CHARSET_INFO *ci)
{ {
DBUG_ENTER("Item_param::set_value"); DBUG_ENTER("Item_param::set_value");
str_value.copy(str,length,default_charset()); str_value.copy(str,length,ci);
item_type= STRING_ITEM; item_type= STRING_ITEM;
value_is_set= 1; value_is_set= 1;
DBUG_PRINT("info", ("string: %s", str_value.ptr())); DBUG_PRINT("info", ("string: %s", str_value.ptr()));
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
void Item_param::set_value(const char *str, uint length)
{
set_value(str, length, default_charset());
}
void Item_param::set_time(TIME *tm, timestamp_type type) void Item_param::set_time(TIME *tm, timestamp_type type)
{ {
...@@ -1493,7 +1498,7 @@ bool Item::send(Protocol *protocol, String *buffer) ...@@ -1493,7 +1498,7 @@ bool Item::send(Protocol *protocol, String *buffer)
} }
case MYSQL_TYPE_TINY: case MYSQL_TYPE_TINY:
{ {
longlong nr; longlong nr;
nr= val_int(); nr= val_int();
if (!null_value) if (!null_value)
result= protocol->store_tiny(nr); result= protocol->store_tiny(nr);
......
...@@ -413,6 +413,7 @@ public: ...@@ -413,6 +413,7 @@ public:
void set_int(longlong i); void set_int(longlong i);
void set_double(double i); void set_double(double i);
void set_value(const char *str, uint length); void set_value(const char *str, uint length);
void set_value(const char *str, uint length, CHARSET_INFO *ci);
void set_long_str(const char *str, ulong length); void set_long_str(const char *str, ulong length);
void set_long_binary(const char *str, ulong length); void set_long_binary(const char *str, ulong length);
void set_longdata(const char *str, ulong length); void set_longdata(const char *str, ulong length);
......
...@@ -133,6 +133,7 @@ static SYMBOL symbols[] = { ...@@ -133,6 +133,7 @@ static SYMBOL symbols[] = {
{ "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM)}, { "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM)},
{ "DAY_MINUTE", SYM(DAY_MINUTE_SYM)}, { "DAY_MINUTE", SYM(DAY_MINUTE_SYM)},
{ "DAY_SECOND", SYM(DAY_SECOND_SYM)}, { "DAY_SECOND", SYM(DAY_SECOND_SYM)},
{ "DEALLOCATE", SYM(DEALLOCATE_SYM)},
{ "DEC", SYM(DECIMAL_SYM)}, { "DEC", SYM(DECIMAL_SYM)},
{ "DECIMAL", SYM(DECIMAL_SYM)}, { "DECIMAL", SYM(DECIMAL_SYM)},
{ "DEFAULT", SYM(DEFAULT)}, { "DEFAULT", SYM(DEFAULT)},
...@@ -322,6 +323,7 @@ static SYMBOL symbols[] = { ...@@ -322,6 +323,7 @@ static SYMBOL symbols[] = {
{ "POINT", SYM(POINT_SYM)}, { "POINT", SYM(POINT_SYM)},
{ "POLYGON", SYM(POLYGON)}, { "POLYGON", SYM(POLYGON)},
{ "PRECISION", SYM(PRECISION)}, { "PRECISION", SYM(PRECISION)},
{ "PREPARE", SYM(PREPARE_SYM)},
{ "PREV", SYM(PREV_SYM)}, { "PREV", SYM(PREV_SYM)},
{ "PRIMARY", SYM(PRIMARY_SYM)}, { "PRIMARY", SYM(PRIMARY_SYM)},
{ "PRIVILEGES", SYM(PRIVILEGES)}, { "PRIVILEGES", SYM(PRIVILEGES)},
......
...@@ -634,8 +634,10 @@ int mysqld_show_column_types(THD *thd); ...@@ -634,8 +634,10 @@ int mysqld_show_column_types(THD *thd);
int mysqld_help (THD *thd, const char *text); int mysqld_help (THD *thd, const char *text);
/* sql_prepare.cc */ /* sql_prepare.cc */
void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length); int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
LEX_STRING *name=NULL);
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length); void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name);
void mysql_stmt_free(THD *thd, char *packet); void mysql_stmt_free(THD *thd, char *packet);
void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
......
...@@ -4809,6 +4809,9 @@ struct show_var_st status_vars[]= { ...@@ -4809,6 +4809,9 @@ struct show_var_st status_vars[]= {
{"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG}, {"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG},
{"Com_update", (char*) (com_stat+(uint) SQLCOM_UPDATE),SHOW_LONG}, {"Com_update", (char*) (com_stat+(uint) SQLCOM_UPDATE),SHOW_LONG},
{"Com_update_multi", (char*) (com_stat+(uint) SQLCOM_UPDATE_MULTI),SHOW_LONG}, {"Com_update_multi", (char*) (com_stat+(uint) SQLCOM_UPDATE_MULTI),SHOW_LONG},
{"Com_prepare_sql", (char*) (com_stat+(uint) SQLCOM_PREPARE), SHOW_LONG},
{"Com_execute_sql", (char*) (com_stat+(uint) SQLCOM_EXECUTE), SHOW_LONG},
{"Com_dealloc_sql", (char*) (com_stat+(uint) SQLCOM_DEALLOCATE_PREPARE), SHOW_LONG},
{"Connections", (char*) &thread_id, SHOW_LONG_CONST}, {"Connections", (char*) &thread_id, SHOW_LONG_CONST},
{"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG}, {"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG},
{"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG}, {"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG},
......
...@@ -78,7 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry) ...@@ -78,7 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry)
my_free((char*) entry,MYF(0)); my_free((char*) entry,MYF(0));
} }
/**************************************************************************** /****************************************************************************
** Thread specific functions ** Thread specific functions
****************************************************************************/ ****************************************************************************/
...@@ -160,7 +159,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0), ...@@ -160,7 +159,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
16); 16);
else else
bzero((char*) &user_var_events, sizeof(user_var_events)); bzero((char*) &user_var_events, sizeof(user_var_events));
/* Protocol */ /* Protocol */
protocol= &protocol_simple; // Default protocol protocol= &protocol_simple; // Default protocol
protocol_simple.init(this); protocol_simple.init(this);
...@@ -1199,6 +1198,7 @@ Statement::Statement(THD *thd) ...@@ -1199,6 +1198,7 @@ Statement::Statement(THD *thd)
query_length(0), query_length(0),
free_list(0) free_list(0)
{ {
name.str= NULL;
init_sql_alloc(&mem_root, init_sql_alloc(&mem_root,
thd->variables.query_alloc_block_size, thd->variables.query_alloc_block_size,
thd->variables.query_prealloc_size); thd->variables.query_prealloc_size);
...@@ -1282,17 +1282,52 @@ static void delete_statement_as_hash_key(void *key) ...@@ -1282,17 +1282,52 @@ static void delete_statement_as_hash_key(void *key)
delete (Statement *) key; delete (Statement *) key;
} }
byte *get_stmt_name_hash_key(Statement *entry, uint *length,
my_bool not_used __attribute__((unused)))
{
*length=(uint) entry->name.length;
return (byte*) entry->name.str;
}
C_MODE_END C_MODE_END
Statement_map::Statement_map() : Statement_map::Statement_map() :
last_found_statement(0) last_found_statement(0)
{ {
enum { START_HASH_SIZE = 16 }; enum
hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0, {
START_STMT_HASH_SIZE = 16,
START_NAME_HASH_SIZE = 16
};
hash_init(&st_hash, default_charset_info, START_STMT_HASH_SIZE, 0, 0,
get_statement_id_as_hash_key, get_statement_id_as_hash_key,
delete_statement_as_hash_key, MYF(0)); delete_statement_as_hash_key, MYF(0));
hash_init(&names_hash, &my_charset_bin, START_NAME_HASH_SIZE, 0, 0,
(hash_get_key) get_stmt_name_hash_key,
NULL,MYF(0));
} }
int Statement_map::insert(Statement *statement)
{
int rc= my_hash_insert(&st_hash, (byte *) statement);
if (rc == 0)
last_found_statement= statement;
if (statement->name.str)
{
/*
If there is a statement with the same name, remove it. It is ok to
remove old and fail to insert new one at the same time.
*/
Statement *old_stmt;
if ((old_stmt= find_by_name(&statement->name)))
erase(old_stmt);
if ((rc= my_hash_insert(&names_hash, (byte*)statement)))
hash_delete(&st_hash, (byte*)statement);
}
return rc;
}
bool select_dumpvar::send_data(List<Item> &items) bool select_dumpvar::send_data(List<Item> &items)
{ {
List_iterator_fast<Item_func_set_user_var> li(vars); List_iterator_fast<Item_func_set_user_var> li(vars);
......
...@@ -456,6 +456,7 @@ public: ...@@ -456,6 +456,7 @@ public:
*/ */
bool allow_sum_func; bool allow_sum_func;
LEX_STRING name; /* name for named prepared statements */
LEX *lex; // parse tree descriptor LEX *lex; // parse tree descriptor
/* /*
Points to the query associated with this statement. It's const, but Points to the query associated with this statement. It's const, but
...@@ -522,8 +523,14 @@ public: ...@@ -522,8 +523,14 @@ public:
/* /*
Used to seek all existing statements in the connection Container for all statements created/used in a connection.
Deletes all statements in destructor. Statements in Statement_map have unique Statement::id (guaranteed by id
assignment in Statement::Statement)
Non-empty statement names are unique too: attempt to insert a new statement
with duplicate name causes older statement to be deleted
Statements are auto-deleted when they are removed from the map and when the
map is deleted.
*/ */
class Statement_map class Statement_map
...@@ -531,12 +538,14 @@ class Statement_map ...@@ -531,12 +538,14 @@ class Statement_map
public: public:
Statement_map(); Statement_map();
int insert(Statement *statement) int insert(Statement *statement);
Statement *find_by_name(LEX_STRING *name)
{ {
int rc= my_hash_insert(&st_hash, (byte *) statement); Statement *stmt;
if (rc == 0) stmt= (Statement*)hash_search(&names_hash, (byte*)name->str,
last_found_statement= statement; name->length);
return rc; return stmt;
} }
Statement *find(ulong id) Statement *find(ulong id)
...@@ -550,15 +559,21 @@ public: ...@@ -550,15 +559,21 @@ public:
{ {
if (statement == last_found_statement) if (statement == last_found_statement)
last_found_statement= 0; last_found_statement= 0;
if (statement->name.str)
{
hash_delete(&names_hash, (byte *) statement);
}
hash_delete(&st_hash, (byte *) statement); hash_delete(&st_hash, (byte *) statement);
} }
~Statement_map() ~Statement_map()
{ {
hash_free(&st_hash); hash_free(&st_hash);
hash_free(&names_hash);
} }
private: private:
HASH st_hash; HASH st_hash;
HASH names_hash;
Statement *last_found_statement; Statement *last_found_statement;
}; };
......
...@@ -76,6 +76,7 @@ enum enum_sql_command { ...@@ -76,6 +76,7 @@ enum enum_sql_command {
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES,
SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE,
/* This should be the last !!! */ /* This should be the last !!! */
SQLCOM_END SQLCOM_END
}; };
...@@ -592,6 +593,11 @@ typedef struct st_lex ...@@ -592,6 +593,11 @@ typedef struct st_lex
bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog; bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog;
bool derived_tables; bool derived_tables;
bool safe_to_cache_query; bool safe_to_cache_query;
/* Prepared statements SQL syntax:*/
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
LEX_STRING prepared_stmt_code; /* Statement query (in PREPARE )*/
/* Names of user variables holding parameters (in EXECUTE) */
List<LEX_STRING> prepared_stmt_params;
st_lex() {} st_lex() {}
inline void uncacheable(uint8 cause) inline void uncacheable(uint8 cause)
{ {
......
...@@ -1957,7 +1957,44 @@ mysql_execute_command(THD *thd) ...@@ -1957,7 +1957,44 @@ mysql_execute_command(THD *thd)
} }
break; break;
} }
case SQLCOM_PREPARE:
{
DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
lex->prepared_stmt_name.length,
lex->prepared_stmt_name.str,
lex->prepared_stmt_code.length,
lex->prepared_stmt_code.str));
thd->command= COM_PREPARE;
if (!mysql_stmt_prepare(thd, lex->prepared_stmt_code.str,
lex->prepared_stmt_code.length + 1,
&lex->prepared_stmt_name))
send_ok(thd, 0L, 0L, "Statement prepared");
break;
}
case SQLCOM_EXECUTE:
{
DBUG_PRINT("info", ("EXECUTE: %.*s\n",
lex->prepared_stmt_name.length,
lex->prepared_stmt_name.str));
mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
lex->prepared_stmt_params.empty();
break;
}
case SQLCOM_DEALLOCATE_PREPARE:
{
Statement* stmt;
DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n",
lex->prepared_stmt_name.length,
lex->prepared_stmt_name.str));
if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
{
thd->stmt_map.erase(stmt);
send_ok(thd);
}
else
send_error(thd,ER_UNKNOWN_STMT_HANDLER,"Undefined prepared statement");
break;
}
case SQLCOM_DO: case SQLCOM_DO:
if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) || if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) ||
(res= open_and_lock_tables(thd,tables)))) (res= open_and_lock_tables(thd,tables))))
......
This diff is collapsed.
...@@ -431,6 +431,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -431,6 +431,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token MEDIUMTEXT %token MEDIUMTEXT
%token NUMERIC_SYM %token NUMERIC_SYM
%token PRECISION %token PRECISION
%token PREPARE_SYM
%token DEALLOCATE_SYM
%token QUICK %token QUICK
%token REAL %token REAL
%token SIGNED_SYM %token SIGNED_SYM
...@@ -723,6 +725,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -723,6 +725,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
precision subselect_start opt_and charset precision subselect_start opt_and charset
subselect_end select_var_list select_var_list_init help opt_len subselect_end select_var_list select_var_list_init help opt_len
opt_extended_describe opt_extended_describe
prepare execute deallocate
END_OF_INPUT END_OF_INPUT
%type <NONE> %type <NONE>
...@@ -759,10 +762,12 @@ verb_clause: ...@@ -759,10 +762,12 @@ verb_clause:
| checksum | checksum
| commit | commit
| create | create
| deallocate
| delete | delete
| describe | describe
| do | do
| drop | drop
| execute
| flush | flush
| grant | grant
| handler | handler
...@@ -774,6 +779,7 @@ verb_clause: ...@@ -774,6 +779,7 @@ verb_clause:
| optimize | optimize
| keycache | keycache
| preload | preload
| prepare
| purge | purge
| rename | rename
| repair | repair
...@@ -794,6 +800,72 @@ verb_clause: ...@@ -794,6 +800,72 @@ verb_clause:
| use | use
; ;
deallocate:
DEALLOCATE_SYM PREPARE_SYM ident
{
THD *thd=YYTHD;
LEX *lex= thd->lex;
if (thd->command == COM_PREPARE)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
lex->sql_command= SQLCOM_DEALLOCATE_PREPARE;
lex->prepared_stmt_name= $3;
};
prepare:
PREPARE_SYM ident FROM TEXT_STRING_sys
{
THD *thd=YYTHD;
LEX *lex= thd->lex;
if (thd->command == COM_PREPARE)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
lex->sql_command= SQLCOM_PREPARE;
lex->prepared_stmt_name= $2;
lex->prepared_stmt_code= $4;
};
execute:
EXECUTE_SYM ident
{
THD *thd=YYTHD;
LEX *lex= thd->lex;
if (thd->command == COM_PREPARE)
{
yyerror(ER(ER_SYNTAX_ERROR));
YYABORT;
}
lex->sql_command= SQLCOM_EXECUTE;
lex->prepared_stmt_name= $2;
}
execute_using
{}
;
execute_using:
/* nothing */
| USING execute_var_list
;
execute_var_list:
execute_var_list ',' execute_var_ident
| execute_var_ident
;
execute_var_ident: '@' ident_or_text
{
LEX *lex=Lex;
LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING));
if (!lexstr || lex->prepared_stmt_params.push_back(lexstr))
YYABORT;
}
;
/* help */ /* help */
help: help:
...@@ -4899,6 +4971,7 @@ keyword: ...@@ -4899,6 +4971,7 @@ keyword:
| DATETIME {} | DATETIME {}
| DATE_SYM {} | DATE_SYM {}
| DAY_SYM {} | DAY_SYM {}
| DEALLOCATE_SYM {}
| DELAY_KEY_WRITE_SYM {} | DELAY_KEY_WRITE_SYM {}
| DES_KEY_FILE {} | DES_KEY_FILE {}
| DIRECTORY_SYM {} | DIRECTORY_SYM {}
...@@ -4996,6 +5069,7 @@ keyword: ...@@ -4996,6 +5069,7 @@ keyword:
| PASSWORD {} | PASSWORD {}
| POINT_SYM {} | POINT_SYM {}
| POLYGON {} | POLYGON {}
| PREPARE_SYM {}
| PREV_SYM {} | PREV_SYM {}
| PROCESS {} | PROCESS {}
| PROCESSLIST_SYM {} | PROCESSLIST_SYM {}
......
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