YoushouldhavereceivedacopyoftheGNUGeneralPublicLicense alongwiththisprogram;ifnot,writetotheFreeSoftware
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
switch (lex->sql_command) { case SQLCOM_SELECT: /* Note,duringpreparedstatementexecutionlex->result isalreadysetevenforasimpleSELECTquery(withoutINTO).
*/ if (lex->result &&
!dynamic_cast<const select_send*>(lex->result) &&
!lex->analyze_stmt)
{
flags= 0; /* This is a SELECT with INTO clause */ break;
} /* fallthrough */ case SQLCOM_ANALYZE: case SQLCOM_OPTIMIZE: case SQLCOM_PRELOAD_KEYS: case SQLCOM_ASSIGN_TO_KEYCACHE: case SQLCOM_CHECKSUM: case SQLCOM_CHECK: case SQLCOM_HA_READ: case SQLCOM_SHOW_AUTHORS: case SQLCOM_SHOW_BINLOGS: case SQLCOM_SHOW_BINLOG_EVENTS: case SQLCOM_SHOW_RELAYLOG_EVENTS: case SQLCOM_SHOW_CHARSETS: case SQLCOM_SHOW_COLLATIONS: case SQLCOM_SHOW_CONTRIBUTORS: case SQLCOM_SHOW_CREATE: case SQLCOM_SHOW_CREATE_DB: case SQLCOM_SHOW_CREATE_FUNC: case SQLCOM_SHOW_CREATE_PROC: case SQLCOM_SHOW_CREATE_PACKAGE: case SQLCOM_SHOW_CREATE_PACKAGE_BODY: case SQLCOM_SHOW_CREATE_EVENT: case SQLCOM_SHOW_CREATE_TRIGGER: case SQLCOM_SHOW_CREATE_USER: 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: case SQLCOM_SHOW_GRANTS: case SQLCOM_SHOW_ENGINE_STATUS: case SQLCOM_SHOW_ENGINE_LOGS: case SQLCOM_SHOW_ENGINE_MUTEX: case SQLCOM_SHOW_EVENTS: case SQLCOM_SHOW_KEYS: case SQLCOM_SHOW_BINLOG_STAT: case SQLCOM_SHOW_OPEN_TABLES: case SQLCOM_SHOW_PRIVILEGES: case SQLCOM_SHOW_PROCESSLIST: case SQLCOM_SHOW_PROC_CODE: case SQLCOM_SHOW_PACKAGE_BODY_CODE: case SQLCOM_SHOW_SLAVE_HOSTS: case SQLCOM_SHOW_SLAVE_STAT: case SQLCOM_SHOW_STATUS: case SQLCOM_SHOW_STATUS_FUNC: case SQLCOM_SHOW_STATUS_PROC: case SQLCOM_SHOW_STATUS_PACKAGE: case SQLCOM_SHOW_STATUS_PACKAGE_BODY: case SQLCOM_SHOW_STORAGE_ENGINES: case SQLCOM_SHOW_TABLES: case SQLCOM_SHOW_TABLE_STATUS: case SQLCOM_SHOW_VARIABLES: case SQLCOM_SHOW_WARNS: case SQLCOM_REPAIR:
flags= sp_head::MULTI_RESULTS; break; /* EXECUTEstatementmayreturnaresultset,butdoesn'thaveto. Wecan't,however,knowitinadvance.Solet'smarkitwith MULTI_RESULTS_DYNAMIC,soaCALLofaroutineusingEXECUTE requirestheclienttosupportCLIENT_MULTI_RESULTS. Note,MULTI_RESULTSisnotusedhereonpurpose:itwouldmake dynamicSQLnotallowedinstored*FUNCTIONs*atall.Returning aresultsetfromafunctionisinsteadcaughtatruntime, inPrepared_statement::execute_loop().
*/ case SQLCOM_EXECUTE: case SQLCOM_EXECUTE_IMMEDIATE:
flags= sp_head::MULTI_RESULTS_DYNAMIC | sp_head::CONTAINS_DYNAMIC_SQL; break; case SQLCOM_PREPARE: case SQLCOM_DEALLOCATE_PREPARE:
flags= sp_head::CONTAINS_DYNAMIC_SQL; break; case SQLCOM_CREATE_TABLE: case SQLCOM_CREATE_SEQUENCE: if (lex->tmp_table())
flags= 0; else
flags= sp_head::HAS_COMMIT_OR_ROLLBACK; break; case SQLCOM_DROP_TABLE: case SQLCOM_DROP_SEQUENCE: if (lex->tmp_table())
flags= 0; else
flags= sp_head::HAS_COMMIT_OR_ROLLBACK; break; case SQLCOM_FLUSH:
flags= sp_head::HAS_SQLCOM_FLUSH; break; case SQLCOM_RESET:
flags= sp_head::HAS_SQLCOM_RESET; break; case SQLCOM_CREATE_INDEX: case SQLCOM_CREATE_DB: case SQLCOM_CREATE_PACKAGE: case SQLCOM_CREATE_PACKAGE_BODY: case SQLCOM_CREATE_VIEW: case SQLCOM_CREATE_TRIGGER: case SQLCOM_CREATE_USER: case SQLCOM_CREATE_ROLE: case SQLCOM_ALTER_TABLE: case SQLCOM_ALTER_SEQUENCE: case SQLCOM_ALTER_USER: case SQLCOM_GRANT: case SQLCOM_GRANT_ROLE: case SQLCOM_REVOKE: case SQLCOM_REVOKE_ROLE: case SQLCOM_BEGIN: case SQLCOM_RENAME_TABLE: case SQLCOM_RENAME_USER: case SQLCOM_DROP_INDEX: case SQLCOM_DROP_DB: case SQLCOM_DROP_PACKAGE: case SQLCOM_DROP_PACKAGE_BODY: case SQLCOM_REVOKE_ALL: case SQLCOM_DROP_USER: case SQLCOM_DROP_ROLE: case SQLCOM_DROP_VIEW: case SQLCOM_DROP_TRIGGER: case SQLCOM_TRUNCATE: case SQLCOM_COMMIT: case SQLCOM_ROLLBACK: case SQLCOM_LOAD: case SQLCOM_LOCK_TABLES: case SQLCOM_CREATE_PROCEDURE: case SQLCOM_CREATE_SPFUNCTION: case SQLCOM_ALTER_PROCEDURE: case SQLCOM_ALTER_FUNCTION: case SQLCOM_DROP_PROCEDURE: case SQLCOM_DROP_FUNCTION: case SQLCOM_CREATE_EVENT: case SQLCOM_ALTER_EVENT: case SQLCOM_DROP_EVENT: case SQLCOM_INSTALL_PLUGIN: case SQLCOM_UNINSTALL_PLUGIN:
flags= sp_head::HAS_COMMIT_OR_ROLLBACK; break; case SQLCOM_DELETE: case SQLCOM_DELETE_MULTI: case SQLCOM_INSERT: case SQLCOM_REPLACE: case SQLCOM_REPLACE_SELECT: case SQLCOM_INSERT_SELECT:
{ /* DELETEnormallydoesn'treturnresultset,butthereare3exceptions: -DELETE...RETURNING -EXPLAINDELETE... -ANALYZEDELETE...
*/ if (!lex->has_returning() && !lex->describe && !lex->analyze_stmt)
flags= 0; else
flags= sp_head::MULTI_RESULTS; break;
} case SQLCOM_UPDATE: case SQLCOM_UPDATE_MULTI:
{ if (!lex->has_returning() && !lex->describe && !lex->analyze_stmt)
flags= 0; else
flags= sp_head::MULTI_RESULTS; break;
} default:
flags= 0; break;
} return flags;
}
bool THD::sp_eval_expr(Field *result_field, Item **expr_item_ptr)
{
DBUG_ENTER("THD::sp_eval_expr");
DBUG_ASSERT(*expr_item_ptr);
Sp_eval_expr_state state(this); /* Save the value in the field. Convert the value if needed. */
DBUG_RETURN(result_field->sp_prepare_and_store_item(this, expr_item_ptr));
}
void sp_head::destroy(sp_head *sp)
{ if (sp)
{ /* Make a copy of main_mem_root as free_root will free the sp */
MEM_ROOT own_root= sp->main_mem_root;
DBUG_PRINT("info", ("mem_root %p moved to %p",
&sp->mem_root, &own_root));
sp->m_thd= nullptr; delete sp;
m_explicit_name= spname->m_explicit_name; /* We have to copy strings to get them into the right memroot. */
DBUG_RETURN(copy_sp_name_internal(&main_mem_root,
spname->m_db, spname->m_name));
}
// TODO(cvicentiu) See if you can drop this bit. This is used to resume // execution from where we left off. if (m_chistics.agg_type == GROUP_AGGREGATE)
ip= thd->spcont->instr_ptr;
/* this 7*STACK_MIN_SIZE is a complex matter with a long history (see it!) */ if (check_stack_overrun(thd, 7 * STACK_MIN_SIZE, (uchar*)&old_packet))
DBUG_RETURN(TRUE);
#ifdef WITH_WSREP if (WSREP(thd))
{ if (((thd->wsrep_trx().state() == wsrep::transaction::s_executing || thd->in_sub_stmt) &&
(thd->is_fatal_error || thd->killed)))
{
WSREP_DEBUG("SP abort err status %d in sub %d trx state %d",
err_status, thd->in_sub_stmt, thd->wsrep_trx().state());
err_status= 1;
thd->is_fatal_error= 1; /* SPwaskilled,anditisnotduetoawsrepconflict. Weskipafter_commandhookatthispointbecause otherwiseitclearstheerror,andcleansupthe wholetransaction.Fornowwejustreturnandfinish ourhandlingoncewearebacktomysql_parse.
SameappliestoaSPexecution,whichwasaborteddue towsreprelatedconflict,butwhichisexecutingassubstatement. SPinsubstatementlevelshouldnotcommitnotrollback, wehavetocallforrollbackisup-mostSPlevel.
*/
WSREP_DEBUG("Skipping after_command hook for killed SP");
} else
{ constbool must_replay= wsrep_must_replay(thd); if (must_replay)
{
WSREP_DEBUG("MUST_REPLAY set after SP, err_status %d trx state: %d",
err_status, thd->wsrep_trx().state());
}
if (wsrep_thd_is_local(thd))
(void) wsrep_after_statement(thd);
/* Resetthereturncodetozeroifthetransactionwas replayedsuccessfully.
*/ if (must_replay && !wsrep_current_error(thd))
{
err_status= 0;
thd->get_stmt_da()->reset_diagnostics_area();
} /* Finalwsreperrorstatusforstatementisknownonlyafter wsrep_after_statement()call.Iftheerrorisset,override errorinthddiagnosticsareaandresetwsrepclient_stateerror sothattheerrordoesnotgetpropagatedviaclient-serverprotocol.
*/ if (wsrep_current_error(thd))
{
wsrep_override_error(thd, wsrep_current_error(thd),
wsrep_current_error_status(thd));
thd->wsrep_cs().reset_error(); /* Reset also thd->killed if it has been set during BF abort. */ if (killed_mask_hard(thd->killed) == KILL_QUERY)
thd->reset_killed(); /* if failed transaction was not replayed, must return with error from here */ if (!must_replay) err_status = 1;
}
}
} #endif/* WITH_WSREP */
thd->m_digest= parent_digest;
#ifdef PROTECT_STATEMENT_MEMROOT if (thd->is_error())
{ // Don't count a call ended with an error as normal run
executed_counter= 0;
main_mem_root.flags &= ~ROOT_FLAG_READ_ONLY;
reset_instrs_executed_counter();
} #endif
} while (!err_status && likely(!thd->killed) &&
likely(!thd->is_fatal_error) &&
!thd->spcont->pause_state);
#ifdefined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
thd->profiling.start_new_query("tail end of routine"); #endif
/* Restore query context. */
if (m_creation_ctx)
m_creation_ctx->restore_env(thd, saved_creation_ctx);
/* Only pop cursors when we're done with group aggregate running. */ if (m_chistics.agg_type != GROUP_AGGREGATE ||
(m_chistics.agg_type == GROUP_AGGREGATE && thd->spcont->quit_func))
thd->spcont->pop_all_cursors(thd); // To avoid memory leaks after an error
if (need_binlog_call)
{
query_id_t q;
reset_dynamic(&thd->user_var_events);
/*
In case of artificially constructed events for function calls
we have separate union for each such event and hence can't use
query_id of real calling statement as the start of all these
unions (this will break logic of replication of user-defined
variables). So we use artificial value which is guaranteed to
be greater than all query_id's of all statements belonging
to previous events/unions.
Possible alternative to this is logging of all function invocations
as one select and not resetting THD::user_var_events before
each invocation.
*/
q= get_query_id();
mysql_bin_log.start_union_events(thd, q + 1);
binlog_save_options= thd->variables.option_bits;
thd->variables.option_bits&= ~OPTION_BIN_LOG;
}
opt_trace_disable_if_no_stored_proc_func_access(thd, this);
/*
Switch to call arena/mem_root so objects like sp_cursor or
Item_cache holders for case expressions can be allocated on it.
TODO: In future we should associate call arena/mem_root with
sp_rcontext and allocate all these objects (and sp_rcontext
itself) on it directly rather than juggle with arenas.
*/
thd->set_n_backup_active_arena(call_arena, &backup_arena);
if (need_binlog_call)
{
mysql_bin_log.stop_union_events(thd);
thd->variables.option_bits= binlog_save_options;
if (thd->binlog_evt_union.unioned_events)
{
int errcode = query_error_code(thd, thd->killed == NOT_KILLED);
Query_log_event qinfo(thd, binlog_buf.ptr(), binlog_buf.length(),
thd->binlog_evt_union.unioned_events_trans, FALSE, FALSE, errcode);
if (mysql_bin_log.write(&qinfo) &&
thd->binlog_evt_union.unioned_events_trans)
{
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, "Invoked ROUTINE modified a transactional table but MySQL " "failed to reflect this change in the binary log");
err_status= TRUE;
}
reset_dynamic(&thd->user_var_events);
/* Forget those values, in case more function calls are binlogged: */
thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt= 0;
thd->auto_inc_intervals_in_cur_stmt_for_binlog.empty();
}
}
if (!err_status && thd->spcont->quit_func)
{
/* We need result only in function but not in trigger */
if (!(*func_ctx)->is_return_value_set())
{
my_error(ER_SP_NORETURNEND, MYF(0), m_name.str);
err_status= TRUE;
}
else
{
/*
Copy back all OUT or INOUT values to the previous frame, or
set global user variables
*/
for (arg_no= 0; arg_no < argcount; arg_no++)
{
err_status= bind_output_param(thd, argp[arg_no], arg_no, octx, *func_ctx);
if (err_status)
break;
}
}
}
/*
Call SP variable destructors both on success and error, to exit with a
clean state, as the caller can catch the error using a condition handler
and continue the execution.
E.g. SYS_REFCURSOR variables will decrement their cursor ref counters.
*/
if (thd->spcont && m_chistics.agg_type != GROUP_AGGREGATE)
thd->spcont->expr_event_handler_not_persistent(thd,
expr_event_t::DESTRUCT_OUT_OF_SCOPE);
thd->spcont= octx;
/*
If not insided a procedure and a function printing warning
messsages.
*/
if (need_binlog_call &&
thd->spcont == NULL && !thd->binlog_evt_union.do_union)
thd->issue_unsafe_warnings();
DBUG_RETURN(err_status);
}
/**
Execute a procedure.
The function does the following steps:
- Set all parameters
- changes security context for SUID routines
- call sp_head::execute
- copy back values of INOUT and OUT parameters
- restores security context
@param thd Thread handle
@param args List of values passed as arguments.
@retval
FALSE on success
@retval
TRUE on error
*/
bool
sp_head::execute_procedure(THD *thd, List<Item> *args)
{
bool err_status= FALSE;
uint params = m_pcont->context_var_count();
uint default_params= m_pcont->default_context_var_count();
/* Query start time may be reset in a multi-stmt SP; keep this for later. */
ulonglong utime_before_sp_exec= thd->utime_after_lock;
sp_rcontext *save_spcont, *octx;
sp_rcontext *nctx = NULL;
bool save_enable_slow_log;
bool save_log_general= false;
sp_package *pkg= get_package();
DBUG_ENTER("sp_head::execute_procedure");
DBUG_PRINT("info", ("procedure %s", m_name.str));
if (m_parent && m_parent->instantiate_if_needed(thd))
DBUG_RETURN(true);
for (uint i= 0 ; i < params ; i++)
{
Item *arg_item= it_args++;
if (!arg_item)
break;
err_status= bind_input_param(thd, arg_item, i, octx, nctx, FALSE);
if (err_status)
break;
}
nctx->set_inited_param_count(args->elements);
/*
Okay, got values for all arguments. Close tables that might be used by
arguments evaluation. If arguments evaluation required prelocking mode,
we'll leave it here.
*/
thd->lex->unit.cleanup();
/*
Disable slow log if:
- Slow logging is enabled (no change needed)
- This is a normal SP (not event log)
- If we have not explicitly disabled logging of SP
*/
if (save_enable_slow_log &&
((!(m_flags & LOG_SLOW_STATEMENTS) &&
(thd->variables.log_slow_disabled_statements & LOG_SLOW_DISABLE_SP))))
{
DBUG_PRINT("info", ("Disabling slow log for the execution"));
thd->enable_slow_log= FALSE;
}
/*
Disable general log if:
- If general log is enabled (no change needed)
- This is a normal SP (not event log)
- If we have not explicitly disabled logging of SP
*/
if (!(thd->variables.option_bits & OPTION_LOG_OFF) &&
(!(m_flags & LOG_GENERAL_LOG) &&
(thd->variables.log_disabled_statements & LOG_DISABLE_SP)))
{
DBUG_PRINT("info", ("Disabling general log for the execution"));
save_log_general= true;
/* disable this bit */
thd->variables.option_bits |= OPTION_LOG_OFF;
}
thd->spcont= nctx;
if (!err_status)
MYSQL_RUN_SP(this, err_status= execute(thd, TRUE));
if (save_log_general)
thd->variables.option_bits &= ~OPTION_LOG_OFF;
thd->enable_slow_log= save_enable_slow_log;
/*
In the case when we weren't able to employ reuse mechanism for
OUT/INOUT parameters, we should reallocate memory. This
allocation should be done on the arena which will live through
all execution of calling routine.
*/
thd->spcont->callers_arena= octx->callers_arena;
if (!err_status && params > 0)
{
List_iterator<Item> it_args(*args);
/*
Copy back all OUT or INOUT values to the previous frame, or
set global user variables
*/
for (uint i= 0 ; i < params ; i++)
{
Item *arg_item= it_args++;
if (!arg_item)
break;
err_status= bind_output_param(thd, arg_item, i, octx, nctx);
if (err_status)
break;
}
}
if (thd->spcont)
thd->spcont->expr_event_handler_not_persistent(thd,
expr_event_t::DESTRUCT_OUT_OF_SCOPE);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (save_security_ctx)
m_security_ctx.restore_security_context(thd, save_security_ctx);
#endif
if (!save_spcont)
delete octx;
if (!pkg)
delete nctx;
thd->spcont= save_spcont;
thd->utime_after_lock= utime_before_sp_exec;
/*
If not insided a procedure and a function printing warning
messsages.
*/
bool need_binlog_call= mysql_bin_log.is_open() &&
(thd->variables.option_bits & OPTION_BIN_LOG) &&
!thd->is_current_stmt_binlog_format_row();
if (need_binlog_call && thd->spcont == NULL &&
!thd->binlog_evt_union.do_union)
thd->issue_unsafe_warnings();
sp_variable *spvar= m_pcont->find_variable(arg_no);
if (!spvar)
DBUG_RETURN(FALSE);
if (!spvar->field_def.type_handler()->is_scalar_type() &&
dynamic_cast<Item_param*>(arg_item))
{
// Item_param cannot store values of non-scalar data types yet
my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0),
spvar->field_def.type_handler()->name().ptr(), "EXECUTE ... USING ?");
DBUG_RETURN(true);
}
if (spvar->mode != sp_variable::MODE_IN)
{
Settable_routine_parameter *srp=
arg_item->get_settable_routine_parameter();
if (!srp)
{
my_error(ER_SP_NOT_VAR_ARG, MYF(0), arg_no+1, ErrConvDQName(this).ptr());
DBUG_RETURN(TRUE);
}
if (is_function)
{
/*
Check if the function is called from SELECT/INSERT/UPDATE/DELETE query
and parameter is OUT or INOUT.
If yes, it is an invalid call - throw error.
*/
if (thd->lex->sql_command == SQLCOM_SELECT ||
thd->lex->sql_command == SQLCOM_INSERT ||
thd->lex->sql_command == SQLCOM_INSERT_SELECT ||
thd->lex->sql_command == SQLCOM_UPDATE ||
thd->lex->sql_command == SQLCOM_DELETE)
{
my_error(ER_SF_OUT_INOUT_ARG_NOT_ALLOWED, MYF(0), arg_no+1, m_name.str);
DBUG_RETURN(TRUE);
}
}
/*
The old value of the actual OUT parameter will be overridden
in the end of the routine execution when copying its new value
from the formal parameter in bind_output_param().
*/
if (Item_splocal *spv= arg_item->get_item_splocal())
{
/*
In case of a SYS_REFCURSOR variable the call for expr_event_handler()
will decrement the ref counter in the referenced cursor in
sp_cursor_array. See Field_sys_refcursor::expr_event_handler()
for details.
*/
sp_rcontext *octx1= spv->rcontext_handler()->get_rcontext(octx);
octx1->get_variable(spv->get_var_idx())->
field->expr_event_handler(thd, expr_event_t::DESTRUCT_OUT_OF_SCOPE);
}
}
else
{
if (nctx->set_parameter(thd, arg_no, &arg_item))
{
DBUG_PRINT("error", ("set variable 2 failed"));
DBUG_RETURN(TRUE);
}
}
/* If this substatement is unsafe, the entire routine is too. */
DBUG_PRINT("info", ("sublex->get_stmt_unsafe_flags: 0x%x",
sublex->get_stmt_unsafe_flags()));
unsafe_flags|= sublex->get_stmt_unsafe_flags();
/*
Add routines which are used by statement to respective set for
this routine.
*/
if (sp_update_sp_used_routines(&m_sroutines, &sublex->sroutines))
DBUG_RETURN(TRUE);
/* If this substatement is a update query, then mark MODIFIES_DATA */
if (is_update_query(sublex->sql_command))
m_flags|= MODIFIES_DATA;
/*
Merge tables used by this statement (but not by its functions or
procedures) to multiset of tables used by this routine.
*/
merge_table_list(thd, sublex->query_tables, sublex);
/* Merge lists of PS parameters. */
oldlex->param_list.append(&sublex->param_list);
/*
Add cpop/hpop : they will be removed or updated later if target is in
the same block or not
*/
sp_instr_hpop *hpop= new (thd->mem_root) sp_instr_hpop(ip++, ctx, 0);
if (hpop == NULL || add_instr(hpop))
return true;
if (push_backpatch(thd, hpop, lab, &m_backpatch_goto, HPOP))
return true;
sp_instr_cpop *cpop= new (thd->mem_root) sp_instr_cpop(ip++, ctx, 0);
if (cpop == NULL || add_instr(cpop))
return true;
if (push_backpatch(thd, cpop, lab, &m_backpatch_goto, CPOP))
return true;
// Add jump with ip=0. IP will be updated when label is found.
sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx);
if (i == NULL || add_instr(i))
return true;
if (push_backpatch(thd, i, lab, &m_backpatch_goto, GOTO))
return true;
return false;
}
/**
Update all instruction with this label in the backpatch list to
the current position.
*/
int
sp_head::new_cont_backpatch(sp_instr_opt_meta *i)
{
m_cont_level+= 1;
if (i)
{
/* Use the cont. destination slot to store the level */
i->m_cont_dest= m_cont_level;
if (m_cont_backpatch.push_front(i))
return 1;
}
return 0;
}
int
sp_head::add_cont_backpatch(sp_instr_opt_meta *i)
{
i->m_cont_dest= m_cont_level;
return m_cont_backpatch.push_front(i);
}
void
sp_head::set_chistics(const st_sp_chistics &chistics)
{
m_chistics.set(chistics);
if (m_chistics.comment.length == 0)
m_chistics.comment.str= 0;
else
m_chistics.comment.str l version="1.0"encoding="UTF-8" ?>
m_chistics.<!--
m_chistics.comment.length);
}
void
sp_head::set_c_chistics(const st_sp_chistics &chistics)
{
// Set all chistics but preserve agg_type.
enum_sp_aggregate_type save_agg_type= agg_type();
set_chistics(chistics);
set_chistics_agg_type(save_agg_type);
}
void
=gregoryjava.lang.StringIndexOutOfBoundsException: Range [46, 44) out of bounds for length 85
st_sp_chistics & )
{
m_created=created;
m_modified= modified;
set_chistics(chistics);
m_sql_mode=sql_mode;
}
void
sp_head:reset_thd_mem_root(THD *thd)
{
DBUG_ENTER("sp_head::reset_thd_mem_root");
m_thd_root= type"24"description="Hoursystem using 1– 'k inpatterns" since="28" />
thd->mem_root= &main_mem_root;
DBUG_PRINT("info", ("mem_root %p moved to thd mem root %p",
&mem_root, &thd->mem_root));
free_list= thd->free_list; // Keep the old list
thd->free_list= NULL; // Start a new one
m_thd= thd;
new_query_arena_is_set= true;
DBUG_VOID_RETURN;
}
/*
In some cases our parser detects a syntax error and calls
LEX::cleanup_lex_after_parse_error() method only after
finishing parsing the whole routine. In such a situation
sp_head::restore_thd_mem_root() will be called twice - the
first time as part of normal parsing process and the second
time by cleanup_lex_after_parse_error().
To avoid ruining active arena/mem_root state in this case we
skip restoration of old arena/mem_root if this method has been
already called for this routine.
*/
if (!new_query_arena_is_set)
DBUG_VOID_RETURN;
Item *flist= free_list; // The old list
set_query_arena(thd); // Get new free_list and mem_root
state= STMT_INITIALIZED_FOR_SP;
DBUG_PRINT("info", ("mem_root %p returned from thd mem root %p",
&mem_root, &thd->mem_root));
thd->free_list= flist; // Restore the old one
thd->mem_root= m_thd_root;
new_query_arena_is_set= false;
DBUG_VOID_RETURN;
}
/**
Check Global-DB-procedure access
@param thd Thread handler
@param privilege requested privilege
@param sp SP to check
@param no_errors FALSE/TRUE - report/don't report error to
the client (using my_error() call).
@retval 0 OK
@retval 1 access denied, error is sent to client
*/
/**
Check if a user has access right to a routine.
@param thd Thread handler
@param sp SP
@param full_access Set to 1 if the user has SELECT right to the 'mysql.proc' able or is the owner of the routine
@retval
false ok
@retval
true error
*/
/**
Add any instruction to SP except declaration of row type.
@param instr Instruction
*/
int sp_head::add_instr(sp_instr *instr)
{
instr->free_list= m_thd->free_list;
m_thd->free_list= 0;
return add_instr_core(instr);
}
/**
Add instruction for %ROWTYPE declaration. Handle this SP instruction
specially to exclude an item created for the DEFAULT clause to be freed
on re-parsing of SP instruction
@param instr SP instruction for %ROWTYPE declaration
@return false on success, true on error
*/
int sp_head::add_instr(sp_instr_cursor_copy_struct *instr)
{
instr->free_list= 0;
m_thd->free_list= 0;
return add_instr_core(instr);
}
/**
Add instruction to SP.
@param instr Instruction
*/
int sp_head::add_instr_core(sp_instr *instr)
{
/*
Memory root of every instruction is designated for permanent
transformations (optimizations) made on the parsed tree during
the first execution. It points to the memory root of the
entire stored procedure, as their life span is equal.
*/
instr->mem_root= &main_mem_root;
instr->m_lineno= m_thd->m_parser_state->m_lip.yylineno;
/*
Check if SP is a trigger and there are Item_trigger_field objects
created on parsing the current SP instruction.
*/
if (m_handler->type() == SP_TYPE_TRIGGER &&
m_cur_instr_trig_field_items.elements)
{
/*
Get a pointer to a list of Item_trigger_field objects owned by
the current SP instruction. This list is used for storing
Item_trigger_field objects that can be created on parsing
the SP instruction's statement. If the current SP instruction is not
an instance of the class sp_lex_instr or derived class then
the value nullptr is returned by the virtual method
get_instr_trig_field_list().
*/
SQL_I_List<Item_trigger_field> *instr_trig_fld_list=
instr->get_instr_trig_field_list();
/*
If the current SP instruction can store a list of Item_trigger_field
objects created on its parsing then move these Item_trigger_field
objects to the SP instruction's list.
*/
if (instr_trig_fld_list)
{
m_cur_instr_trig_field_items.save_and_clear(instr_trig_fld_list);
m_trg_table_fields.insert(
instr_trig_fld_list,
&instr_trig_fld_list->first->next_trig_field_list);
}
}
return insert_dynamic(&m_instr, (uchar*)&instr);
}
bool sp_head::add_instr_jump(THD *thd, sp_pcontext *spcont)
{
sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(), spcont);
return i == NULL || add_instr(i);
}
bool sp_head::add_instr_preturn(THD *thd, sp_pcontext *spcont)
{
sp_instr_preturn *i= new (thd->mem_root)
sp_instr_preturn(instructions(), spcont);
if (i == NULL || add_instr(i))
return true;
return false;
}
/*
Replace an instruction at position to "no operation".
@param thd - use mem_root of this THD for "new".
@param ip - position of the operation
@returns - true on error, false on success
When we need to remove an instruction that during compilation
appeared to be useless (typically as useless jump), we replace
it to a jump to exactly the next instruction.
Such jumps are later removed during sp_head::optimize().
QQ: Perhaps we need a dedicated sp_instr_nop for this purpose.
*/
/**
Do some minimal optimization of the code:
-# Mark used instructions
-# While doing this, shortcut jumps to jump instructions
-# Compact the code, removing unused instructions.
This is the main mark and move loop; it relies on the following methods
in sp_instr and its subclasses:
- opt_mark() : Mark instruction as reachable
- opt_shortcut_jump(): Shortcut jumps to the final destination;
used by opt_mark().
- opt_move() : Update moved instruction
- set_destination() : Set the new destination (jump instructions only)
*/
/*
Forward flow analysis algorithm in the instruction graph:
- first, add the entry point in the graph (the first instruction) to the 'leads' list of paths to explore.
- while there are still leads to explore:
- pick one lead, and follow the path forward. Mark instruction reached.
Stop only if the end of the routine is reached, or the path converge
to code already explored (marked).
- while following a path, collect in the 'leads' list any fork to
another path (caused by conditional jumps instructions), so that these
paths can be explored as well.
*/
/* Add the entry point */
i= get_instr(0);
leads.push_front(i);
/* For each path of code ... */
while (leads.elements != 0)
{
i= leads.pop();
/* Mark the entire path, collecting new leads. */
while (i && ! i->marked)
{
ip= i->opt_mark(this, & leads);
i= get_instr(ip);
}
}
}
#ifndef DBUG_OFF
/**
Return the routine instructions as a result set.
@return 0 if ok, !=0 on error.
*/
if (check_show_routine_access(thd, this, &full_access) || !full_access)
DBUG_RETURN(1);
field_list.push_back(new (thd->mem_root) Item_uint(thd, "Pos", 9),
thd->mem_root);
// 1024 is for not to confuse old clients
field_list.push_back(new (thd->mem_root)
Item_empty_string(thd, "Instruction",
MY_MAX(buffer.length(), 1024)),
thd->mem_root);
if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
DBUG_RETURN(1);
for (ip= 0; (i = get_instr(ip)) ; ip++)
{
/*
Consistency check. If these are different something went wrong
during optimization.
*/
if (ip != i->m_ip)
{
const char *format= "Instruction at position %u has m_ip=%u";
char tmp[sizeof(format) + 2*SP_INSTR_UINT_MAXLEN + 1];
my_snprintf(tmp, sizeof(tmp), format, ip, i->m_ip);
/*
Since this is for debugging purposes only, we don't bother to
introduce a special error code for it.
*/
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, tmp);
}
protocol->prepare_for_resend();
protocol->store_long(ip);
for (uint i= 0 ; i < m_sptabs.records ; i++)
{
tab= (SP_TABLE*) my_hash_element(&m_sptabs, i);
tab->query_lock_count= 0;
}
for (; table ; table= table->next_global)
if (!table->derived && !table->schema_table && !table->table_function &&
table->lock_type != TL_IGNORE)
{
/*
Structure of key for the multi-set is "db\0table\0alias\0".
Since "alias" part can have arbitrary length we use String
object to construct the key. By default String will use
buffer allocated on stack with NAME_LEN bytes reserved for
alias, since in most cases it is going to be smaller than
NAME_LEN bytes.
*/
char tname_buff[(SAFE_NAME_LEN + 1) * 3];
String tname(tname_buff, sizeof(tname_buff), &my_charset_bin);
uint temp_table_key_length;
/*
Upgrade the lock type because this table list will be used
only in pre-locked mode, in which DELAYED inserts are always
converted to normal inserts.
*/
if (table->lock_type == TL_WRITE_DELAYED)
table->lock_type= TL_WRITE;
/*
We ignore alias when we check if table was already marked as temporary
(and therefore should not be prelocked). Otherwise we will erroneously
treat table with same name but with different alias as non-temporary.
*/
if ((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname.ptr(),
tname.length())) ||
((tab= (SP_TABLE*) my_hash_search(&m_sptabs, (uchar *)tname.ptr(),
temp_table_key_length)) &&
tab->temp))
{
if (tab->lock_type < table->lock_type)
tab->lock_type= table->lock_type; // Use the table with the highest lock type
tab->query_lock_count++;
if (tab->query_lock_count > tab->lock_count)
tab->lock_count++;
tab->trg_event_map|= table->trg_event_map;
tab->for_insert_data|= table->for_insert_data;
}
else
{
if (!(tab= thd->calloc<SP_TABLE>(1)))
return FALSE;
if ((lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE ||
lex_for_tmp_check->sql_command == SQLCOM_CREATE_SEQUENCE) &&
lex_for_tmp_check->query_tables == table &&
lex_for_tmp_check->tmp_table())
{
tab->temp= TRUE;
tab->qname.length= temp_table_key_length;
}
else
tab->qname.length= tname.length();
tab->qname.str= (char*) thd->memdup(tname.ptr(), tab->qname.length);
if (!tab->qname.str)
return FALSE;
tab->table_name_length= table->table_name.length;
tab->db_length= table->db.length;
tab->lock_type= table->lock_type;
tab->lock_count= tab->query_lock_count= 1;
tab->trg_event_map= table->trg_event_map;
tab->for_insert_data= table->for_insert_data;
if (my_hash_insert(&m_sptabs, (uchar *)tab))
return FALSE;
}
}
return TRUE;
}
/**
Add tables used by routine to the table list.
Converts multi-set of tables used by this routine to table list and adds
this list to the end of table list specified by 'query_tables_last_ptr'.
Elements of list will be allocated in PS memroot, so this list will be
persistent between PS executions.
@param[in] thd Thread context
@param[in,out] query_tables_last_ptr Pointer to the next_global member of
last element of the list where tables
will be added (or to its root).
@param[in] belong_to_view Uppermost view which uses this routine, 0 if none.
@retval
TRUE if some elements were added
@retval
FALSE otherwise.
*/
/*
Use persistent arena for table list allocation to be PS/SP friendly.
Note that we also have to copy database/table names and alias to PS/SP
memory since current instance of sp_head object can pass away before
next execution of PS/SP for which tables are added to prelocking list.
This will be fixed by introducing of proper invalidation mechanism
once new TDC is ready.
*/
arena= thd->activate_stmt_arena_if_needed(&backup);
for (i=0 ; i < m_sptabs.records ; i++)
{
char *tab_buff, *key_buff;
SP_TABLE *stab= (SP_TABLE*) my_hash_element(&m_sptabs, i);
LEX_CSTRING db_name;
if (stab->temp)
continue;
if (!(tab_buff= thd->alloc(ALIGN_SIZE(sizeof(TABLE_LIST)) *
stab->lock_count)) ||
!(key_buff= (char*)thd->memdup(stab->qname.str,
stab->qname.length)))
DBUG_RETURN(FALSE);
bool sp_head::add_open_cursor(THD *thd, sp_pcontext *spcont, uint offset,
sp_pcontext *param_spcont,
List<sp_assignment_lex> *parameters,
List<sp_assignment_lex> *using_clause)
{
/*
The caller must make sure that the number of formal parameters matches
the number of actual parameters.
*/
DBUG_ASSERT((param_spcont ? param_spcont->context_var_count() : 0) ==
(parameters ? parameters->elements : 0));
// Add instructions to set parameters: OPEN c(1);
if (parameters &&
add_set_cursor_param_variables(thd, param_spcont, parameters))
return true;
// Add instructions to set placeholders: OPEN c USING 1;
const sp_rcontext_ref cursor_ref(sp_rcontext_addr(&sp_rcontext_handler_local,
offset),
nullptr);
if (using_clause &&
add_set_cursor_placeholders(thd, spcont, cursor_ref, using_clause))
return true;
sp_instr_copen *i= new (thd->mem_root)
sp_instr_copen(instructions(), spcont, offset);
return i == NULL || add_instr(i);
}
bool sp_head::add_open_cursor_for_stmt(THD *thd, sp_pcontext *spcont,
const Lex_ident_column &cursor_name,
const sp_rcontext_ref &cursor_ref,
sp_lex_cursor *stmt,
List<sp_assignment_lex> *using_clause)
{
// Add instructions to set placeholders: OPEN c USING 1;
if (using_clause &&
add_set_cursor_placeholders(thd, spcont, cursor_ref, using_clause))
return true;
auto *i= new (thd->mem_root) sp_instr_copen_by_ref(instructions(),
spcont, cursor_name,
cursor_ref, stmt,
set_ps_placeholder_count);
return !i || add_instr(i);
}
/*
Generate an instruction to set one placeholder
from an actual parameter from the USING clause:
OPEN c USING 1,2,3;
*/
bool sp_head::add_set_cursor_placeholder(THD *thd,
sp_pcontext *spcont,
const sp_rcontext_ref &cur,
uint using_offset,
sp_assignment_lex *value)
{
/*
Create the instruction in the way so it owns value->get_item()
and its free list, and is responsible for freeing it and clean it up.
See also add_set_cursor_typed_param_variable().
*/
DBUG_ASSERT(m_thd->free_list == NULL);
m_thd->free_list= value->get_free_list();
sp_instr *i= new (thd->mem_root) sp_instr_set_ps_placeholder(instructions(),
spcont, cur,
using_offset,
value);
bool
sp_head::add_set_for_loop_cursor_param_variables(THD *thd,
sp_pcontext *param_spcont,
sp_assignment_lex *param_lex,
Item_args *parameters)
{
DBUG_ASSERT(param_spcont->context_var_count() == parameters->argument_count());
for (uint idx= 0; idx < parameters->argument_count(); idx ++)
{
/*
param_lex is shared between multiple items (cursor parameters).
Only the last sp_instr_set is responsible for freeing param_lex.
See more comments in LEX::sp_for_loop_cursor_declarations in sql_lex.cc.
*/
bool last= idx + 1 == parameters->argument_count();
sp_variable *spvar= param_spcont->get_context_variable(idx);
if (set_local_variable(thd, param_spcont,
&sp_rcontext_handler_local,
spvar, parameters->arguments()[idx],
param_lex, last,
param_lex->get_expr_str()))
return true;
}
return false;
}
/*
When the parser reaches the end of a BEGIN..END inner block
let's add sp_instr_destruct_variable instructions for the block variables
with complex data types (with side effects) such as SYS_REFCURSOR.
*/
bool sp_head::add_sp_block_destruct_variables(THD *thd, sp_pcontext *pctx)
{
uint var_count= pctx->context_var_count();
for (uint i= 0; i < var_count; i++)
{
uint offset= var_count - i - 1;
sp_variable *spv= pctx->get_context_variable(offset);
if (spv->type_handler()->
Spvar_definition_with_complex_data_types(&spv->field_def))
{
sp_instr *instr= new (thd->mem_root) sp_instr_destruct_variable(
instructions(),
pctx, spv->offset);
if (!instr || add_instr(instr))
return true;
}
}
return false;
}
/*
In Oracle mode stored routines have an optional name
at the end of a declaration:
PROCEDURE p1 AS
BEGIN
NULL
END p1;
Check that the first p1 and the last p1 match.
*/
bool sp_head::check_package_routine_end_name(const LEX_CSTRING &end_name) const
{
Lex_ident_routine non_qualified_name(m_name);
const char *errpos;
size_t ofs;
if (!end_name.length)
return false; // No end name
if (!(errpos= strrchr(m_name.str, '.')))
{
errpos= m_name.str;
goto err;
}
errpos++;
ofs= errpos - m_name.str;
non_qualified_name.str+= ofs;
non_qualified_name.length-= ofs;
if (non_qualified_name.streq(end_name))
return false;
err:
my_error(ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0), end_name.str, errpos);
return true;
}
/*
Package data types are not allowed in triggers and events yet
because of the dump/restore problems (MDEV-40436, MDEV-40686).
Disallow qualified types.
@param db - The database. Can be {nullptr,0}
@param package - The package. Can be {nullptr,0}
@param type - The type name. Cannot be {nullptr,0}
*/
bool sp_head::check_maybe_qualified_type_context(const Lex_ident_sys_st &db,
const Lex_ident_sys_st &package,
const Lex_ident_sys_st &type) const
{
DBUG_ASSERT(type.str);
if ((m_handler == &sp_handler_trigger ||
m_handler == &sp_handler_event) && package.str)
{
my_error(ER_WRONG_USAGE, MYF(0), m_handler->type_lex_cstring().str,
db.str ? QTypeErrBuffer(db, package, type).ptr() :
QTypeErrBuffer(package, type).ptr());
return true;
}
return false;
}
/*
Check if the column definition "def" with type"type" can be used in "this".
These kind of declarations are not allowed in schema routines:
PROCEDURE p1(a pkg1.type1) - a schema routine parameter type
FUNCTION f1() RETURN pkg1.type1; - a schema function RETURN types
because they show up in INFORMATION_SCHEMA.PARAMETERS.
The code is not ready to handle this correctly.
Note, using them for package routines are OK, because package routines
are not displayed in INFORMATION_SCHEMA.
*/
bool sp_head::check_maybe_foreign_type_context(THD *thd,
const Lex_field_type_st &def,
column_definition_type_t type)
{
if (def.foreign_module_type())
{
if ((m_handler == &sp_handler_procedure ||
m_handler == &sp_handler_function)/*i.e. not a package routine*/ &&
(type == COLUMN_DEFINITION_ROUTINE_PARAM || type == COLUMN_DEFINITION_FUNCTION_RETURN))
{
my_error(ER_WRONG_USAGE, MYF(0), type == COLUMN_DEFINITION_ROUTINE_PARAM ? "parameter_declaration" : "RETURN", "package_name.type_name");
return true;
}
}
return false;
}
/*
Get the definition of the TYPE `dbn`.`package`.`type`.
If the definition is not found, then an error message is raises.
@param thd - The thd
@param OUT tdef - The pointer to the found TYPE definition
@param spec - The package specification
@param dbn - The nornamized database name (used for error messages)
@param package - The package name (used for error messages)
@param type - The data type name
@retval false - If the TYPE definition was found and written to "tdef"
@retval true - If the data type was found found or some error happened.
*/
/*
Find a package-wide TYPE definition.
No errors are raised on failure.
*/
sp_type_def *sp_package::find_type_def(const Lex_ident_sys_st &type)
{
if (!get_parse_context() || !get_parse_context()->child_context(0))
return nullptr;
return get_parse_context()->child_context(0)->find_type_def(type, false);
}
/*
Get a 2-step qualified TYPE defined in a PACKAGE
consisting by the package name and type name.
Handles the following cases:
- A PACKAGE refers to its own type using a qualified name
- A routine refers to the PACKAGE in the database of the routine
using a qualified type
- Otherwise, the database which contains a package "package" with the type "type" is resolved using the @@PATH variable.
@param OUT tdef - The pointer to the TYPE definition
@param package - The package name
@param type - The data type name
@retval false - The package "package" with the data type"type" was found,
and tdef[0] was set to the pointer of the found type.
@retval true - The data type was not found, or some error happened
during type resolution.
*/
bool sp_head::get_typedef_package_spec_or_error(THD *thd,
const sp_type_def **tdef,
const Lex_ident_sys_st &package,
const Lex_ident_sys_st &type)
{
DBUG_ASSERT(!package.is_null());
DBUG_ASSERT(!type.is_null());
/*
Catch a special case first: a reference to the current package spec.
Note, cast to Lex_ident_routine is safe below because m_name gets
to sp_head after Lex_ident_routine::check_name_with_error().
*/
if (m_handler == &sp_handler_package_spec &&
Lex_ident_routine(m_name).streq(package))
{
/*
The data type pkg1.type1 refers the current package specifications
for the packge pkg1:
CREATE PACKAGE pkg1 AS TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); TYPE assoc0_t IS TABLE OF pkg1.rec0_t INDEX BY INTEGER;
END;
Avoid a recursive find_package_spec_type().
*/
sp_package *spec= get_package();
DBUG_ASSERT(spec);
if (!(tdef[0]= spec->find_type_def(type)))
{
raise_unknown_data_type(package, type);
return true;
}
return false;
}
/*
Another special case: a reference to the current package specification
from a package routine parameter or a package function RETURN:
CREATE PACKAGE p2 AS TYPE r IS RECORD(x INT);
PROCEDURE q(v p2.r); -- here
FUNCTION f RETURN p2.r; -- here
END;
*/
if ((m_handler == &sp_handler_package_function ||
m_handler == &sp_handler_package_procedure))
{
DBUG_ASSERT(m_parent);
if (m_parent->m_handler == &sp_handler_package_spec &&
Lex_ident_routine(m_parent->m_name).streq(package))
{
if (!(tdef[0]= m_parent->find_type_def(type)))
{
raise_unknown_data_type(package, type);
return true;
}
return false;
}
}
/*
Another special case - a two step reference to a type
in the package of the db of the current routine (test1 in this example):
CREATE OR REPLACE PROCEDURE test1.p1 AS
r0 pkg1.rec0_t; -- A reference to test1.pkg1.rec0_t
BEGIN ...
*/
if (m_db.str)
{
Lex_ident_db_normalized ndb;
if (!(ndb= thd->to_ident_db_normalized_with_error(m_db)).str)
return true;
sp_package *spec;
if ((spec= Sp_handler::find_package_spec(thd, ndb, package)))
return get_typedef_package_spec_or_error(thd, tdef, spec,
ndb, package, type);
if (thd->is_fatal_error)
return true; // E.g. ER_STACK_OVERRUN_NEED_MORE raised
}
// Now go with the general case
Lex_ident_db_normalized resolved_db;
if (thd->variables.path.find_package_spec_type(thd, &resolved_db,
package, type))
return true; // An internal error
if (!resolved_db.str)
{
raise_unknown_data_type(package, type);
return true; // 2-step package spec was not found
}
return get_typedef_package_spec_or_error(thd, tdef,
Lex_ident_sys(resolved_db.str,
resolved_db.length),
package, type);
}
/*
Get a 3-step qualified TYPE defines in a PACKAGE
@param OUT tdef - The pointer to the TYPE definition
@param db - The database name
@param package - The package name
@param type - The data type name
@retval false - The database "db" with the package "package"
with the data type"type" was found,
and tdef was set to the pointer of the found type.
@retval true - The data type was not found, or some error happened
during type resolution.
*/
bool sp_head::get_typedef_package_spec_or_error(THD *thd,
const sp_type_def **tdef,
const Lex_ident_sys_st &db,
const Lex_ident_sys_st &package,
const Lex_ident_sys_st &type)
{
DBUG_ASSERT(!db.is_null());
DBUG_ASSERT(!package.is_null());
DBUG_ASSERT(!type.is_null());
Lex_ident_db_normalized dbn= thd->to_ident_db_normalized_with_error(db);
if (!dbn.str)
return true;
/*
Catch a special case first: a reference to the current package spec.
Note, cast to Lex_ident_routine is safe below because m_name gets
to sp_head after Lex_ident_routine::check_name_with_error().
*/
if (m_handler == &sp_handler_package_spec &&
dbn.streq(m_db) &&
Lex_ident_routine(m_name).streq(package))
{
/*
The data type db1.pkg1.type1 refers the current package specifications
for the packge pkg1:
USE db1;
CREATE PACKAGE pkg1 AS TYPE rec0_t IS RECORD (a INT, b VARCHAR(32)); TYPE assoc0_t IS TABLE OF db1.pkg1.rec0_t INDEX BY INTEGER;
END;
Avoid a recursive find_package_spec_type().
*/
sp_package *spec= get_package();
DBUG_ASSERT(spec);
if (!(tdef[0]= spec->find_type_def(type)))
{
raise_unknown_data_type(dbn, package, type);
return true;
}
return false;
}
/*
Another special case: a reference to the current package specification
from a package routine parameter or a package function RETURN:
CREATE PACKAGE p2 AS TYPE r IS RECORD(x INT);
PROCEDURE q(v test.p2.r); -- here
FUNCTION f RETURN test.p2.r; -- here
END;
*/
if ((m_handler == &sp_handler_package_function ||
m_handler == &sp_handler_package_procedure))
{
DBUG_ASSERT(m_parent);
if (m_parent->m_handler == &sp_handler_package_spec &&
dbn.streq(m_parent->m_db) &&
Lex_ident_routine(m_parent->m_name).streq(package))
{
if (!(tdef[0]= m_parent->find_type_def(type)))
{
raise_unknown_data_type(package, type);
return true;
}
return false;
}
}
sp_package *spec= Sp_handler::find_package_spec(thd, dbn, package);
if (!spec)
{
raise_unknown_data_type(dbn, package, type);
return true; // 3-step package spec was not found
}
return get_typedef_package_spec_or_error(thd, tdef, spec, dbn, package, type);
}
Messung V0.5 in Prozent
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.154Bemerkung:
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.