mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 12:32:27 +01:00
Merge sgluhov@bk-internal.mysql.com:/home/bk/mysql-5.0
into gluh.mysql.r18.ru:/home/gluh/mysql-5.0.sp
This commit is contained in:
commit
e44a0c2aeb
36 changed files with 600 additions and 32 deletions
|
@ -327,4 +327,5 @@
|
|||
#define ER_SP_DUP_VAR 1308
|
||||
#define ER_SP_DUP_COND 1309
|
||||
#define ER_SP_DUP_CURS 1310
|
||||
#define ER_ERROR_MESSAGES 311
|
||||
#define ER_SP_CANT_ALTER 1311
|
||||
#define ER_ERROR_MESSAGES 312
|
||||
|
|
|
@ -187,6 +187,11 @@ end;
|
|||
call p();
|
||||
ERROR HY000: Cursor is not open
|
||||
drop procedure p;
|
||||
alter procedure bar3 SECURITY INVOKER;
|
||||
ERROR HY000: PROCEDURE bar3 does not exist
|
||||
alter procedure bar3 name
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;
|
||||
ERROR 42000: Identifier name 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' is too long
|
||||
drop table t1;
|
||||
drop table if exists t1;
|
||||
create table t1 (val int, x float);
|
||||
|
|
|
@ -734,6 +734,9 @@ n f
|
|||
19 121645100408832000
|
||||
20 2432902008176640000
|
||||
drop table fac;
|
||||
show function status like '%f%';
|
||||
Name Type Creator Modified Created Suid Comment
|
||||
fac function root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 Y
|
||||
drop procedure ifac;
|
||||
drop function fac;
|
||||
drop table if exists primes;
|
||||
|
@ -794,6 +797,38 @@ set p = p+2;
|
|||
end;
|
||||
end while;
|
||||
end;
|
||||
show create procedure opp;
|
||||
Procedure Create Procedure
|
||||
opp create procedure opp(n bigint unsigned, out pp bool)
|
||||
begin
|
||||
declare r double;
|
||||
declare b, s bigint unsigned default 0;
|
||||
set r = sqrt(n);
|
||||
again:
|
||||
loop
|
||||
if s = 45 then
|
||||
set b = b+200, s = 0;
|
||||
else
|
||||
begin
|
||||
declare p bigint unsigned;
|
||||
select t.p into p from test.primes t where t.i = s;
|
||||
if b+p > r then
|
||||
set pp = 1;
|
||||
leave again;
|
||||
end if;
|
||||
if mod(n, b+p) = 0 then
|
||||
set pp = 0;
|
||||
leave again;
|
||||
end if;
|
||||
set s = s+1;
|
||||
end;
|
||||
end if;
|
||||
end loop;
|
||||
end
|
||||
show procedure status like '%p%';
|
||||
Name Type Creator Modified Created Suid Comment
|
||||
ip procedure root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 Y
|
||||
opp procedure root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 Y
|
||||
call ip(200);
|
||||
select * from primes where i=45 or i=100 or i=199;
|
||||
i p
|
||||
|
@ -803,5 +838,22 @@ i p
|
|||
drop table primes;
|
||||
drop procedure opp;
|
||||
drop procedure ip;
|
||||
create procedure bar(x char(16), y int)
|
||||
comment "111111111111" SECURITY INVOKER
|
||||
insert into test.t1 values (x, y);
|
||||
show procedure status like 'bar';
|
||||
Name Type Creator Modified Created Suid Comment
|
||||
bar procedure root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 N 111111111111
|
||||
alter procedure bar name bar2 comment "2222222222" SECURITY DEFINER;
|
||||
alter procedure bar2 name bar comment "3333333333";
|
||||
alter procedure bar;
|
||||
show create procedure bar;
|
||||
Procedure Create Procedure
|
||||
bar create procedure bar(x char(16), y int)
|
||||
comment "111111111111" SECURITY INVOKER
|
||||
insert into test.t1 values (x, y)
|
||||
show procedure status like 'bar';
|
||||
Name Type Creator Modified Created Suid Comment
|
||||
bar procedure root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 Y 3333333333
|
||||
drop table t1;
|
||||
drop table t2;
|
||||
|
|
|
@ -256,6 +256,12 @@ end|
|
|||
call p()|
|
||||
drop procedure p|
|
||||
|
||||
--error 1282
|
||||
alter procedure bar3 SECURITY INVOKER|
|
||||
--error 1059
|
||||
alter procedure bar3 name
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|
|
||||
|
||||
drop table t1|
|
||||
|
||||
--disable_warnings
|
||||
|
|
|
@ -852,6 +852,8 @@ end|
|
|||
call ifac(20)|
|
||||
select * from fac|
|
||||
drop table fac|
|
||||
--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
|
||||
show function status like '%f%'|
|
||||
drop procedure ifac|
|
||||
drop function fac|
|
||||
|
||||
|
@ -928,6 +930,9 @@ begin
|
|||
end;
|
||||
end while;
|
||||
end|
|
||||
show create procedure opp|
|
||||
--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
|
||||
show procedure status like '%p%'|
|
||||
|
||||
# This isn't the fastest way in the world to compute prime numbers, so
|
||||
# don't be too ambitious. ;-)
|
||||
|
@ -939,6 +944,19 @@ drop table primes|
|
|||
drop procedure opp|
|
||||
drop procedure ip|
|
||||
|
||||
# Comment & suid
|
||||
create procedure bar(x char(16), y int)
|
||||
comment "111111111111" SECURITY INVOKER
|
||||
insert into test.t1 values (x, y)|
|
||||
--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
|
||||
show procedure status like 'bar'|
|
||||
alter procedure bar name bar2 comment "2222222222" SECURITY DEFINER|
|
||||
alter procedure bar2 name bar comment "3333333333"|
|
||||
alter procedure bar|
|
||||
show create procedure bar|
|
||||
--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00'
|
||||
show procedure status like 'bar'|
|
||||
|
||||
delimiter ;|
|
||||
drop table t1;
|
||||
drop table t2;
|
||||
|
|
|
@ -130,6 +130,7 @@ static SYMBOL symbols[] = {
|
|||
{ "DECLARE", SYM(DECLARE_SYM),0,0},
|
||||
{ "DES_KEY_FILE", SYM(DES_KEY_FILE),0,0},
|
||||
{ "DEFAULT", SYM(DEFAULT),0,0},
|
||||
{ "DEFINER", SYM(DEFINER),0,0},
|
||||
{ "DELAYED", SYM(DELAYED_SYM),0,0},
|
||||
{ "DELAY_KEY_WRITE", SYM(DELAY_KEY_WRITE_SYM),0,0},
|
||||
{ "DELETE", SYM(DELETE_SYM),0,0},
|
||||
|
@ -232,6 +233,7 @@ static SYMBOL symbols[] = {
|
|||
{ "ISOLATION", SYM(ISOLATION),0,0},
|
||||
{ "ISSUER", SYM(ISSUER_SYM),0,0},
|
||||
{ "ITERATE", SYM(ITERATE_SYM),0,0},
|
||||
{ "INVOKER", SYM(INVOKER),0,0},
|
||||
{ "JOIN", SYM(JOIN_SYM),0,0},
|
||||
{ "KEY", SYM(KEY_SYM),0,0},
|
||||
{ "KEYS", SYM(KEYS),0,0},
|
||||
|
@ -295,6 +297,7 @@ static SYMBOL symbols[] = {
|
|||
{ "MULTILINESTRING", SYM(MULTILINESTRING),0,0},
|
||||
{ "MULTIPOINT", SYM(MULTIPOINT),0,0},
|
||||
{ "MULTIPOLYGON", SYM(MULTIPOLYGON),0,0},
|
||||
{ "NAME", SYM(NAME_SYM),0,0},
|
||||
{ "NAMES", SYM(NAMES_SYM),0,0},
|
||||
{ "NATURAL", SYM(NATURAL),0,0},
|
||||
{ "NATIONAL", SYM(NATIONAL_SYM),0,0},
|
||||
|
@ -367,6 +370,7 @@ static SYMBOL symbols[] = {
|
|||
{ "SAVEPOINT", SYM(SAVEPOINT_SYM),0,0},
|
||||
{ "SECOND", SYM(SECOND_SYM),0,0},
|
||||
{ "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_SYM),0,0},
|
||||
{ "SECURITY", SYM(SECURITY),0,0},
|
||||
{ "SEPARATOR", SYM(SEPARATOR_SYM),0,0},
|
||||
{ "SELECT", SYM(SELECT_SYM),0,0},
|
||||
{ "SENSITIVE", SYM(SENSITIVE_SYM),0,0},
|
||||
|
|
|
@ -323,3 +323,4 @@ character-set=latin2
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -317,3 +317,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -325,3 +325,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -314,3 +314,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -319,3 +319,4 @@ character-set=latin7
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -314,3 +314,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -326,3 +326,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -314,3 +314,4 @@ character-set=greek
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -316,3 +316,4 @@ character-set=latin2
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -314,3 +314,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -316,3 +316,4 @@ character-set=ujis
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -314,3 +314,4 @@ character-set=euckr
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -316,3 +316,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -316,3 +316,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -318,3 +318,4 @@ character-set=latin2
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -315,3 +315,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -318,3 +318,4 @@ character-set=latin2
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -316,3 +316,4 @@ character-set=koi8r
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -309,3 +309,4 @@ character-set=cp1250
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -322,3 +322,4 @@ character-set=latin2
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -316,3 +316,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -314,3 +314,4 @@ character-set=latin1
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
|
@ -319,3 +319,4 @@ character-set=koi8u
|
|||
"Duplicate variable: %s"
|
||||
"Duplicate condition: %s"
|
||||
"Duplicate cursor: %s"
|
||||
"Failed to ALTER %s %s"
|
||||
|
|
261
sql/sp.cc
261
sql/sp.cc
|
@ -230,8 +230,8 @@ db_create_routine(THD *thd, int type,
|
|||
(uint)strlen(creator),
|
||||
system_charset_info);
|
||||
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time();
|
||||
if (suid)
|
||||
table->field[MYSQL_PROC_FIELD_SUID]->store((longlong)suid);
|
||||
if (!suid)
|
||||
table->field[MYSQL_PROC_FIELD_SUID]->store((longlong) 1);
|
||||
if (comment)
|
||||
table->field[MYSQL_PROC_FIELD_COMMENT]->store(comment, commentlen,
|
||||
system_charset_info);
|
||||
|
@ -268,6 +268,182 @@ db_drop_routine(THD *thd, int type, char *name, uint namelen)
|
|||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
static int
|
||||
db_update_routine(THD *thd, int type, char *name, uint namelen,
|
||||
char *newname, uint newnamelen,
|
||||
char *comment, uint commentlen, enum suid_behaviour suid)
|
||||
{
|
||||
DBUG_ENTER("db_update_routine");
|
||||
DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
|
||||
TABLE *table;
|
||||
int ret;
|
||||
bool opened;
|
||||
|
||||
ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened);
|
||||
if (ret == SP_OK)
|
||||
{
|
||||
store_record(table,record[1]);
|
||||
((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time();
|
||||
if (suid)
|
||||
table->field[MYSQL_PROC_FIELD_SUID]->store((longlong) suid);
|
||||
if (newname)
|
||||
table->field[MYSQL_PROC_FIELD_NAME]->store(newname,
|
||||
newnamelen,
|
||||
system_charset_info);
|
||||
if (comment)
|
||||
table->field[MYSQL_PROC_FIELD_COMMENT]->store(comment,
|
||||
commentlen,
|
||||
system_charset_info);
|
||||
if ((table->file->update_row(table->record[1],table->record[0])))
|
||||
ret= SP_WRITE_ROW_FAILED;
|
||||
}
|
||||
if (opened)
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
struct st_used_field
|
||||
{
|
||||
const char *field_name;
|
||||
uint field_length;
|
||||
enum enum_field_types field_type;
|
||||
Field *field;
|
||||
};
|
||||
|
||||
static struct st_used_field init_fields[]=
|
||||
{
|
||||
{ "Name", NAME_LEN, MYSQL_TYPE_STRING, 0},
|
||||
{ "Type", 9, MYSQL_TYPE_STRING, 0},
|
||||
{ "Creator", 77, MYSQL_TYPE_STRING, 0},
|
||||
{ "Modified", 0, MYSQL_TYPE_TIMESTAMP, 0},
|
||||
{ "Created", 0, MYSQL_TYPE_TIMESTAMP, 0},
|
||||
{ "Suid", 1, MYSQL_TYPE_STRING, 0},
|
||||
{ "Comment", NAME_LEN, MYSQL_TYPE_STRING, 0},
|
||||
{ 0, 0, MYSQL_TYPE_STRING, 0}
|
||||
};
|
||||
|
||||
int print_field_values(THD *thd, TABLE *table,
|
||||
struct st_used_field *used_fields,
|
||||
int type, const char *wild)
|
||||
{
|
||||
Protocol *protocol= thd->protocol;
|
||||
|
||||
if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type)
|
||||
{
|
||||
String *tmp_string= new String();
|
||||
struct st_used_field *used_field= used_fields;
|
||||
get_field(&thd->mem_root,
|
||||
used_field->field,
|
||||
tmp_string);
|
||||
if (!wild || !wild[0] || !wild_compare(tmp_string->ptr(), wild, 0))
|
||||
{
|
||||
protocol->prepare_for_resend();
|
||||
protocol->store(tmp_string);
|
||||
for (used_field++;
|
||||
used_field->field_name;
|
||||
used_field++)
|
||||
{
|
||||
switch (used_field->field_type) {
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
{
|
||||
TIME tmp_time;
|
||||
((Field_timestamp *) used_field->field)->get_time(&tmp_time);
|
||||
protocol->store(&tmp_time);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
String *tmp_string1= new String();
|
||||
get_field(&thd->mem_root, used_field->field, tmp_string1);
|
||||
protocol->store(tmp_string1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (protocol->write())
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
db_show_routine_status(THD *thd, int type, const char *wild)
|
||||
{
|
||||
DBUG_ENTER("db_show_routine_status");
|
||||
|
||||
TABLE *table;
|
||||
TABLE_LIST tables;
|
||||
|
||||
memset(&tables, 0, sizeof(tables));
|
||||
tables.db= (char*)"mysql";
|
||||
tables.real_name= tables.alias= (char*)"proc";
|
||||
|
||||
if (! (table= open_ltable(thd, &tables, TL_READ)))
|
||||
{
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
Item *item;
|
||||
List<Item> field_list;
|
||||
struct st_used_field *used_field;
|
||||
st_used_field used_fields[array_elements(init_fields)];
|
||||
memcpy((char*) used_fields, (char*) init_fields, sizeof(used_fields));
|
||||
/* Init header */
|
||||
for (used_field= &used_fields[0];
|
||||
used_field->field_name;
|
||||
used_field++)
|
||||
{
|
||||
switch (used_field->field_type) {
|
||||
case MYSQL_TYPE_TIMESTAMP:
|
||||
field_list.push_back(item=new Item_datetime(used_field->field_name));
|
||||
break;
|
||||
default:
|
||||
field_list.push_back(item=new Item_empty_string(used_field->field_name,
|
||||
used_field->
|
||||
field_length));
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Print header */
|
||||
if (thd->protocol->send_fields(&field_list,1))
|
||||
goto err_case;
|
||||
|
||||
/* Init fields */
|
||||
setup_tables(&tables);
|
||||
for (used_field= &used_fields[0];
|
||||
used_field->field_name;
|
||||
used_field++)
|
||||
{
|
||||
TABLE_LIST *not_used;
|
||||
Item_field *field= new Item_field("mysql", "proc",
|
||||
used_field->field_name);
|
||||
if (!(used_field->field= find_field_in_tables(thd, field, &tables,
|
||||
¬_used, TRUE)))
|
||||
goto err_case1;
|
||||
}
|
||||
|
||||
table->file->index_init(0);
|
||||
table->file->index_first(table->record[0]);
|
||||
if (print_field_values(thd, table, used_fields, type, wild))
|
||||
goto err_case1;
|
||||
while (!table->file->index_next(table->record[0]))
|
||||
{
|
||||
if (print_field_values(thd, table, used_fields, type, wild))
|
||||
goto err_case1;
|
||||
}
|
||||
send_eof(thd);
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
err_case1:
|
||||
send_eof(thd);
|
||||
err_case:
|
||||
close_thread_tables(thd);
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -326,6 +502,46 @@ sp_drop_procedure(THD *thd, char *name, uint namelen)
|
|||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
int
|
||||
sp_update_procedure(THD *thd, char *name, uint namelen,
|
||||
char *newname, uint newnamelen,
|
||||
char *comment, uint commentlen, enum suid_behaviour suid)
|
||||
{
|
||||
DBUG_ENTER("sp_update_procedure");
|
||||
DBUG_PRINT("enter", ("name: %*s", namelen, name));
|
||||
sp_head *sp;
|
||||
int ret;
|
||||
|
||||
sp= sp_cache_remove(&thd->sp_proc_cache, name, namelen);
|
||||
if (sp)
|
||||
delete sp;
|
||||
ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen,
|
||||
newname, newnamelen,
|
||||
comment, commentlen, suid);
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
int
|
||||
sp_show_create_procedure(THD *thd, LEX_STRING *name)
|
||||
{
|
||||
DBUG_ENTER("sp_show_create_procedure");
|
||||
DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
|
||||
sp_head *sp;
|
||||
|
||||
sp= sp_find_procedure(thd, name);
|
||||
if (sp)
|
||||
DBUG_RETURN(sp->show_create_procedure(thd));
|
||||
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
int
|
||||
db_show_status_procedure(THD *thd, const char *wild)
|
||||
{
|
||||
DBUG_ENTER("db_show_status_procedure");
|
||||
DBUG_RETURN(db_show_routine_status(thd, TYPE_ENUM_PROCEDURE, wild));
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -381,6 +597,47 @@ sp_drop_function(THD *thd, char *name, uint namelen)
|
|||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
int
|
||||
sp_update_function(THD *thd, char *name, uint namelen,
|
||||
char *newname, uint newnamelen,
|
||||
char *comment, uint commentlen, enum suid_behaviour suid)
|
||||
{
|
||||
DBUG_ENTER("sp_update_procedure");
|
||||
DBUG_PRINT("enter", ("name: %*s", namelen, name));
|
||||
sp_head *sp;
|
||||
int ret;
|
||||
|
||||
sp= sp_cache_remove(&thd->sp_func_cache, name, namelen);
|
||||
if (sp)
|
||||
delete sp;
|
||||
ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, namelen,
|
||||
newname, newnamelen,
|
||||
comment, commentlen, suid);
|
||||
|
||||
DBUG_RETURN(ret);
|
||||
}
|
||||
|
||||
int
|
||||
sp_show_create_function(THD *thd, LEX_STRING *name)
|
||||
{
|
||||
DBUG_ENTER("sp_show_create_function");
|
||||
DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
|
||||
sp_head *sp;
|
||||
|
||||
sp= sp_find_function(thd, name);
|
||||
if (sp)
|
||||
DBUG_RETURN(sp->show_create_function(thd));
|
||||
|
||||
DBUG_RETURN(1);
|
||||
}
|
||||
|
||||
int
|
||||
db_show_status_function(THD *thd, const char *wild)
|
||||
{
|
||||
DBUG_ENTER("db_show_status_function");
|
||||
DBUG_RETURN(db_show_routine_status(thd, TYPE_ENUM_FUNCTION, wild));
|
||||
}
|
||||
|
||||
// QQ Temporary until the function call detection in sql_lex has been reworked.
|
||||
bool
|
||||
sp_function_exists(THD *thd, LEX_STRING *name)
|
||||
|
|
22
sql/sp.h
22
sql/sp.h
|
@ -38,6 +38,17 @@ int
|
|||
sp_drop_procedure(THD *thd, char *name, uint namelen);
|
||||
|
||||
|
||||
int
|
||||
sp_update_procedure(THD *thd, char *name, uint namelen,
|
||||
char *newname, uint newnamelen,
|
||||
char *comment, uint commentlen, enum suid_behaviour suid);
|
||||
|
||||
int
|
||||
sp_show_create_procedure(THD *thd, LEX_STRING *name);
|
||||
|
||||
int
|
||||
db_show_status_procedure(THD *thd, const char *wild);
|
||||
|
||||
sp_head *
|
||||
sp_find_function(THD *thd, LEX_STRING *name);
|
||||
|
||||
|
@ -48,6 +59,17 @@ sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen,
|
|||
int
|
||||
sp_drop_function(THD *thd, char *name, uint namelen);
|
||||
|
||||
int
|
||||
sp_update_function(THD *thd, char *name, uint namelen,
|
||||
char *newname, uint newnamelen,
|
||||
char *comment, uint commentlen, enum suid_behaviour suid);
|
||||
|
||||
int
|
||||
sp_show_create_function(THD *thd, LEX_STRING *name);
|
||||
|
||||
int
|
||||
db_show_status_function(THD *thd, const char *wild);
|
||||
|
||||
// QQ Temporary until the function call detection in sql_lex has been reworked.
|
||||
bool
|
||||
sp_function_exists(THD *thd, LEX_STRING *name);
|
||||
|
|
|
@ -131,7 +131,7 @@ sp_head::sp_head()
|
|||
}
|
||||
|
||||
void
|
||||
sp_head::init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid)
|
||||
sp_head::init(LEX_STRING *name, LEX *lex)
|
||||
{
|
||||
DBUG_ENTER("sp_head::init");
|
||||
const char *dstr = (const char*)lex->buf;
|
||||
|
@ -141,16 +141,6 @@ sp_head::init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid)
|
|||
m_name.str= lex->thd->strmake(name->str, name->length);
|
||||
m_defstr.length= lex->end_of_query - lex->buf;
|
||||
m_defstr.str= lex->thd->strmake(dstr, m_defstr.length);
|
||||
|
||||
m_comment.length= 0;
|
||||
m_comment.str= 0;
|
||||
if (comment)
|
||||
{
|
||||
m_comment.length= comment->length;
|
||||
m_comment.str= comment->str;
|
||||
}
|
||||
|
||||
m_suid= suid;
|
||||
lex->spcont= m_pcont= new sp_pcontext();
|
||||
my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
|
||||
DBUG_VOID_RETURN;
|
||||
|
@ -605,7 +595,56 @@ sp_head::backpatch(sp_label_t *lab)
|
|||
}
|
||||
}
|
||||
|
||||
int
|
||||
sp_head::show_create_procedure(THD *thd)
|
||||
{
|
||||
Protocol *protocol= thd->protocol;
|
||||
char buff[2048];
|
||||
String buffer(buff, sizeof(buff), system_charset_info);
|
||||
int res;
|
||||
List<Item> field_list;
|
||||
|
||||
DBUG_ENTER("sp_head::show_create_procedure");
|
||||
DBUG_PRINT("info", ("procedure %s", m_name.str));
|
||||
|
||||
field_list.push_back(new Item_empty_string("Procedure",NAME_LEN));
|
||||
// 1024 is for not to confuse old clients
|
||||
field_list.push_back(new Item_empty_string("Create Procedure",
|
||||
max(buffer.length(),1024)));
|
||||
if (protocol->send_fields(&field_list, 1))
|
||||
DBUG_RETURN(1);
|
||||
protocol->prepare_for_resend();
|
||||
protocol->store(m_name.str, m_name.length, system_charset_info);
|
||||
protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
|
||||
res= protocol->write();
|
||||
send_eof(thd);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
|
||||
int
|
||||
sp_head::show_create_function(THD *thd)
|
||||
{
|
||||
Protocol *protocol= thd->protocol;
|
||||
char buff[2048];
|
||||
String buffer(buff, sizeof(buff), system_charset_info);
|
||||
int res;
|
||||
List<Item> field_list;
|
||||
|
||||
DBUG_ENTER("sp_head::show_create_function");
|
||||
DBUG_PRINT("info", ("procedure %s", m_name.str));
|
||||
|
||||
field_list.push_back(new Item_empty_string("Function",NAME_LEN));
|
||||
field_list.push_back(new Item_empty_string("Create Function",
|
||||
max(buffer.length(),1024)));
|
||||
if (protocol->send_fields(&field_list, 1))
|
||||
DBUG_RETURN(1);
|
||||
protocol->prepare_for_resend();
|
||||
protocol->store(m_name.str, m_name.length, system_charset_info);
|
||||
protocol->store(m_defstr.str, m_defstr.length, system_charset_info);
|
||||
res= protocol->write();
|
||||
send_eof(thd);
|
||||
DBUG_RETURN(res);
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
//
|
||||
|
|
|
@ -66,7 +66,21 @@ public:
|
|||
|
||||
// Initialize after we have reset mem_root
|
||||
void
|
||||
init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid);
|
||||
init(LEX_STRING *name, LEX *lex);
|
||||
|
||||
void
|
||||
init_options(LEX_STRING *comment, enum suid_behaviour suid)
|
||||
{
|
||||
m_comment.length= 0;
|
||||
m_comment.str= 0;
|
||||
if (comment)
|
||||
{
|
||||
m_comment.length= comment->length;
|
||||
m_comment.str= comment->str;
|
||||
}
|
||||
m_suid= suid ? suid - 1 : 1;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
create(THD *thd);
|
||||
|
@ -83,6 +97,12 @@ public:
|
|||
int
|
||||
execute_procedure(THD *thd, List<Item> *args);
|
||||
|
||||
int
|
||||
show_create_procedure(THD *thd);
|
||||
|
||||
int
|
||||
show_create_function(THD *thd);
|
||||
|
||||
inline void
|
||||
add_instr(sp_instr *i)
|
||||
{
|
||||
|
|
|
@ -79,11 +79,18 @@ enum enum_sql_command {
|
|||
SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
|
||||
SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL,
|
||||
SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
|
||||
SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC,
|
||||
SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC,
|
||||
|
||||
/* This should be the last !!! */
|
||||
SQLCOM_END
|
||||
};
|
||||
|
||||
enum suid_behaviour
|
||||
{
|
||||
IS_DEFAULT_SUID= 0, IS_NOT_SUID, IS_SUID
|
||||
};
|
||||
|
||||
typedef List<Item> List_item;
|
||||
|
||||
typedef struct st_lex_master_info
|
||||
|
@ -544,6 +551,7 @@ typedef struct st_lex
|
|||
enum enum_enable_or_disable alter_keys_onoff;
|
||||
enum enum_var_type option_type;
|
||||
enum tablespace_op_type tablespace_op;
|
||||
enum suid_behaviour suid;
|
||||
uint uint_geom_type;
|
||||
uint grant, grant_tot_col, which_columns;
|
||||
uint fk_delete_opt, fk_update_opt, fk_match_option;
|
||||
|
|
|
@ -3394,22 +3394,34 @@ mysql_execute_command(THD *thd)
|
|||
case SQLCOM_ALTER_PROCEDURE:
|
||||
case SQLCOM_ALTER_FUNCTION:
|
||||
{
|
||||
sp_head *sp;
|
||||
|
||||
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
|
||||
sp= sp_find_procedure(thd, &lex->udf.name);
|
||||
else
|
||||
sp= sp_find_function(thd, &lex->udf.name);
|
||||
if (! sp)
|
||||
res= -1;
|
||||
uint newname_len= 0;
|
||||
if (lex->name)
|
||||
newname_len= strlen(lex->name);
|
||||
if (newname_len > NAME_LEN)
|
||||
{
|
||||
net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name);
|
||||
net_printf(thd, ER_TOO_LONG_IDENT, lex->name);
|
||||
goto error;
|
||||
}
|
||||
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
|
||||
res= sp_update_procedure(thd, lex->udf.name.str, lex->udf.name.length,
|
||||
lex->name, newname_len, lex->comment->str,
|
||||
lex->comment->length, lex->suid);
|
||||
else
|
||||
res= sp_update_function(thd, lex->udf.name.str, lex->udf.name.length,
|
||||
lex->name, newname_len, lex->comment->str,
|
||||
lex->comment->length, lex->suid);
|
||||
switch (res)
|
||||
{
|
||||
/* QQ This is an no-op right now, since we haven't
|
||||
put the characteristics in yet. */
|
||||
case SP_OK:
|
||||
send_ok(thd);
|
||||
break;
|
||||
case SP_KEY_NOT_FOUND:
|
||||
net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name);
|
||||
goto error;
|
||||
default:
|
||||
net_printf(thd, ER_SP_CANT_ALTER, SP_COM_STRING(lex),lex->udf.name);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -3463,6 +3475,51 @@ mysql_execute_command(THD *thd)
|
|||
}
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_CREATE_PROC:
|
||||
{
|
||||
res= -1;
|
||||
if (lex->udf.name.length > NAME_LEN)
|
||||
{
|
||||
net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str);
|
||||
goto error;
|
||||
}
|
||||
res= sp_show_create_procedure(thd, &lex->udf.name);
|
||||
if (res == SP_KEY_NOT_FOUND)
|
||||
{
|
||||
net_printf(thd, ER_SP_DOES_NOT_EXIST,
|
||||
SP_COM_STRING(lex), lex->udf.name.str);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_CREATE_FUNC:
|
||||
{
|
||||
if (lex->udf.name.length > NAME_LEN)
|
||||
{
|
||||
net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str);
|
||||
goto error;
|
||||
}
|
||||
res= sp_show_create_function(thd, &lex->udf.name);
|
||||
if (res == SP_KEY_NOT_FOUND)
|
||||
{
|
||||
net_printf(thd, ER_SP_DOES_NOT_EXIST,
|
||||
SP_COM_STRING(lex), lex->udf.name.str);
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_STATUS_PROC:
|
||||
{
|
||||
res= db_show_status_procedure(thd, (lex->wild ?
|
||||
lex->wild->ptr() : NullS));
|
||||
break;
|
||||
}
|
||||
case SQLCOM_SHOW_STATUS_FUNC:
|
||||
{
|
||||
res= db_show_status_function(thd, (lex->wild ?
|
||||
lex->wild->ptr() : NullS));
|
||||
break;
|
||||
}
|
||||
default: /* Impossible */
|
||||
send_ok(thd);
|
||||
break;
|
||||
|
|
|
@ -137,6 +137,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||
%token CREATE
|
||||
%token CROSS
|
||||
%token CUBE_SYM
|
||||
%token DEFINER
|
||||
%token DELETE_SYM
|
||||
%token DUAL_SYM
|
||||
%token DO_SYM
|
||||
|
@ -269,6 +270,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||
%token INOUT_SYM
|
||||
%token INTO
|
||||
%token IN_SYM
|
||||
%token INVOKER
|
||||
%token ISOLATION
|
||||
%token JOIN_SYM
|
||||
%token KEYS
|
||||
|
@ -311,6 +313,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||
%token MEDIUM_SYM
|
||||
%token MIN_ROWS
|
||||
%token NAMES_SYM
|
||||
%token NAME_SYM
|
||||
%token NATIONAL_SYM
|
||||
%token NATURAL
|
||||
%token NEW_SYM
|
||||
|
@ -361,6 +364,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||
%token ROW_FORMAT_SYM
|
||||
%token ROW_SYM
|
||||
%token RTREE_SYM
|
||||
%token SECURITY
|
||||
%token SET
|
||||
%token SEPARATOR_SYM
|
||||
%token SERIAL_SYM
|
||||
|
@ -619,7 +623,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||
ULONGLONG_NUM field_ident select_alias ident ident_or_text
|
||||
UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
|
||||
NCHAR_STRING opt_component
|
||||
SP_FUNC ident_or_spfunc sp_opt_label
|
||||
SP_FUNC ident_or_spfunc sp_opt_label sp_comment sp_newname
|
||||
|
||||
%type <lex_str_ptr>
|
||||
opt_table_alias
|
||||
|
@ -745,7 +749,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
|||
union_clause union_list union_option
|
||||
precision subselect_start opt_and charset
|
||||
subselect_end select_var_list select_var_list_init help opt_len
|
||||
statement
|
||||
statement sp_suid
|
||||
END_OF_INPUT
|
||||
|
||||
%type <NONE> call sp_proc_stmts sp_proc_stmt
|
||||
|
@ -1021,7 +1025,7 @@ create:
|
|||
/* Order is important here: new - reset - init */
|
||||
sp= new sp_head();
|
||||
sp->reset_thd_mem_root(YYTHD);
|
||||
sp->init(&$3, lex, 0, 0);
|
||||
sp->init(&$3, lex);
|
||||
|
||||
sp->m_type= TYPE_ENUM_PROCEDURE;
|
||||
lex->sphead= sp;
|
||||
|
@ -1037,6 +1041,10 @@ create:
|
|||
{
|
||||
Lex->spcont->set_params();
|
||||
}
|
||||
sp_comment sp_suid
|
||||
{
|
||||
Lex->sphead->init_options(&$9, Lex->suid);
|
||||
}
|
||||
sp_proc_stmt
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
|
@ -1075,7 +1083,7 @@ create_function_tail:
|
|||
/* Order is important here: new - reset - init */
|
||||
sp= new sp_head();
|
||||
sp->reset_thd_mem_root(YYTHD);
|
||||
sp->init(&lex->udf.name, lex, 0, 0);
|
||||
sp->init(&lex->udf.name, lex);
|
||||
|
||||
sp->m_type= TYPE_ENUM_FUNCTION;
|
||||
lex->sphead= sp;
|
||||
|
@ -1095,6 +1103,10 @@ create_function_tail:
|
|||
{
|
||||
Lex->sphead->m_returns= (enum enum_field_types)$7;
|
||||
}
|
||||
sp_comment sp_suid
|
||||
{
|
||||
Lex->sphead->init_options(&$9, Lex->suid);
|
||||
}
|
||||
sp_proc_stmt
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
|
@ -1107,6 +1119,23 @@ create_function_tail:
|
|||
}
|
||||
;
|
||||
|
||||
sp_comment:
|
||||
/* Empty */ { $$.str=0; }
|
||||
| COMMENT_SYM TEXT_STRING_sys { $$= $2; }
|
||||
;
|
||||
|
||||
sp_newname:
|
||||
/* Empty */ { $$.str=0; }
|
||||
| NAME_SYM ident { $$= $2; }
|
||||
;
|
||||
|
||||
|
||||
sp_suid:
|
||||
/* Empty */ { Lex->suid= IS_DEFAULT_SUID; }
|
||||
| SECURITY DEFINER { Lex->suid= IS_SUID; }
|
||||
| SECURITY INVOKER { Lex->suid= IS_NOT_SUID; }
|
||||
;
|
||||
|
||||
call:
|
||||
CALL_SYM ident_or_spfunc
|
||||
{
|
||||
|
@ -2573,7 +2602,7 @@ alter:
|
|||
lex->sql_command=SQLCOM_ALTER_DB;
|
||||
lex->name=$3.str;
|
||||
}
|
||||
| ALTER PROCEDURE ident
|
||||
| ALTER PROCEDURE ident sp_newname sp_comment sp_suid
|
||||
/* QQ Characteristics missing for now */
|
||||
opt_restrict
|
||||
{
|
||||
|
@ -2583,8 +2612,10 @@ alter:
|
|||
put the characteristics in yet. */
|
||||
lex->sql_command= SQLCOM_ALTER_PROCEDURE;
|
||||
lex->udf.name= $3;
|
||||
lex->name= $4.str;
|
||||
lex->comment= &$5;
|
||||
}
|
||||
| ALTER FUNCTION_SYM ident
|
||||
| ALTER FUNCTION_SYM ident sp_newname sp_comment sp_suid
|
||||
/* QQ Characteristics missing for now */
|
||||
opt_restrict
|
||||
{
|
||||
|
@ -2594,6 +2625,8 @@ alter:
|
|||
put the characteristics in yet. */
|
||||
lex->sql_command= SQLCOM_ALTER_FUNCTION;
|
||||
lex->udf.name= $3;
|
||||
lex->name= $4.str;
|
||||
lex->comment= &$5;
|
||||
}
|
||||
;
|
||||
|
||||
|
@ -5012,7 +5045,26 @@ show_param:
|
|||
| SLAVE STATUS_SYM
|
||||
{
|
||||
Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT;
|
||||
};
|
||||
}
|
||||
| CREATE PROCEDURE ident
|
||||
{
|
||||
Lex->sql_command = SQLCOM_SHOW_CREATE_PROC;
|
||||
Lex->udf.name= $3;
|
||||
}
|
||||
| CREATE FUNCTION_SYM ident
|
||||
{
|
||||
Lex->sql_command = SQLCOM_SHOW_CREATE_FUNC;
|
||||
Lex->udf.name= $3;
|
||||
}
|
||||
| PROCEDURE STATUS_SYM wild
|
||||
{
|
||||
Lex->sql_command = SQLCOM_SHOW_STATUS_PROC;
|
||||
}
|
||||
| FUNCTION_SYM STATUS_SYM wild
|
||||
{
|
||||
Lex->sql_command = SQLCOM_SHOW_STATUS_FUNC;
|
||||
};
|
||||
|
||||
|
||||
master_or_binary:
|
||||
MASTER_SYM
|
||||
|
@ -5560,6 +5612,7 @@ keyword:
|
|||
| DATETIME {}
|
||||
| DATE_SYM {}
|
||||
| DAY_SYM {}
|
||||
| DEFINER {}
|
||||
| DELAY_KEY_WRITE_SYM {}
|
||||
| DES_KEY_FILE {}
|
||||
| DIRECTORY_SYM {}
|
||||
|
@ -5595,6 +5648,7 @@ keyword:
|
|||
| HOSTS_SYM {}
|
||||
| HOUR_SYM {}
|
||||
| IDENTIFIED_SYM {}
|
||||
| INVOKER {}
|
||||
| IMPORT {}
|
||||
| INDEXES {}
|
||||
| ISOLATION {}
|
||||
|
@ -5637,6 +5691,7 @@ keyword:
|
|||
| MULTILINESTRING {}
|
||||
| MULTIPOINT {}
|
||||
| MULTIPOLYGON {}
|
||||
| NAME_SYM {}
|
||||
| NAMES_SYM {}
|
||||
| NATIONAL_SYM {}
|
||||
| NCHAR_SYM {}
|
||||
|
@ -5680,6 +5735,7 @@ keyword:
|
|||
| RTREE_SYM {}
|
||||
| SAVEPOINT_SYM {}
|
||||
| SECOND_SYM {}
|
||||
| SECURITY {}
|
||||
| SERIAL_SYM {}
|
||||
| SERIALIZABLE_SYM {}
|
||||
| SESSION_SYM {}
|
||||
|
|
Loading…
Reference in a new issue