Skip to content
Permalink
Browse files
Refactor parts of Item_func_sp into Item_sp
In preparation for implementing custom aggregate functions, refactor
the common code between regular stored functions and aggregate stored
functions. This includes:

* initialising SP result field
* executing a SP
* access checks

In addition, refactor sp_head::execute_function to take two extra
parameters, a function rcontext and a Query_arena. These two paremeters
were initially initialised and destroyed within
sp_head::execute_function, but for aggregate stored functions we will
require control over their lifetime. The owner of these objects now
becomes Item_sp.

Signed-off-by: Vicențiu Ciorbaru <vicentiu@mariadb.org>
  • Loading branch information
varunraiko authored and cvicentiu committed Dec 4, 2017
1 parent b213f57 commit c12d1ed
Show file tree
Hide file tree
Showing 6 changed files with 296 additions and 259 deletions.
@@ -2730,6 +2730,237 @@ Item* Item_func_or_sum::build_clone(THD *thd)
return copy;
}

Item_sp::Item_sp(THD *thd, Name_resolution_context *context_arg,
sp_name *name_arg) :
context(context_arg), m_name(name_arg), m_sp(NULL), func_ctx(NULL),
sp_result_field(NULL)
{
dummy_table= (TABLE*) thd->calloc(sizeof(TABLE) + sizeof(TABLE_SHARE));
dummy_table->s= (TABLE_SHARE*) (dummy_table + 1);
memset(&sp_mem_root, 0, sizeof(sp_mem_root));
}

const char *
Item_sp::func_name(THD *thd) const
{
/* Calculate length to avoid reallocation of string for sure */
uint len= (((m_name->m_explicit_name ? m_name->m_db.length : 0) +
m_name->m_name.length)*2 + //characters*quoting
2 + // ` and `
(m_name->m_explicit_name ?
3 : 0) + // '`', '`' and '.' for the db
1 + // end of string
ALIGN_SIZE(1)); // to avoid String reallocation
String qname((char *)alloc_root(thd->mem_root, len), len,
system_charset_info);

qname.length(0);
if (m_name->m_explicit_name)
{
append_identifier(thd, &qname, m_name->m_db.str, m_name->m_db.length);
qname.append('.');
}
append_identifier(thd, &qname, m_name->m_name.str, m_name->m_name.length);
return qname.c_ptr_safe();
}

void
Item_sp::cleanup()
{
delete sp_result_field;
sp_result_field= NULL;
m_sp= NULL;
delete func_ctx;
func_ctx= NULL;
free_root(&sp_mem_root, MYF(0));
dummy_table->alias.free();
}

/**
@brief Checks if requested access to function can be granted to user.
If function isn't found yet, it searches function first.
If function can't be found or user don't have requested access
error is raised.
@param thd thread handler
@return Indication if the access was granted or not.
@retval FALSE Access is granted.
@retval TRUE Requested access can't be granted or function doesn't exists.
*/
bool
Item_sp::sp_check_access(THD *thd)
{
DBUG_ENTER("Item_sp::sp_check_access");
DBUG_ASSERT(m_sp);
DBUG_RETURN(m_sp->check_execute_access(thd));
}

/**
@brief Execute function & store value in field.
@return Function returns error status.
@retval FALSE on success.
@retval TRUE if an error occurred.
*/
bool Item_sp::execute(THD *thd, bool *null_value, Item **args, uint arg_count)
{
if (execute_impl(thd, args, arg_count))
{
*null_value= 1;
context->process_error(thd);
if (thd->killed)
thd->send_kill_message();
return true;
}

/* Check that the field (the value) is not NULL. */

*null_value= sp_result_field->is_null();
return (*null_value);
}

/**
@brief Execute function and store the return value in the field.
@note This function was intended to be the concrete implementation of
the interface function execute. This was never realized.
@return The error state.
@retval FALSE on success
@retval TRUE if an error occurred.
*/
bool
Item_sp::execute_impl(THD *thd, Item **args, uint arg_count)
{
Sub_statement_state statement_state;
Security_context *save_security_ctx= thd->security_ctx;
enum enum_sp_data_access access=
(m_sp->daccess() == SP_DEFAULT_ACCESS) ?
SP_DEFAULT_ACCESS_MAPPING : m_sp->daccess();

DBUG_ENTER("Item_sp::execute_impl");

if (context->security_ctx)
{
/* Set view definer security context */
thd->security_ctx= context->security_ctx;
}

if (sp_check_access(thd))
{
thd->security_ctx= save_security_ctx;
DBUG_RETURN(TRUE);
}

/*
Throw an error if a non-deterministic function is called while
statement-based replication (SBR) is active.
*/

if (!m_sp->detistic() && !trust_function_creators &&
(access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) &&
(mysql_bin_log.is_open() &&
thd->variables.binlog_format == BINLOG_FORMAT_STMT))
{
my_error(ER_BINLOG_UNSAFE_ROUTINE, MYF(0));
thd->security_ctx= save_security_ctx;
DBUG_RETURN(TRUE);
}

/*
Disable the binlogging if this is not a SELECT statement. If this is a
SELECT, leave binlogging on, so execute_function() code writes the
function call into binlog.
*/
thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION);

init_sql_alloc(&sp_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
Query_arena call_arena(&sp_mem_root, Query_arena::STMT_INITIALIZED_FOR_SP);

bool err_status= m_sp->execute_function(thd, args, arg_count,
sp_result_field, &func_ctx,
&call_arena);
/* Free Items allocated during function execution. */
delete func_ctx;
func_ctx= NULL;
call_arena.free_items();
free_root(&sp_mem_root, MYF(0));
memset(&sp_mem_root, 0, sizeof(sp_mem_root));

thd->restore_sub_statement_state(&statement_state);

thd->security_ctx= save_security_ctx;
DBUG_RETURN(err_status);
}


/**
@brief Initialize the result field by creating a temporary dummy table
and assign it to a newly created field object. Meta data used to
create the field is fetched from the sp_head belonging to the stored
proceedure found in the stored procedure functon cache.
@note This function should be called from fix_fields to init the result
field. It is some what related to Item_field.
@see Item_field
@param thd A pointer to the session and thread context.
@return Function return error status.
@retval TRUE is returned on an error
@retval FALSE is returned on success.
*/

bool
Item_sp::init_result_field(THD *thd, sp_head *sp, uint max_length,
uint maybe_null, bool *null_value, LEX_CSTRING *name)
{
DBUG_ENTER("Item_sp::init_result_field");

DBUG_ASSERT(m_sp == NULL);
DBUG_ASSERT(sp_result_field == NULL);

if (!(m_sp= sp))
{
my_missing_function_error (m_name->m_name, ErrConvDQName(m_name).ptr());
context->process_error(thd);
DBUG_RETURN(TRUE);
}

/*
A Field needs to be attached to a Table.
Below we "create" a dummy table by initializing
the needed pointers.
*/
dummy_table->alias.set("", 0, table_alias_charset);
dummy_table->in_use= thd;
dummy_table->copy_blobs= TRUE;
dummy_table->s->table_cache_key= empty_clex_str;
dummy_table->s->table_name= empty_clex_str;
dummy_table->maybe_null= maybe_null;

if (!(sp_result_field= m_sp->create_result_field(max_length, name,
dummy_table)))
DBUG_RETURN(TRUE);

if (sp_result_field->pack_length() > sizeof(result_buf))
{
void *tmp;
if (!(tmp= thd->alloc(sp_result_field->pack_length())))
DBUG_RETURN(TRUE);
sp_result_field->move_field((uchar*) tmp);
}
else
sp_result_field->move_field(result_buf);

sp_result_field->null_ptr= (uchar *) null_value;
sp_result_field->null_bit= 1;

DBUG_RETURN(FALSE);
}

/**
@brief
@@ -4471,6 +4471,34 @@ class Item_func_or_sum: public Item_result_field,
Item* build_clone(THD *thd);
};

class sp_head;
class sp_name;
struct st_sp_security_context;

class Item_sp
{
public:
Name_resolution_context *context;
sp_name *m_name;
sp_head *m_sp;
TABLE *dummy_table;
uchar result_buf[64];
sp_rcontext *func_ctx;
MEM_ROOT sp_mem_root;

/*
The result field of the stored function.
*/
Field *sp_result_field;
Item_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name_arg);
const char *func_name(THD *thd) const;
void cleanup();
bool sp_check_access(THD *thd);
bool execute(THD *thd, bool *null_value, Item **args, uint arg_count);
bool execute_impl(THD *thd, Item **args, uint arg_count);
bool init_result_field(THD *thd, sp_head *sp, uint max_length,
uint maybe_null, bool *null_value, LEX_CSTRING *name);
};

class Item_ref :public Item_ident
{

0 comments on commit c12d1ed

Please sign in to comment.