The world's most popular open source database
#include "mysql_priv.h"#include "sp_head.h"#include "sp.h"#include "sp_pcontext.h"#include "sp_rcontext.h"#include "sp_cache.h"#include <my_user.h>Include dependency graph for sp_head.cc:

Go to the source code of this file.
| #define SP_INSTR_UINT_MAXLEN 8 |
Definition at line 30 of file sp_head.cc.
Referenced by sp_instr_set_case_expr::print(), sp_instr_error::print(), sp_instr_cfetch::print(), sp_instr_cclose::print(), sp_instr_copen::print(), sp_instr_cpop::print(), sp_instr_cpush::print(), sp_instr_hreturn::print(), sp_instr_hpop::print(), sp_instr_hpush_jump::print(), sp_instr_jump_if_not::print(), sp_instr_jump::print(), sp_instr_set::print(), sp_instr_stmt::print(), and sp_head::show_routine_code().
| #define SP_STMT_PRINT_MAXLEN 40 |
| typedef struct st_sp_table SP_TABLE |
| bool check_routine_name | ( | LEX_STRING | ident | ) |
Definition at line 2065 of file sp_head.cc.
References bzero, check_some_routine_access(), check_table_access(), sp_head::m_db, sp_head::m_definer_host, sp_head::m_definer_user, sp_head::m_name, sp_head::m_type, SELECT_ACL, LEX_STRING::str, strcmp(), and TYPE_ENUM_PROCEDURE.
Referenced by sp_head::show_create_function(), sp_head::show_create_procedure(), and sp_head::show_routine_code().
02066 { 02067 TABLE_LIST tables; 02068 bzero((char*) &tables,sizeof(tables)); 02069 tables.db= (char*) "mysql"; 02070 tables.table_name= tables.alias= (char*) "proc"; 02071 *full_access= (!check_table_access(thd, SELECT_ACL, &tables, 1) || 02072 (!strcmp(sp->m_definer_user.str, 02073 thd->security_ctx->priv_user) && 02074 !strcmp(sp->m_definer_host.str, 02075 thd->security_ctx->priv_host))); 02076 if (!*full_access) 02077 return check_some_routine_access(thd, sp->m_db.str, sp->m_name.str, 02078 sp->m_type == TYPE_ENUM_PROCEDURE); 02079 return 0; 02080 }
Here is the call graph for this function:

Here is the caller graph for this function:

| int cmp_splocal_locations | ( | Item_splocal *const * | a, | |
| Item_splocal *const * | b | |||
| ) |
Definition at line 734 of file sp_head.cc.
Referenced by subst_spvars().
Here is the caller graph for this function:

| static TYPELIB* create_typelib | ( | MEM_ROOT * | mem_root, | |
| create_field * | field_def, | |||
| List< String > * | src | |||
| ) | [static] |
Definition at line 564 of file sp_head.cc.
References alloc_root(), String::charset(), create_field::charset, String::copy(), st_typelib::count, charset_info_st::cset, DBUG_ENTER, DBUG_RETURN, base_list::elements, String::length(), my_charset_handler_st::lengthsp, st_typelib::name, String::needs_conversion(), NULL, String::ptr(), strmake_root(), st_typelib::type_lengths, and st_typelib::type_names.
Referenced by sp_head::fill_field_definition().
00565 { 00566 TYPELIB *result= NULL; 00567 CHARSET_INFO *cs= field_def->charset; 00568 DBUG_ENTER("create_typelib"); 00569 00570 if (src->elements) 00571 { 00572 result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB)); 00573 result->count= src->elements; 00574 result->name= ""; 00575 if (!(result->type_names=(const char **) 00576 alloc_root(mem_root,(sizeof(char *)+sizeof(int))*(result->count+1)))) 00577 DBUG_RETURN(0); 00578 result->type_lengths= (uint*)(result->type_names + result->count+1); 00579 List_iterator<String> it(*src); 00580 String conv; 00581 for (uint i=0; i < result->count; i++) 00582 { 00583 uint32 dummy; 00584 uint length; 00585 String *tmp= it++; 00586 00587 if (String::needs_conversion(tmp->length(), tmp->charset(), 00588 cs, &dummy)) 00589 { 00590 uint cnv_errs; 00591 conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs); 00592 00593 length= conv.length(); 00594 result->type_names[i]= (char*) strmake_root(mem_root, conv.ptr(), 00595 length); 00596 } 00597 else 00598 { 00599 length= tmp->length(); 00600 result->type_names[i]= strmake_root(mem_root, tmp->ptr(), length); 00601 } 00602 00603 // Strip trailing spaces. 00604 length= cs->cset->lengthsp(cs, result->type_names[i], length); 00605 result->type_lengths[i]= length; 00606 ((uchar *)result->type_names[i])[length]= '\0'; 00607 } 00608 result->type_names[result->count]= 0; 00609 result->type_lengths[result->count]= 0; 00610 } 00611 DBUG_RETURN(result); 00612 }
Here is the call graph for this function:

Here is the caller graph for this function:

| bool set_routine_security_ctx | ( | THD * | thd, | |
| sp_head * | sp, | |||
| bool | is_proc, | |||
| Security_context ** | save_ctx | |||
| ) |
Definition at line 1227 of file sp_head.cc.
References check_routine_access(), EXECUTE_ACL, FALSE, sp_head::m_db, sp_head::m_name, sp_change_security_context(), sp_restore_security_context(), LEX_STRING::str, and TRUE.
Referenced by sp_head::execute_function(), sp_head::execute_procedure(), and Item_func_sp::fix_fields().
01229 { 01230 *save_ctx= 0; 01231 if (sp_change_security_context(thd, sp, save_ctx)) 01232 return TRUE; 01233 01234 /* 01235 If we changed context to run as another user, we need to check the 01236 access right for the new context again as someone may have revoked 01237 the right to use the procedure from this user. 01238 01239 TODO: 01240 Cache if the definer has the right to use the object on the 01241 first usage and only reset the cache if someone does a GRANT 01242 statement that 'may' affect this. 01243 */ 01244 if (*save_ctx && 01245 check_routine_access(thd, EXECUTE_ACL, 01246 sp->m_db.str, sp->m_name.str, is_proc, FALSE)) 01247 { 01248 sp_restore_security_context(thd, *save_ctx); 01249 *save_ctx= 0; 01250 return TRUE; 01251 } 01252 01253 return FALSE; 01254 }
Here is the call graph for this function:

Here is the caller graph for this function:

| TABLE_LIST* sp_add_to_query_tables | ( | THD * | thd, | |
| LEX * | lex, | |||
| const char * | db, | |||
| const char * | name, | |||
| thr_lock_type | locktype | |||
| ) |
Definition at line 3606 of file sp_head.cc.
References st_table_list::alias, st_table_list::cacheable_table, st_table_list::db, st_table_list::db_length, ER_OUTOFMEMORY, st_table_list::lock_type, my_error(), MYF, NULL, st_table_list::select_lex, strlen(), st_table_list::table_name, and st_table_list::table_name_length.
Referenced by add_table_for_trigger().
03609 { 03610 TABLE_LIST *table; 03611 03612 if (!(table= (TABLE_LIST *)thd->calloc(sizeof(TABLE_LIST)))) 03613 { 03614 my_error(ER_OUTOFMEMORY, MYF(0), sizeof(TABLE_LIST)); 03615 return NULL; 03616 } 03617 table->db_length= strlen(db); 03618 table->db= thd->strmake(db, table->db_length); 03619 table->table_name_length= strlen(name); 03620 table->table_name= thd->strmake(name, table->table_name_length); 03621 table->alias= thd->strdup(name); 03622 table->lock_type= locktype; 03623 table->select_lex= lex->current_select; 03624 table->cacheable_table= 1; 03625 03626 lex->add_to_query_tables(table); 03627 return table; 03628 }
Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 3352 of file sp_head.cc.
References acl_getroot_no_password(), ER_NO_SUCH_USER, FALSE, sp_head::m_chistics, sp_head::m_db, sp_head::m_definer_host, sp_head::m_definer_user, sp_head::m_security_ctx, my_error(), my_strcasecmp, MYF, LEX_STRING::str, strcmp(), system_charset_info, and TRUE.
Referenced by Table_triggers_list::process_triggers(), and set_routine_security_ctx().
03353 { 03354 *backup= 0; 03355 if (sp->m_chistics->suid != SP_IS_NOT_SUID && 03356 (strcmp(sp->m_definer_user.str, 03357 thd->security_ctx->priv_user) || 03358 my_strcasecmp(system_charset_info, sp->m_definer_host.str, 03359 thd->security_ctx->priv_host))) 03360 { 03361 if (acl_getroot_no_password(&sp->m_security_ctx, sp->m_definer_user.str, 03362 sp->m_definer_host.str, 03363 sp->m_definer_host.str, 03364 sp->m_db.str)) 03365 { 03366 my_error(ER_NO_SUCH_USER, MYF(0), sp->m_definer_user.str, 03367 sp->m_definer_host.str); 03368 return TRUE; 03369 } 03370 *backup= thd->security_ctx; 03371 thd->security_ctx= &sp->m_security_ctx; 03372 } 03373 return FALSE; 03374 }
Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 325 of file sp_head.cc.
References CHECK_FIELD_ERROR_FOR_NULL, DBUG_ENTER, DBUG_RETURN, FALSE, MODE_STRICT_ALL_TABLES, MODE_STRICT_TRANS_TABLES, Item::save_in_field(), sp_prepare_func_item(), and TRUE.
Referenced by sp_rcontext::set_return_value(), and sp_rcontext::set_variable().
00326 { 00327 Item *expr_item; 00328 00329 DBUG_ENTER("sp_eval_expr"); 00330 00331 if (!*expr_item_ptr) 00332 DBUG_RETURN(TRUE); 00333 00334 if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr))) 00335 DBUG_RETURN(TRUE); 00336 00337 bool err_status= FALSE; 00338 00339 /* 00340 Set THD flags to emit warnings/errors in case of overflow/type errors 00341 during saving the item into the field. 00342 00343 Save original values and restore them after save. 00344 */ 00345 00346 enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; 00347 bool save_abort_on_warning= thd->abort_on_warning; 00348 bool save_no_trans_update= thd->no_trans_update; 00349 00350 thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL; 00351 thd->abort_on_warning= 00352 thd->variables.sql_mode & 00353 (MODE_STRICT_TRANS_TABLES | MODE_STRICT_ALL_TABLES); 00354 thd->no_trans_update= 0; 00355 00356 /* Save the value in the field. Convert the value if needed. */ 00357 00358 expr_item->save_in_field(result_field, 0); 00359 00360 thd->count_cuted_fields= save_count_cuted_fields; 00361 thd->abort_on_warning= save_abort_on_warning; 00362 thd->no_trans_update= save_no_trans_update; 00363 00364 if (thd->net.report_error) 00365 { 00366 /* Return error status if something went wrong. */ 00367 err_status= TRUE; 00368 } 00369 00370 DBUG_RETURN(err_status); 00371 }
Here is the call graph for this function:

Here is the caller graph for this function:

| uint sp_get_flags_for_command | ( | LEX * | lex | ) |
Definition at line 151 of file sp_head.cc.
References sp_head::CONTAINS_DYNAMIC_SQL, flags, HA_LEX_CREATE_TMP_TABLE, sp_head::HAS_COMMIT_OR_ROLLBACK, sp_head::MULTI_RESULTS, SQLCOM_ALTER_EVENT, SQLCOM_ALTER_FUNCTION, SQLCOM_ALTER_PROCEDURE, SQLCOM_ALTER_TABLE, SQLCOM_ANALYZE, SQLCOM_ASSIGN_TO_KEYCACHE, SQLCOM_BACKUP_TABLE, SQLCOM_BEGIN, SQLCOM_CHECK, SQLCOM_CHECKSUM, SQLCOM_COMMIT, SQLCOM_CREATE_DB, SQLCOM_CREATE_EVENT, SQLCOM_CREATE_INDEX, SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CREATE_TABLE, SQLCOM_CREATE_TRIGGER, SQLCOM_CREATE_USER, SQLCOM_CREATE_VIEW, SQLCOM_DEALLOCATE_PREPARE, SQLCOM_DROP_DB, SQLCOM_DROP_EVENT, SQLCOM_DROP_FUNCTION, SQLCOM_DROP_INDEX, SQLCOM_DROP_PROCEDURE, SQLCOM_DROP_TABLE, SQLCOM_DROP_TRIGGER, SQLCOM_DROP_USER, SQLCOM_DROP_VIEW, SQLCOM_EXECUTE, SQLCOM_FLUSH, SQLCOM_HA_READ, SQLCOM_INSTALL_PLUGIN, SQLCOM_LOAD, SQLCOM_LOAD_MASTER_DATA, SQLCOM_LOCK_TABLES, SQLCOM_OPTIMIZE, SQLCOM_PRELOAD_KEYS, SQLCOM_PREPARE, SQLCOM_RENAME_TABLE, SQLCOM_RENAME_USER, SQLCOM_REPAIR, SQLCOM_RESTORE_TABLE, SQLCOM_ROLLBACK, SQLCOM_SELECT, SQLCOM_SHOW_AUTHORS, SQLCOM_SHOW_BINLOG_EVENTS, SQLCOM_SHOW_BINLOGS, SQLCOM_SHOW_CHARSETS, SQLCOM_SHOW_COLLATIONS, SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_CONTRIBUTORS, SQLCOM_SHOW_CREATE, SQLCOM_SHOW_CREATE_DB, SQLCOM_SHOW_CREATE_EVENT, SQLCOM_SHOW_CREATE_FUNC, SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_DATABASES, SQLCOM_SHOW_ENGINE_LOGS, SQLCOM_SHOW_ENGINE_MUTEX, SQLCOM_SHOW_ENGINE_STATUS, SQLCOM_SHOW_ERRORS, SQLCOM_SHOW_EVENTS, SQLCOM_SHOW_FIELDS, SQLCOM_SHOW_FUNC_CODE, SQLCOM_SHOW_GRANTS, SQLCOM_SHOW_KEYS, SQLCOM_SHOW_MASTER_STAT, SQLCOM_SHOW_NEW_MASTER, SQLCOM_SHOW_OPEN_TABLES, SQLCOM_SHOW_PRIVILEGES, SQLCOM_SHOW_PROC_CODE, SQLCOM_SHOW_PROCESSLIST, SQLCOM_SHOW_SCHEDULER_STATUS, SQLCOM_SHOW_SLAVE_HOSTS, SQLCOM_SHOW_SLAVE_STAT, SQLCOM_SHOW_STATUS, SQLCOM_SHOW_STATUS_FUNC, SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_TABLES, SQLCOM_SHOW_VARIABLES, SQLCOM_SHOW_WARNS, SQLCOM_TRUNCATE, and SQLCOM_UNINSTALL_PLUGIN.
00152 { 00153 uint flags; 00154 00155 switch (lex->sql_command) { 00156 case SQLCOM_SELECT: 00157 if (lex->result) 00158 { 00159 flags= 0; /* This is a SELECT with INTO clause */ 00160 break; 00161 } 00162 /* fallthrough */ 00163 case SQLCOM_ANALYZE: 00164 case SQLCOM_BACKUP_TABLE: 00165 case SQLCOM_OPTIMIZE: 00166 case SQLCOM_PRELOAD_KEYS: 00167 case SQLCOM_ASSIGN_TO_KEYCACHE: 00168 case SQLCOM_CHECKSUM: 00169 case SQLCOM_CHECK: 00170 case SQLCOM_HA_READ: 00171 case SQLCOM_SHOW_AUTHORS: 00172 case SQLCOM_SHOW_BINLOGS: 00173 case SQLCOM_SHOW_BINLOG_EVENTS: 00174 case SQLCOM_SHOW_CHARSETS: 00175 case SQLCOM_SHOW_COLLATIONS: 00176 case SQLCOM_SHOW_COLUMN_TYPES: 00177 case SQLCOM_SHOW_CONTRIBUTORS: 00178 case SQLCOM_SHOW_CREATE: 00179 case SQLCOM_SHOW_CREATE_DB: 00180 case SQLCOM_SHOW_CREATE_FUNC: 00181 case SQLCOM_SHOW_CREATE_PROC: 00182 case SQLCOM_SHOW_CREATE_EVENT: 00183 case SQLCOM_SHOW_DATABASES: 00184 case SQLCOM_SHOW_ERRORS: 00185 case SQLCOM_SHOW_FIELDS: 00186 case SQLCOM_SHOW_FUNC_CODE: 00187 case SQLCOM_SHOW_GRANTS: 00188 case SQLCOM_SHOW_ENGINE_STATUS: 00189 case SQLCOM_SHOW_ENGINE_LOGS: 00190 case SQLCOM_SHOW_ENGINE_MUTEX: 00191 case SQLCOM_SHOW_EVENTS: 00192 case SQLCOM_SHOW_KEYS: 00193 case SQLCOM_SHOW_MASTER_STAT: 00194 case SQLCOM_SHOW_NEW_MASTER: 00195 case SQLCOM_SHOW_OPEN_TABLES: 00196 case SQLCOM_SHOW_PRIVILEGES: 00197 case SQLCOM_SHOW_PROCESSLIST: 00198 case SQLCOM_SHOW_PROC_CODE: 00199 case SQLCOM_SHOW_SCHEDULER_STATUS: 00200 case SQLCOM_SHOW_SLAVE_HOSTS: 00201 case SQLCOM_SHOW_SLAVE_STAT: 00202 case SQLCOM_SHOW_STATUS: 00203 case SQLCOM_SHOW_STATUS_FUNC: 00204 case SQLCOM_SHOW_STATUS_PROC: 00205 case SQLCOM_SHOW_STORAGE_ENGINES: 00206 case SQLCOM_SHOW_TABLES: 00207 case SQLCOM_SHOW_VARIABLES: 00208 case SQLCOM_SHOW_WARNS: 00209 case SQLCOM_REPAIR: 00210 case SQLCOM_RESTORE_TABLE: 00211 flags= sp_head::MULTI_RESULTS; 00212 break; 00213 /* 00214 EXECUTE statement may return a result set, but doesn't have to. 00215 We can't, however, know it in advance, and therefore must add 00216 this statement here. This is ok, as is equivalent to a result-set 00217 statement within an IF condition. 00218 */ 00219 case SQLCOM_EXECUTE: 00220 flags= sp_head::MULTI_RESULTS | sp_head::CONTAINS_DYNAMIC_SQL; 00221 break; 00222 case SQLCOM_PREPARE: 00223 case SQLCOM_DEALLOCATE_PREPARE: 00224 flags= sp_head::CONTAINS_DYNAMIC_SQL; 00225 break; 00226 case SQLCOM_CREATE_TABLE: 00227 if (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE) 00228 flags= 0; 00229 else 00230 flags= sp_head::HAS_COMMIT_OR_ROLLBACK; 00231 break; 00232 case SQLCOM_DROP_TABLE: 00233 if (lex->drop_temporary) 00234 flags= 0; 00235 else 00236 flags= sp_head::HAS_COMMIT_OR_ROLLBACK; 00237 break; 00238 case SQLCOM_CREATE_INDEX: 00239 case SQLCOM_CREATE_DB: 00240 case SQLCOM_CREATE_VIEW: 00241 case SQLCOM_CREATE_TRIGGER: 00242 case SQLCOM_CREATE_USER: 00243 case SQLCOM_ALTER_TABLE: 00244 case SQLCOM_BEGIN: 00245 case SQLCOM_RENAME_TABLE: 00246 case SQLCOM_RENAME_USER: 00247 case SQLCOM_DROP_INDEX: 00248 case SQLCOM_DROP_DB: 00249 case SQLCOM_DROP_USER: 00250 case SQLCOM_DROP_VIEW: 00251 case SQLCOM_DROP_TRIGGER: 00252 case SQLCOM_TRUNCATE: 00253 case SQLCOM_COMMIT: 00254 case SQLCOM_ROLLBACK: 00255 case SQLCOM_LOAD: 00256 case SQLCOM_LOAD_MASTER_DATA: 00257 case SQLCOM_LOCK_TABLES: 00258 case SQLCOM_CREATE_PROCEDURE: 00259 case SQLCOM_CREATE_SPFUNCTION: 00260 case SQLCOM_ALTER_PROCEDURE: 00261 case SQLCOM_ALTER_FUNCTION: 00262 case SQLCOM_DROP_PROCEDURE: 00263 case SQLCOM_DROP_FUNCTION: 00264 case SQLCOM_CREATE_EVENT: 00265 case SQLCOM_ALTER_EVENT: 00266 case SQLCOM_DROP_EVENT: 00267 case SQLCOM_FLUSH: 00268 case SQLCOM_INSTALL_PLUGIN: 00269 case SQLCOM_UNINSTALL_PLUGIN: 00270 flags= sp_head::HAS_COMMIT_OR_ROLLBACK; 00271 break; 00272 default: 00273 flags= 0; 00274 break; 00275 } 00276 return flags; 00277 }
Definition at line 96 of file sp_head.cc.
References append_query_string(), buf, String::charset(), charset_info_st::csname, DECIMAL_RESULT, charset_info_st::escape_with_backslash_is_dangerous, INT_RESULT, NULL, REAL_RESULT, Item::result_type(), ROW_RESULT, STRING_BUFFER_USUAL_SIZE, STRING_RESULT, and Item::val_str().
Referenced by sp_head::execute_function(), and subst_spvars().
00097 { 00098 Item_result result_type= item->result_type(); 00099 00100 switch (item->result_type()) { 00101 case REAL_RESULT: 00102 case INT_RESULT: 00103 case DECIMAL_RESULT: 00104 return item->val_str(str); 00105 00106 case STRING_RESULT: 00107 { 00108 String *result= item->val_str(str); 00109 00110 if (!result) 00111 return NULL; 00112 00113 { 00114 char buf_holder[STRING_BUFFER_USUAL_SIZE]; 00115 String buf(buf_holder, sizeof(buf_holder), result->charset()); 00116 00117 /* We must reset length of the buffer, because of String specificity. */ 00118 buf.length(0); 00119 00120 buf.append('_'); 00121 buf.append(result->charset()->csname); 00122 if (result->charset()->escape_with_backslash_is_dangerous) 00123 buf.append(' '); 00124 append_query_string(result->charset(), result, &buf); 00125 str->copy(buf); 00126 00127 return str; 00128 } 00129 } 00130 00131 case ROW_RESULT: 00132 default: 00133 return NULL; 00134 } 00135 }
Here is the call graph for this function:

Here is the caller graph for this function:

| Item::Type sp_map_item_type | ( | enum enum_field_types | type | ) |
Definition at line 59 of file sp_head.cc.
References Item::DECIMAL_ITEM, Item::INT_ITEM, MYSQL_TYPE_DECIMAL, MYSQL_TYPE_DOUBLE, MYSQL_TYPE_FLOAT, MYSQL_TYPE_INT24, MYSQL_TYPE_LONG, MYSQL_TYPE_LONGLONG, MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_SHORT, MYSQL_TYPE_TINY, Item::REAL_ITEM, and Item::STRING_ITEM.
Referenced by Item_splocal::Item_splocal().
00060 { 00061 switch (type) { 00062 case MYSQL_TYPE_TINY: 00063 case MYSQL_TYPE_SHORT: 00064 case MYSQL_TYPE_LONG: 00065 case MYSQL_TYPE_LONGLONG: 00066 case MYSQL_TYPE_INT24: 00067 return Item::INT_ITEM; 00068 case MYSQL_TYPE_DECIMAL: 00069 case MYSQL_TYPE_NEWDECIMAL: 00070 return Item::DECIMAL_ITEM; 00071 case MYSQL_TYPE_FLOAT: 00072 case MYSQL_TYPE_DOUBLE: 00073 return Item::REAL_ITEM; 00074 default: 00075 return Item::STRING_ITEM; 00076 } 00077 }
Here is the caller graph for this function:

| Item_result sp_map_result_type | ( | enum enum_field_types | type | ) |

