Commit 85547962 authored by bell@sanja.is.com.ua's avatar bell@sanja.is.com.ua

new UDF arguments interface (WL#1017) (SCRUM)

parent 08e5a7e1
...@@ -278,6 +278,8 @@ typedef struct st_udf_args ...@@ -278,6 +278,8 @@ typedef struct st_udf_args
char **args; /* Pointer to argument */ char **args; /* Pointer to argument */
unsigned long *lengths; /* Length of string arguments */ unsigned long *lengths; /* Length of string arguments */
char *maybe_null; /* Set to 1 for all maybe_null args */ char *maybe_null; /* Set to 1 for all maybe_null args */
char **attributes; /* Pointer to attribute name */
unsigned long *attribute_lengths; /* Length of attribute arguments */
} UDF_ARGS; } UDF_ARGS;
/* This holds information about the result */ /* This holds information about the result */
......
...@@ -39,7 +39,7 @@ void item_init(void) ...@@ -39,7 +39,7 @@ void item_init(void)
} }
Item::Item(): Item::Item():
fixed(0) name_length(0), fixed(0)
{ {
marker= 0; marker= 0;
maybe_null=null_value=with_sum_func=unsigned_flag=0; maybe_null=null_value=with_sum_func=unsigned_flag=0;
...@@ -121,6 +121,7 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) ...@@ -121,6 +121,7 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
{ {
/* Empty string, used by AS or internal function like last_insert_id() */ /* Empty string, used by AS or internal function like last_insert_id() */
name= (char*) str; name= (char*) str;
name_length= 0;
return; return;
} }
while (length && !my_isgraph(cs,*str)) while (length && !my_isgraph(cs,*str))
...@@ -131,12 +132,12 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs) ...@@ -131,12 +132,12 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
if (!my_charset_same(cs, system_charset_info)) if (!my_charset_same(cs, system_charset_info))
{ {
uint32 res_length; uint32 res_length;
name= sql_strmake_with_convert(str, length, cs, name= sql_strmake_with_convert(str, name_length= length, cs,
MAX_ALIAS_NAME, system_charset_info, MAX_ALIAS_NAME, system_charset_info,
&res_length); &res_length);
} }
else else
name=sql_strmake(str, min(length,MAX_ALIAS_NAME)); name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME)));
} }
......
...@@ -106,6 +106,7 @@ public: ...@@ -106,6 +106,7 @@ public:
my_string name; /* Name from select */ my_string name; /* Name from select */
Item *next; Item *next;
uint32 max_length; uint32 max_length;
uint name_length; /* Length of name */
uint8 marker,decimals; uint8 marker,decimals;
my_bool maybe_null; /* If item may be null */ my_bool maybe_null; /* If item may be null */
my_bool null_value; /* if item is null */ my_bool null_value; /* if item is null */
......
...@@ -1498,11 +1498,16 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, ...@@ -1498,11 +1498,16 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func,
const_item_cache&=item->const_item(); const_item_cache&=item->const_item();
f_args.arg_type[i]=item->result_type(); f_args.arg_type[i]=item->result_type();
} }
//TODO: why all folowing memory is not allocated with 1 call of sql_alloc?
if (!(buffers=new String[arg_count]) || if (!(buffers=new String[arg_count]) ||
!(f_args.args= (char**) sql_alloc(arg_count * sizeof(char *))) || !(f_args.args= (char**) sql_alloc(arg_count * sizeof(char *))) ||
!(f_args.lengths=(ulong*) sql_alloc(arg_count * sizeof(long))) || !(f_args.lengths= (ulong*) sql_alloc(arg_count * sizeof(long))) ||
!(f_args.maybe_null=(char*) sql_alloc(arg_count * sizeof(char))) || !(f_args.maybe_null= (char*) sql_alloc(arg_count * sizeof(char))) ||
!(num_buffer= (char*) sql_alloc(ALIGN_SIZE(sizeof(double))*arg_count))) !(num_buffer= (char*) sql_alloc(arg_count *
ALIGN_SIZE(sizeof(double)))) ||
!(f_args.attributes= (char**) sql_alloc(arg_count * sizeof(char *))) ||
!(f_args.attribute_lengths= (ulong*) sql_alloc(arg_count *
sizeof(long))))
{ {
free_udf(u_d); free_udf(u_d);
DBUG_RETURN(1); DBUG_RETURN(1);
...@@ -1521,8 +1526,10 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func, ...@@ -1521,8 +1526,10 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func,
for (uint i=0; i < arg_count; i++) for (uint i=0; i < arg_count; i++)
{ {
f_args.args[i]=0; f_args.args[i]=0;
f_args.lengths[i]=arguments[i]->max_length; f_args.lengths[i]= arguments[i]->max_length;
f_args.maybe_null[i]=(char) arguments[i]->maybe_null; f_args.maybe_null[i]= (char) arguments[i]->maybe_null;
f_args.attributes[i]= arguments[i]->name;
f_args.attribute_lengths[i]= arguments[i]->name_length;
switch(arguments[i]->type()) { switch(arguments[i]->type()) {
case Item::STRING_ITEM: // Constant string ! case Item::STRING_ITEM: // Constant string !
......
...@@ -2986,6 +2986,8 @@ mysql_execute_command(THD *thd) ...@@ -2986,6 +2986,8 @@ mysql_execute_command(THD *thd)
break; break;
#ifdef HAVE_DLOPEN #ifdef HAVE_DLOPEN
sp_head *sph= sp_find_function(thd, &lex->udf.name); sp_head *sph= sp_find_function(thd, &lex->udf.name);
// close & unlock table opened by sp_find_function
close_thread_tables(thd);
if (sph) if (sph)
{ {
net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str); net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str);
......
...@@ -630,13 +630,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -630,13 +630,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%type <item> %type <item>
literal text_literal insert_ident order_ident literal text_literal insert_ident order_ident
simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr
table_wild no_in_expr expr_expr simple_expr no_and_expr table_wild no_in_expr expr_expr simple_expr no_and_expr udf_expr
using_list expr_or_default set_expr_or_default interval_expr using_list expr_or_default set_expr_or_default interval_expr
param_marker singlerow_subselect singlerow_subselect_init param_marker singlerow_subselect singlerow_subselect_init
exists_subselect exists_subselect_init sp_opt_default exists_subselect exists_subselect_init sp_opt_default
%type <item_list> %type <item_list>
expr_list udf_expr_list when_list ident_list ident_list_arg expr_list sp_expr_list udf_expr_list udf_expr_list2 when_list
ident_list ident_list_arg
%type <key_type> %type <key_type>
key_type opt_unique_or_fulltext key_type opt_unique_or_fulltext
...@@ -702,7 +703,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -702,7 +703,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
select_item_list select_item values_list no_braces select_item_list select_item values_list no_braces
opt_limit_clause delete_limit_clause fields opt_values values opt_limit_clause delete_limit_clause fields opt_values values
procedure_list procedure_list2 procedure_item procedure_list procedure_list2 procedure_item
when_list2 expr_list2 handler when_list2 expr_list2 udf_expr_list3 handler
opt_precision opt_ignore opt_column opt_restrict opt_precision opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list field_options field_option grant revoke set lock unlock string_list field_options field_option
field_opt_list opt_binary table_lock_list table_lock field_opt_list opt_binary table_lock_list table_lock
...@@ -3246,7 +3247,7 @@ simple_expr: ...@@ -3246,7 +3247,7 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); } { $$= new Item_func_round($3,$5,1); }
| TRUE_SYM | TRUE_SYM
{ $$= new Item_int((char*) "TRUE",1,1); } { $$= new Item_int((char*) "TRUE",1,1); }
| SP_FUNC '(' udf_expr_list ')' | SP_FUNC '(' sp_expr_list ')'
{ {
sp_add_fun_to_lex(Lex, $1); sp_add_fun_to_lex(Lex, $1);
if ($3) if ($3)
...@@ -3336,10 +3337,43 @@ simple_expr: ...@@ -3336,10 +3337,43 @@ simple_expr:
| EXTRACT_SYM '(' interval FROM expr ')' | EXTRACT_SYM '(' interval FROM expr ')'
{ $$=new Item_extract( $3, $5); }; { $$=new Item_extract( $3, $5); };
udf_expr_list: sp_expr_list:
/* empty */ { $$= NULL; } /* empty */ { $$= NULL; }
| expr_list { $$= $1;}; | expr_list { $$= $1;};
udf_expr_list:
/* empty */ { $$= NULL; }
| udf_expr_list2 { $$= $1;}
;
udf_expr_list2:
{ Select->expr_list.push_front(new List<Item>); }
udf_expr_list3
{ $$= Select->expr_list.pop(); }
;
udf_expr_list3:
udf_expr
{
Select->expr_list.head()->push_back($1);
}
| udf_expr_list3 ',' udf_expr
{
Select->expr_list.head()->push_back($3);
}
;
udf_expr:
remember_name expr remember_end select_alias
{
if ($4.str)
$2->set_name($4.str,$4.length,system_charset_info);
else
$2->set_name($1,(uint) ($3 - $1), YYTHD->charset());
$$= $2;
}
;
sum_expr: sum_expr:
AVG_SYM '(' in_sum_expr ')' AVG_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_avg($3); } { $$=new Item_sum_avg($3); }
......
...@@ -56,7 +56,9 @@ ...@@ -56,7 +56,9 @@
** **
** Function 'myfunc_int' returns summary length of all its arguments. ** Function 'myfunc_int' returns summary length of all its arguments.
** **
** Function 'sequence' returns an sequence starting from a certain number ** Function 'sequence' returns an sequence starting from a certain number.
**
** Function 'myfunc_argument_name' returns name of argument.
** **
** On the end is a couple of functions that converts hostnames to ip and ** On the end is a couple of functions that converts hostnames to ip and
** vice versa. ** vice versa.
...@@ -82,6 +84,7 @@ ...@@ -82,6 +84,7 @@
** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; ** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; ** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"; ** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
** CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so";
** **
** After this the functions will work exactly like native MySQL functions. ** After this the functions will work exactly like native MySQL functions.
** Functions should be created only once. ** Functions should be created only once.
...@@ -94,6 +97,7 @@ ...@@ -94,6 +97,7 @@
** DROP FUNCTION lookup; ** DROP FUNCTION lookup;
** DROP FUNCTION reverse_lookup; ** DROP FUNCTION reverse_lookup;
** DROP FUNCTION avgcost; ** DROP FUNCTION avgcost;
** DROP FUNCTION myfunc_argument_name;
** **
** The CREATE FUNCTION and DROP FUNCTION update the func@mysql table. All ** The CREATE FUNCTION and DROP FUNCTION update the func@mysql table. All
** Active function will be reloaded on every restart of server ** Active function will be reloaded on every restart of server
...@@ -975,4 +979,46 @@ avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* error ) ...@@ -975,4 +979,46 @@ avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* error )
return data->totalprice/double(data->totalquantity); return data->totalprice/double(data->totalquantity);
} }
extern "C" {
my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
char *message);
void myfunc_argument_name_deinit(UDF_INIT *initid);
char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *length, char *null_value,
char *error);
}
my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
char *message)
{
if (args->arg_count != 1)
{
strmov(message,"myfunc_argument_name_init accepts only one argument");
return 1;
}
initid->max_length= args->attribute_lengths[0];
initid->maybe_null= 1;
initid->const_item= 1;
return 0;
}
void myfunc_argument_name_deinit(UDF_INIT *initid) {}
char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result,
unsigned long *length, char *null_value,
char *error)
{
if (!args->attributes[0])
{
null_value= 0;
return 0;
}
(*length)--; // space for ending \0 (for debugging purposes)
if (*length > args->attribute_lengths[0])
*length= args->attribute_lengths[0];
memcpy(result, args->attributes[0], *length);
result[*length]= 0;
return result;
}
#endif /* HAVE_DLOPEN */ #endif /* HAVE_DLOPEN */
...@@ -9,6 +9,7 @@ CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so"; ...@@ -9,6 +9,7 @@ CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so";
CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so"; CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"; CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so";
select metaphon("hello"); select metaphon("hello");
select myfunc_double("hello","world"); select myfunc_double("hello","world");
...@@ -20,6 +21,7 @@ create temporary table t1 (a int,b double); ...@@ -20,6 +21,7 @@ create temporary table t1 (a int,b double);
insert into t1 values (1,5),(1,4),(2,8),(3,9),(4,11); insert into t1 values (1,5),(1,4),(2,8),(3,9),(4,11);
select avgcost(a,b) from t1; select avgcost(a,b) from t1;
select avgcost(a,b) from t1 group by a; select avgcost(a,b) from t1 group by a;
select a, myfunc_argument_name(a) from t1;
drop table t1; drop table t1;
DROP FUNCTION metaphon; DROP FUNCTION metaphon;
...@@ -28,3 +30,4 @@ DROP FUNCTION myfunc_int; ...@@ -28,3 +30,4 @@ DROP FUNCTION myfunc_int;
DROP FUNCTION lookup; DROP FUNCTION lookup;
DROP FUNCTION reverse_lookup; DROP FUNCTION reverse_lookup;
DROP FUNCTION avgcost; DROP FUNCTION avgcost;
DROP FUNCTION myfunc_argument_name;
...@@ -34,6 +34,12 @@ CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so" ...@@ -34,6 +34,12 @@ CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"
Query OK, 0 rows affected Query OK, 0 rows affected
--------------
CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so"
--------------
Query OK, 0 rows affected
-------------- --------------
select metaphon("hello") select metaphon("hello")
-------------- --------------
...@@ -106,6 +112,18 @@ avgcost(a,b) ...@@ -106,6 +112,18 @@ avgcost(a,b)
11.0000 11.0000
4 rows in set 4 rows in set
--------------
select a, myfunc_argument_name(a) from t1;
--------------
a myfunc_argument_name(a)
1 a
1 a
2 a
3 a
4 a
5 rows in set
-------------- --------------
drop table t1 drop table t1
-------------- --------------
...@@ -148,4 +166,10 @@ DROP FUNCTION avgcost ...@@ -148,4 +166,10 @@ DROP FUNCTION avgcost
Query OK, 0 rows affected Query OK, 0 rows affected
--------------
DROP FUNCTION myfunc_argument_name;
--------------
Query OK, 0 rows affected
Bye Bye
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