diff options
Diffstat (limited to 'sql')
208 files changed, 48635 insertions, 13929 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index d929f4c047f..b3c99cc4788 100644 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -48,6 +48,8 @@ ${WSREP_INCLUDES} SET(GEN_SOURCES ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc +${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.h +${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc ${CMAKE_CURRENT_BINARY_DIR}/lex_hash.h ${CMAKE_CURRENT_BINARY_DIR}/lex_token.h ) @@ -138,6 +140,7 @@ SET (SQL_SOURCE sql_type.cc item_windowfunc.cc sql_window.cc sql_cte.cc + sql_sequence.cc sql_sequence.h ha_sequence.h ${WSREP_SOURCES} table_cache.cc encryption.cc temporary_tables.cc ${CMAKE_CURRENT_BINARY_DIR}/sql_builtin.cc @@ -159,6 +162,8 @@ ENDIF() MYSQL_ADD_PLUGIN(partition ha_partition.cc STORAGE_ENGINE DEFAULT STATIC_ONLY RECOMPILE_FOR_EMBEDDED) +MYSQL_ADD_PLUGIN(sql_sequence ha_sequence.cc STORAGE_ENGINE MANDATORY STATIC_ONLY +RECOMPILE_FOR_EMBEDDED) ADD_LIBRARY(sql STATIC ${SQL_SOURCE}) ADD_DEPENDENCIES(sql GenServerSource) @@ -349,6 +354,14 @@ RUN_BISON( ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc.yy ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.cc ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc.h + MYSQL +) + +RUN_BISON( + ${CMAKE_CURRENT_SOURCE_DIR}/sql_yacc_ora.yy + ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.cc + ${CMAKE_CURRENT_BINARY_DIR}/sql_yacc_ora.h + ORA ) # Gen_lex_hash @@ -387,6 +400,7 @@ CONFIGURE_FILE( ADD_CUSTOM_TARGET(dist COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/make_dist.cmake DEPENDS ${CMAKE_BINARY_DIR}/sql/sql_yacc.cc ${CMAKE_BINARY_DIR}/sql/sql_yacc.h + DEPENDS ${CMAKE_BINARY_DIR}/sql/sql_yacc_ora.cc ${CMAKE_BINARY_DIR}/sql/sql_yacc_ora.h WORKING_DIRECTORY ${CMAKE_BINARY_DIR} ) diff --git a/sql/create_options.cc b/sql/create_options.cc index 96893aa172e..22e58f8801b 100644 --- a/sql/create_options.cc +++ b/sql/create_options.cc @@ -119,7 +119,7 @@ static bool report_unknown_option(THD *thd, engine_option_value *val, #define value_ptr(STRUCT,OPT) ((char*)(STRUCT) + (OPT)->offset) static bool set_one_value(ha_create_table_option *opt, - THD *thd, const LEX_STRING *value, void *base, + THD *thd, const LEX_CSTRING *value, void *base, bool suppress_warning, MEM_ROOT *root) { @@ -311,7 +311,7 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg, } if (!seen || (opt->var && !last->value.str)) { - LEX_STRING default_val= null_lex_str; + LEX_CSTRING default_val= null_clex_str; /* Okay, here's the logic for sysvar options: @@ -320,8 +320,8 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg, *current* value of the underlying sysvar. 2. But only if the underlying sysvar value is different from the sysvar's default. - 3. If it's ALTER TABLE and the sysvar option was not explicitly - mentioned - do nothing, do not add it to the list. + 3. If it's ALTER TABLE or CREATE_SEQUENCE and the sysvar option was + not explicitly mentioned - do nothing, do not add it to the list. 4. But if it was ALTER TABLE with sysvar option = DEFAULT, we add it to the list (under the same condition #2). 5. If we're here parsing the option list from the .frm file @@ -329,7 +329,6 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg, do not add it to the list (makes no sense anyway) and use the *default* value of the underlying sysvar. Because sysvar value can change, but it should not affect existing tables. - This is how it's implemented: the current sysvar value is added to the list if suppress_warning is FALSE (meaning a table is created, that is CREATE TABLE or ALTER TABLE) and it's actually a CREATE TABLE @@ -349,9 +348,9 @@ bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg, { char buf[256]; String sbuf(buf, sizeof(buf), system_charset_info), *str; - if ((str= sysvar->val_str(&sbuf, thd, OPT_SESSION, &null_lex_str))) + if ((str= sysvar->val_str(&sbuf, thd, OPT_SESSION, &null_clex_str))) { - LEX_STRING name= { const_cast<char*>(opt->name), opt->name_length }; + LEX_CSTRING name= { opt->name, opt->name_length }; default_val.str= strmake_root(root, str->ptr(), str->length()); default_val.length= str->length(); val= new (root) engine_option_value(name, default_val, @@ -689,7 +688,7 @@ uchar *engine_option_value::frm_read(const uchar *buff, const uchar *buff_end, engine_option_value **start, engine_option_value **end, MEM_ROOT *root) { - LEX_STRING name, value; + LEX_CSTRING name, value; uint len; #define need_buff(N) if (buff + (N) >= buff_end) return NULL diff --git a/sql/create_options.h b/sql/create_options.h index 191ec88750a..41e8abcb232 100644 --- a/sql/create_options.h +++ b/sql/create_options.h @@ -29,8 +29,8 @@ enum { ENGINE_OPTION_MAX_LENGTH=32767 }; class engine_option_value: public Sql_alloc { public: - LEX_STRING name; - LEX_STRING value; + LEX_CSTRING name; + LEX_CSTRING value; engine_option_value *next; ///< parser puts them in a FIFO linked list bool parsed; ///< to detect unrecognized options bool quoted_value; ///< option=VAL vs. option='VAL' @@ -42,28 +42,30 @@ class engine_option_value: public Sql_alloc { link(start, end); } - engine_option_value(LEX_STRING &name_arg, LEX_STRING &value_arg, bool quoted, + engine_option_value(LEX_CSTRING &name_arg, LEX_CSTRING &value_arg, + bool quoted, engine_option_value **start, engine_option_value **end) : name(name_arg), value(value_arg), next(NULL), parsed(false), quoted_value(quoted) { link(start, end); } - engine_option_value(LEX_STRING &name_arg, + engine_option_value(LEX_CSTRING &name_arg, engine_option_value **start, engine_option_value **end) : - name(name_arg), value(null_lex_str), + name(name_arg), value(null_clex_str), next(NULL), parsed(false), quoted_value(false) { link(start, end); } - engine_option_value(LEX_STRING &name_arg, ulonglong value_arg, + engine_option_value(LEX_CSTRING &name_arg, ulonglong value_arg, engine_option_value **start, engine_option_value **end, MEM_ROOT *root) : name(name_arg), next(NULL), parsed(false), quoted_value(false) { - if ((value.str= (char *)alloc_root(root, 22))) + char *str; + if ((value.str= str= (char *)alloc_root(root, 22))) { - value.length= longlong10_to_str(value_arg, value.str, 10) - value.str; + value.length= longlong10_to_str(value_arg, str, 10) - str; link(start, end); } } diff --git a/sql/datadict.cc b/sql/datadict.cc index 103a33214ae..edc9fe5681b 100644 --- a/sql/datadict.cc +++ b/sql/datadict.cc @@ -18,6 +18,7 @@ #include "sql_priv.h" #include "sql_class.h" #include "sql_table.h" +#include "ha_sequence.h" static int read_string(File file, uchar**to, size_t length) { @@ -43,36 +44,43 @@ static int read_string(File file, uchar**to, size_t length) @param[in] path path to FRM file. @param[in/out] engine_name table engine name (length < NAME_CHAR_LEN) - engine_name is a LEX_STRING, where engine_name->str must point to + engine_name is a LEX_CSTRING, where engine_name->str must point to a buffer of at least NAME_CHAR_LEN+1 bytes. - @retval FRMTYPE_ERROR error - @retval FRMTYPE_TABLE table - @retval FRMTYPE_VIEW view + @param[out] is_sequence 1 if table is a SEQUENCE, 0 otherwise + + @retval TABLE_TYPE_UNKNOWN error + @retval TABLE_TYPE_TABLE table + @retval TABLE_TYPE_SEQUENCE sequence table + @retval TABLE_TYPE_VIEW view */ -frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name) +Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name, + bool *is_sequence) { File file; - uchar header[10]; //"TYPE=VIEW\n" it is 10 characters + uchar header[40]; //"TYPE=VIEW\n" it is 10 characters size_t error; - frm_type_enum type= FRMTYPE_ERROR; + Table_type type= TABLE_TYPE_UNKNOWN; uchar dbt; DBUG_ENTER("dd_frm_type"); - if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) < 0) - DBUG_RETURN(FRMTYPE_ERROR); + *is_sequence= 0; + + if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) + < 0) + DBUG_RETURN(TABLE_TYPE_UNKNOWN); error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP)); if (error) goto err; - if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header))) + if (!strncmp((char*) header, "TYPE=VIEW\n", 10)) { - type= FRMTYPE_VIEW; + type= TABLE_TYPE_VIEW; goto err; } - type= FRMTYPE_TABLE; + type= TABLE_TYPE_NORMAL; if (!is_binary_frm_header(header) || !engine_name) goto err; @@ -80,6 +88,12 @@ frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name) engine_name->length= 0; dbt= header[3]; + if (((header[39] >> 4) & 3) == HA_CHOICE_YES) + { + DBUG_PRINT("info", ("Sequence found")); + *is_sequence= 1; + } + /* cannot use ha_resolve_by_legacy_type without a THD */ if (thd && dbt < DB_TYPE_FIRST_DYNAMIC) { @@ -121,8 +135,14 @@ frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name) { uint len= uint2korr(next_chunk); if (len <= NAME_CHAR_LEN) - strmake(engine_name->str, (char*)next_chunk + 2, + { + /* + The following cast is safe as the caller has allocated buffer + and it's up to this function to generate the name. + */ + strmake((char*) engine_name->str, (char*)next_chunk + 2, engine_name->length= len); + } } } diff --git a/sql/datadict.h b/sql/datadict.h index 9b180a882f9..e102618c2f7 100644 --- a/sql/datadict.h +++ b/sql/datadict.h @@ -1,6 +1,7 @@ #ifndef DATADICT_INCLUDED #define DATADICT_INCLUDED -/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2010, Oracle and/or its affiliates. + Copyright (c) 2017 MariaDB corporation. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,11 +22,12 @@ Data dictionary API. */ -enum frm_type_enum +enum Table_type { - FRMTYPE_ERROR= 0, - FRMTYPE_TABLE, - FRMTYPE_VIEW + TABLE_TYPE_UNKNOWN, + TABLE_TYPE_NORMAL, /* Normal table */ + TABLE_TYPE_SEQUENCE, + TABLE_TYPE_VIEW }; /* @@ -35,11 +37,14 @@ enum frm_type_enum Prefer to use ha_table_exists() instead. To check whether it's an frm of a view, use dd_frm_is_view(). */ -frm_type_enum dd_frm_type(THD *thd, char *path, LEX_STRING *engine_name); + +enum Table_type dd_frm_type(THD *thd, char *path, LEX_CSTRING *engine_name, + bool *is_sequence); static inline bool dd_frm_is_view(THD *thd, char *path) { - return dd_frm_type(thd, path, NULL) == FRMTYPE_VIEW; + bool not_used2; + return dd_frm_type(thd, path, NULL, ¬_used2) == TABLE_TYPE_VIEW; } bool dd_recreate_table(THD *thd, const char *db, const char *table_name, diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index e494b4a429b..4df981700ef 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -171,13 +171,13 @@ Event_creation_ctx::load_from_db(THD *thd, */ bool -Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n) +Event_queue_element_for_exec::init(LEX_CSTRING db, LEX_CSTRING n) { if (!(dbname.str= my_strndup(db.str, dbname.length= db.length, MYF(MY_WME)))) return TRUE; if (!(name.str= my_strndup(n.str, name.length= n.length, MYF(MY_WME)))) { - my_free(dbname.str); + my_free(const_cast<char*>(dbname.str)); return TRUE; } return FALSE; @@ -193,8 +193,8 @@ Event_queue_element_for_exec::init(LEX_STRING db, LEX_STRING n) Event_queue_element_for_exec::~Event_queue_element_for_exec() { - my_free(dbname.str); - my_free(name.str); + my_free(const_cast<char*>(dbname.str)); + my_free(const_cast<char*>(name.str)); } @@ -233,7 +233,7 @@ Event_basic::~Event_basic() /* - Short function to load a char column into a LEX_STRING + Short function to load a char column into a LEX_CSTRING SYNOPSIS Event_basic::load_string_field() @@ -249,7 +249,7 @@ Event_basic::load_string_fields(Field **fields, ...) bool ret= FALSE; va_list args; enum enum_events_table_field field_name; - LEX_STRING *field_value; + LEX_CSTRING *field_value; DBUG_ENTER("Event_basic::load_string_fields"); @@ -257,7 +257,7 @@ Event_basic::load_string_fields(Field **fields, ...) field_name= (enum enum_events_table_field) va_arg(args, int); while (field_name < ET_FIELD_COUNT) { - field_value= va_arg(args, LEX_STRING *); + field_value= va_arg(args, LEX_CSTRING *); if ((field_value->str= get_field(&mem_root, fields[field_name])) == NullS) { ret= TRUE; @@ -274,9 +274,9 @@ Event_basic::load_string_fields(Field **fields, ...) bool -Event_basic::load_time_zone(THD *thd, const LEX_STRING tz_name) +Event_basic::load_time_zone(THD *thd, const LEX_CSTRING *tz_name) { - String str(tz_name.str, &my_charset_latin1); + String str(tz_name->str, &my_charset_latin1); time_zone= my_tz_find(thd, &str); return (time_zone == NULL); @@ -391,9 +391,9 @@ Event_timed::init() bool Event_job_data::load_from_row(THD *thd, TABLE *table) { - char *ptr; + const char *ptr; size_t len; - LEX_STRING tz_name; + LEX_CSTRING tz_name; DBUG_ENTER("Event_job_data::load_from_row"); @@ -412,7 +412,7 @@ Event_job_data::load_from_row(THD *thd, TABLE *table) ET_FIELD_COUNT)) DBUG_RETURN(TRUE); - if (load_time_zone(thd, tz_name)) + if (load_time_zone(thd, &tz_name)) DBUG_RETURN(TRUE); Event_creation_ctx::load_from_db(thd, &mem_root, dbname.str, name.str, table, @@ -452,9 +452,9 @@ Event_job_data::load_from_row(THD *thd, TABLE *table) bool Event_queue_element::load_from_row(THD *thd, TABLE *table) { - char *ptr; + const char *ptr; MYSQL_TIME time; - LEX_STRING tz_name; + LEX_CSTRING tz_name; DBUG_ENTER("Event_queue_element::load_from_row"); @@ -472,7 +472,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) ET_FIELD_COUNT)) DBUG_RETURN(TRUE); - if (load_time_zone(thd, tz_name)) + if (load_time_zone(thd, &tz_name)) DBUG_RETURN(TRUE); starts_null= table->field[ET_FIELD_STARTS]->is_null(); @@ -519,7 +519,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) int i; char buff[MAX_FIELD_WIDTH]; String str(buff, sizeof(buff), &my_charset_bin); - LEX_STRING tmp; + LEX_CSTRING tmp; table->field[ET_FIELD_TRANSIENT_INTERVAL]->val_str(&str); if (!(tmp.length= str.length())) @@ -590,7 +590,7 @@ Event_queue_element::load_from_row(THD *thd, TABLE *table) bool Event_timed::load_from_row(THD *thd, TABLE *table) { - char *ptr; + const char *ptr; size_t len; DBUG_ENTER("Event_timed::load_from_row"); @@ -1203,7 +1203,7 @@ Event_timed::get_create_event(THD *thd, String *buf) buf->append(STRING_WITH_LEN(" ON SCHEDULE EVERY ")); buf->append(expr_buf); buf->append(' '); - LEX_STRING *ival= &interval_type_to_name[interval]; + LEX_CSTRING *ival= &interval_type_to_name[interval]; buf->append(ival->str, ival->length); if (!starts_null) @@ -1249,7 +1249,7 @@ Event_timed::get_create_event(THD *thd, String *buf) bool Event_job_data::construct_sp_sql(THD *thd, String *sp_sql) { - LEX_STRING buffer; + LEX_CSTRING buffer; const uint STATIC_SQL_LENGTH= 44; DBUG_ENTER("Event_job_data::construct_sp_sql"); @@ -1298,7 +1298,7 @@ Event_job_data::construct_sp_sql(THD *thd, String *sp_sql) bool Event_job_data::construct_drop_event_sql(THD *thd, String *sp_sql) { - LEX_STRING buffer; + LEX_CSTRING buffer; const uint STATIC_SQL_LENGTH= 14; DBUG_ENTER("Event_job_data::construct_drop_event_sql"); @@ -1426,7 +1426,13 @@ Event_job_data::execute(THD *thd, bool drop) sphead->m_flags|= sp_head::LOG_SLOW_STATEMENTS; sphead->m_flags|= sp_head::LOG_GENERAL_LOG; - sphead->set_info(0, 0, &thd->lex->sp_chistics, sql_mode); + /* + construct_sp_sql() + parse_sql() set suid to SP_IS_NOT_SUID, + because we have the security context already set to the event + definer here. See more comments in construct_sp_sql(). + */ + DBUG_ASSERT(sphead->suid() == SP_IS_NOT_SUID); + sphead->m_sql_mode= sql_mode; sphead->set_creation_ctx(creation_ctx); sphead->optimize(); @@ -1478,7 +1484,7 @@ end: WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL); } - ret= Events::drop_event(thd, dbname, name, FALSE); + ret= Events::drop_event(thd, &dbname, &name, FALSE); WSREP_TO_ISOLATION_END; @@ -1519,9 +1525,9 @@ end: */ bool -event_basic_db_equal(LEX_STRING db, Event_basic *et) +event_basic_db_equal(const LEX_CSTRING *db, Event_basic *et) { - return !sortcmp_lex_string(et->dbname, db, system_charset_info); + return !sortcmp_lex_string(&et->dbname, db, system_charset_info); } @@ -1540,10 +1546,11 @@ event_basic_db_equal(LEX_STRING db, Event_basic *et) */ bool -event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b) +event_basic_identifier_equal(const LEX_CSTRING *db, const LEX_CSTRING *name, + Event_basic *b) { - return !sortcmp_lex_string(name, b->name, system_charset_info) && - !sortcmp_lex_string(db, b->dbname, system_charset_info); + return !sortcmp_lex_string(name, &b->name, system_charset_info) && + !sortcmp_lex_string(db, &b->dbname, system_charset_info); } /** diff --git a/sql/event_data_objects.h b/sql/event_data_objects.h index 7ad8191f4d2..884bbd7d701 100644 --- a/sql/event_data_objects.h +++ b/sql/event_data_objects.h @@ -37,10 +37,10 @@ public: ~Event_queue_element_for_exec(); bool - init(LEX_STRING dbname, LEX_STRING name); + init(LEX_CSTRING dbname, LEX_CSTRING name); - LEX_STRING dbname; - LEX_STRING name; + LEX_CSTRING dbname; + LEX_CSTRING name; bool dropped; THD *thd; @@ -58,9 +58,9 @@ protected: public: - LEX_STRING dbname; - LEX_STRING name; - LEX_STRING definer;// combination of user and host + LEX_CSTRING dbname; + LEX_CSTRING name; + LEX_CSTRING definer;// combination of user and host Time_zone *time_zone; @@ -75,7 +75,7 @@ protected: load_string_fields(Field **fields, ...); bool - load_time_zone(THD *thd, const LEX_STRING tz_name); + load_time_zone(THD *thd, const LEX_CSTRING *tz_name); }; @@ -122,12 +122,12 @@ class Event_timed : public Event_queue_element void operator=(Event_timed &); public: - LEX_STRING body; + LEX_CSTRING body; - LEX_STRING definer_user; - LEX_STRING definer_host; + LEX_CSTRING definer_user; + LEX_CSTRING definer_host; - LEX_STRING comment; + LEX_CSTRING comment; ulonglong created; ulonglong modified; @@ -135,7 +135,7 @@ public: sql_mode_t sql_mode; class Stored_program_creation_ctx *creation_ctx; - LEX_STRING body_utf8; + LEX_CSTRING body_utf8; Event_timed(); virtual ~Event_timed(); @@ -154,9 +154,9 @@ public: class Event_job_data : public Event_basic { public: - LEX_STRING body; - LEX_STRING definer_user; - LEX_STRING definer_host; + LEX_CSTRING body; + LEX_CSTRING definer_user; + LEX_CSTRING definer_host; sql_mode_t sql_mode; @@ -182,11 +182,12 @@ private: /* Compares only the schema part of the identifier */ bool -event_basic_db_equal(LEX_STRING db, Event_basic *et); +event_basic_db_equal(const LEX_CSTRING *db, Event_basic *et); /* Compares the whole identifier*/ bool -event_basic_identifier_equal(LEX_STRING db, LEX_STRING name, Event_basic *b); +event_basic_identifier_equal(const LEX_CSTRING *db, const LEX_CSTRING *name, + Event_basic *b); /** @} (End of group Event_Scheduler) diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 50bc84883a0..20c2384ff04 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -368,14 +368,14 @@ mysql_event_fill_row(THD *thd, if (rs) { - my_error(ER_EVENT_STORE_FAILED, MYF(0), fields[f_num]->field_name, rs); + my_error(ER_EVENT_STORE_FAILED, MYF(0), fields[f_num]->field_name.str, rs); DBUG_RETURN(TRUE); } DBUG_RETURN(FALSE); err_truncate: - my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name); + my_error(ER_EVENT_DATA_TOO_LONG, MYF(0), fields[f_num]->field_name.str); DBUG_RETURN(TRUE); } @@ -670,7 +670,7 @@ Event_db_repository::create_event(THD *thd, Event_parse_data *parse_data, parse_data->name.str)); DBUG_PRINT("info", ("check existance of an event with the same name")); - if (!find_named_event(parse_data->dbname, parse_data->name, table)) + if (!find_named_event(&parse_data->dbname, &parse_data->name, table)) { if (thd->lex->create_info.or_replace()) { @@ -768,8 +768,8 @@ end: bool Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, - LEX_STRING *new_dbname, - LEX_STRING *new_name) + LEX_CSTRING *new_dbname, + LEX_CSTRING *new_name) { CHARSET_INFO *scs= system_charset_info; TABLE *table= NULL; @@ -802,7 +802,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, if (new_name) { DBUG_PRINT("info", ("rename to: %s@%s", new_dbname->str, new_name->str)); - if (!find_named_event(*new_dbname, *new_name, table)) + if (!find_named_event(new_dbname, new_name, table)) { my_error(ER_EVENT_ALREADY_EXISTS, MYF(0), new_name->str); goto end; @@ -814,7 +814,7 @@ Event_db_repository::update_event(THD *thd, Event_parse_data *parse_data, overwrite the key and SE will tell us that it cannot find the already found row (copied into record[1] later */ - if (find_named_event(parse_data->dbname, parse_data->name, table)) + if (find_named_event(&parse_data->dbname, &parse_data->name, table)) { my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), parse_data->name.str); goto end; @@ -878,7 +878,8 @@ end: */ bool -Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name, +Event_db_repository::drop_event(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *name, bool drop_if_exists) { TABLE *table= NULL; @@ -891,7 +892,7 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name, int ret= 1; DBUG_ENTER("Event_db_repository::drop_event"); - DBUG_PRINT("enter", ("%s@%s", db.str, name.str)); + DBUG_PRINT("enter", ("%s@%s", db->str, name->str)); if (open_event_table(thd, TL_WRITE, &table)) goto end; @@ -906,13 +907,13 @@ Event_db_repository::drop_event(THD *thd, LEX_STRING db, LEX_STRING name, /* Event not found */ if (!drop_if_exists) { - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str); goto end; } push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST), - "Event", name.str); + "Event", name->str); ret= 0; end: @@ -939,12 +940,13 @@ end: */ bool -Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name, +Event_db_repository::find_named_event(const LEX_CSTRING *db, + const LEX_CSTRING *name, TABLE *table) { uchar key[MAX_KEY_LENGTH]; DBUG_ENTER("Event_db_repository::find_named_event"); - DBUG_PRINT("enter", ("name: %.*s", (int) name.length, name.str)); + DBUG_PRINT("enter", ("name: %.*s", (int) name->length, name->str)); /* Create key to find row. We have to use field->store() to be able to @@ -953,16 +955,16 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name, 'db' and 'name' and the first key is the primary key over the same fields. */ - if (db.length > table->field[ET_FIELD_DB]->field_length || - name.length > table->field[ET_FIELD_NAME]->field_length || + if (db->length > table->field[ET_FIELD_DB]->field_length || + name->length > table->field[ET_FIELD_NAME]->field_length || table->s->keys == 0 || table->key_info[0].user_defined_key_parts != 2 || table->key_info[0].key_part[0].fieldnr != ET_FIELD_DB+1 || table->key_info[0].key_part[1].fieldnr != ET_FIELD_NAME+1) DBUG_RETURN(TRUE); - table->field[ET_FIELD_DB]->store(db.str, db.length, &my_charset_bin); - table->field[ET_FIELD_NAME]->store(name.str, name.length, &my_charset_bin); + table->field[ET_FIELD_DB]->store(db->str, db->length, &my_charset_bin); + table->field[ET_FIELD_NAME]->store(name->str, name->length, &my_charset_bin); key_copy(key, table->record[0], table->key_info, table->key_info->key_length); @@ -989,7 +991,7 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name, */ void -Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema) +Event_db_repository::drop_schema_events(THD *thd, const LEX_CSTRING *schema) { int ret= 0; TABLE *table= NULL; @@ -997,7 +999,7 @@ Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema) enum enum_events_table_field field= ET_FIELD_DB; MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint(); DBUG_ENTER("Event_db_repository::drop_schema_events"); - DBUG_PRINT("enter", ("field=%d schema=%s", field, schema.str)); + DBUG_PRINT("enter", ("field: %d schema: %s", field, schema->str)); if (open_event_table(thd, TL_WRITE, &table)) DBUG_VOID_RETURN; @@ -1013,12 +1015,12 @@ Event_db_repository::drop_schema_events(THD *thd, LEX_STRING schema) /* et_field may be NULL if the table is corrupted or out of memory */ if (et_field) { - LEX_STRING et_field_lex= { et_field, strlen(et_field) }; + LEX_CSTRING et_field_lex= { et_field, strlen(et_field) }; DBUG_PRINT("info", ("Current event %s name=%s", et_field, get_field(thd->mem_root, table->field[ET_FIELD_NAME]))); - if (!sortcmp_lex_string(et_field_lex, schema, system_charset_info)) + if (!sortcmp_lex_string(&et_field_lex, schema, system_charset_info)) { DBUG_PRINT("info", ("Dropping")); if ((ret= table->file->ha_delete_row(table->record[0]))) @@ -1051,8 +1053,9 @@ end: */ bool -Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, - LEX_STRING name, Event_basic *etn) +Event_db_repository::load_named_event(THD *thd, const LEX_CSTRING *dbname, + const LEX_CSTRING *name, + Event_basic *etn) { bool ret; ulonglong saved_mode= thd->variables.sql_mode; @@ -1061,7 +1064,7 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, DBUG_ENTER("Event_db_repository::load_named_event"); DBUG_PRINT("enter",("thd: %p name: %*s", thd, - (int) name.length, name.str)); + (int) name->length, name->str)); event_table.init_one_table("mysql", 5, "event", 5, "event", TL_READ); @@ -1084,7 +1087,7 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, } if ((ret= find_named_event(dbname, name, event_table.table))) - my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name.str); + my_error(ER_EVENT_DOES_NOT_EXIST, MYF(0), name->str); else if ((ret= etn->load_from_row(thd, event_table.table))) my_error(ER_CANNOT_LOAD_FROM_TABLE_V2, MYF(0), "mysql", "event"); @@ -1106,8 +1109,8 @@ Event_db_repository::load_named_event(THD *thd, LEX_STRING dbname, bool Event_db_repository:: update_timing_fields_for_event(THD *thd, - LEX_STRING event_db_name, - LEX_STRING event_name, + const LEX_CSTRING *event_db_name, + const LEX_CSTRING *event_name, my_time_t last_executed, ulonglong status) { @@ -1211,7 +1214,7 @@ Event_db_repository::check_system_tables(THD *thd) else { if (tables.table->s->fields < event_priv_column_position || - strncmp(tables.table->field[event_priv_column_position]->field_name, + strncmp(tables.table->field[event_priv_column_position]->field_name.str, STRING_WITH_LEN("Event_priv"))) { sql_print_error("mysql.user has no `Event_priv` column at position %d", diff --git a/sql/event_db_repository.h b/sql/event_db_repository.h index e7b52bacc2a..34de3ec2c45 100644 --- a/sql/event_db_repository.h +++ b/sql/event_db_repository.h @@ -77,20 +77,24 @@ public: create_event(THD *thd, Event_parse_data *parse_data, bool *event_already_exists); bool - update_event(THD *thd, Event_parse_data *parse_data, LEX_STRING *new_dbname, - LEX_STRING *new_name); + update_event(THD *thd, Event_parse_data *parse_data, LEX_CSTRING *new_dbname, + LEX_CSTRING *new_name); bool - drop_event(THD *thd, LEX_STRING db, LEX_STRING name, bool drop_if_exists); + drop_event(THD *thd, const LEX_CSTRING *db, const LEX_CSTRING *name, + bool drop_if_exists); void - drop_schema_events(THD *thd, LEX_STRING schema); + drop_schema_events(THD *thd, const LEX_CSTRING *schema); bool - find_named_event(LEX_STRING db, LEX_STRING name, TABLE *table); + find_named_event(const LEX_CSTRING *db, const LEX_CSTRING *name, + TABLE *table); bool - load_named_event(THD *thd, LEX_STRING dbname, LEX_STRING name, Event_basic *et); + load_named_event(THD *thd, const LEX_CSTRING *dbname, + const LEX_CSTRING *name, + Event_basic *et); static bool open_event_table(THD *thd, enum thr_lock_type lock_type, TABLE **table); @@ -100,8 +104,8 @@ public: bool update_timing_fields_for_event(THD *thd, - LEX_STRING event_db_name, - LEX_STRING event_name, + const LEX_CSTRING *event_db_name, + const LEX_CSTRING *event_name, my_time_t last_executed, ulonglong status); public: diff --git a/sql/event_parse_data.cc b/sql/event_parse_data.cc index 6fa0c25508e..e203712e229 100644 --- a/sql/event_parse_data.cc +++ b/sql/event_parse_data.cc @@ -88,9 +88,6 @@ Event_parse_data::init_name(THD *thd, sp_name *spn) name.length= spn->m_name.length; name.str= thd->strmake(spn->m_name.str, spn->m_name.length); - if (spn->m_qname.length == 0) - spn->init_qname(thd); - DBUG_VOID_RETURN; } @@ -531,7 +528,7 @@ Event_parse_data::init_definer(THD *thd) const char *definer_host= thd->lex->definer->host.str; size_t definer_user_len= thd->lex->definer->user.length; size_t definer_host_len= thd->lex->definer->host.length; - + char *tmp; DBUG_PRINT("info",("init definer_user thd->mem_root: %p " "definer_user: %p", thd->mem_root, definer_user)); @@ -539,15 +536,14 @@ Event_parse_data::init_definer(THD *thd) /* + 1 for @ */ DBUG_PRINT("info",("init definer as whole")); definer.length= definer_user_len + definer_host_len + 1; - definer.str= (char*) thd->alloc(definer.length + 1); + definer.str= tmp= (char*) thd->alloc(definer.length + 1); DBUG_PRINT("info",("copy the user")); - memcpy(definer.str, definer_user, definer_user_len); - definer.str[definer_user_len]= '@'; + strmake(tmp, definer_user, definer_user_len); + tmp[definer_user_len]= '@'; DBUG_PRINT("info",("copy the host")); - memcpy(definer.str + definer_user_len + 1, definer_host, definer_host_len); - definer.str[definer.length]= '\0'; + strmake(tmp + definer_user_len + 1, definer_host, definer_host_len); DBUG_PRINT("info",("definer [%s] initted", definer.str)); DBUG_VOID_RETURN; diff --git a/sql/event_parse_data.h b/sql/event_parse_data.h index 3ca7fcaab72..e1aed36aa01 100644 --- a/sql/event_parse_data.h +++ b/sql/event_parse_data.h @@ -66,10 +66,10 @@ public: bool body_changed; - LEX_STRING dbname; - LEX_STRING name; - LEX_STRING definer;// combination of user and host - LEX_STRING comment; + LEX_CSTRING dbname; + LEX_CSTRING name; + LEX_CSTRING definer;// combination of user and host + LEX_CSTRING comment; Item* item_starts; Item* item_ends; diff --git a/sql/event_queue.cc b/sql/event_queue.cc index 01a1507f6f2..208ad55c5f5 100644 --- a/sql/event_queue.cc +++ b/sql/event_queue.cc @@ -238,11 +238,13 @@ Event_queue::create_event(THD *thd, Event_queue_element *new_element, */ void -Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, +Event_queue::update_event(THD *thd, const LEX_CSTRING *dbname, + const LEX_CSTRING *name, Event_queue_element *new_element) { DBUG_ENTER("Event_queue::update_event"); - DBUG_PRINT("enter", ("thd: %p et=[%s.%s]", thd, dbname.str, name.str)); + DBUG_PRINT("enter", ("thd: %p et: [%s.%s]", thd, dbname->str, + name->str)); if ((new_element->status == Event_parse_data::DISABLED) || (new_element->status == Event_parse_data::SLAVESIDE_DISABLED)) @@ -287,11 +289,12 @@ Event_queue::update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, */ void -Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name) +Event_queue::drop_event(THD *thd, const LEX_CSTRING *dbname, + const LEX_CSTRING *name) { DBUG_ENTER("Event_queue::drop_event"); - DBUG_PRINT("enter", ("thd: %p db :%s name: %s", thd, - dbname.str, name.str)); + DBUG_PRINT("enter", ("thd: %p db: %s name: %s", thd, + dbname->str, name->str)); LOCK_QUEUE_DATA(); find_n_remove_event(dbname, name); @@ -325,12 +328,12 @@ Event_queue::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name) */ void -Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern, - bool (*comparator)(LEX_STRING, Event_basic *)) +Event_queue::drop_matching_events(THD *thd, const LEX_CSTRING *pattern, + bool (*comparator)(const LEX_CSTRING *, Event_basic *)) { uint i; DBUG_ENTER("Event_queue::drop_matching_events"); - DBUG_PRINT("enter", ("pattern=%s", pattern.str)); + DBUG_PRINT("enter", ("pattern: %s", pattern->str)); for (i= queue_first_element(&queue) ; i <= queue_last_element(&queue) ; @@ -380,7 +383,7 @@ Event_queue::drop_matching_events(THD *thd, LEX_STRING pattern, */ void -Event_queue::drop_schema_events(THD *thd, LEX_STRING schema) +Event_queue::drop_schema_events(THD *thd, const LEX_CSTRING *schema) { DBUG_ENTER("Event_queue::drop_schema_events"); LOCK_QUEUE_DATA(); @@ -404,7 +407,8 @@ Event_queue::drop_schema_events(THD *thd, LEX_STRING schema) */ void -Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name) +Event_queue::find_n_remove_event(const LEX_CSTRING *db, + const LEX_CSTRING *name) { uint i; DBUG_ENTER("Event_queue::find_n_remove_event"); @@ -414,7 +418,7 @@ Event_queue::find_n_remove_event(LEX_STRING db, LEX_STRING name) i++) { Event_queue_element *et= (Event_queue_element *) queue_element(&queue, i); - DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db.str, name.str, + DBUG_PRINT("info", ("[%s.%s]==[%s.%s]?", db->str, name->str, et->dbname.str, et->name.str)); if (event_basic_identifier_equal(db, name, et)) { @@ -683,7 +687,7 @@ end: Event_db_repository *db_repository= Events::get_db_repository(); (void) db_repository->update_timing_fields_for_event(thd, - (*event_name)->dbname, (*event_name)->name, + &(*event_name)->dbname, &(*event_name)->name, last_executed, (ulonglong) status); } diff --git a/sql/event_queue.h b/sql/event_queue.h index fdd5937ee17..ad3809fe086 100644 --- a/sql/event_queue.h +++ b/sql/event_queue.h @@ -31,7 +31,7 @@ extern PSI_cond_key key_COND_queue_state; #endif /* HAVE_PSI_INTERFACE */ #include "queues.h" // QUEUE -#include "sql_string.h" /* LEX_STRING */ +#include "sql_string.h" /* LEX_CSTRING */ #include "my_time.h" /* my_time_t, interval_type */ class Event_basic; @@ -60,14 +60,14 @@ public: bool *created); void - update_event(THD *thd, LEX_STRING dbname, LEX_STRING name, + update_event(THD *thd, const LEX_CSTRING *dbname, const LEX_CSTRING *name, Event_queue_element *new_element); void - drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name); + drop_event(THD *thd, const LEX_CSTRING *dbname, const LEX_CSTRING *name); void - drop_schema_events(THD *thd, LEX_STRING schema); + drop_schema_events(THD *thd, const LEX_CSTRING *schema); void recalculate_activation_times(THD *thd); @@ -98,12 +98,12 @@ private: const char *src_func, const char *src_file, uint src_line); void - find_n_remove_event(LEX_STRING db, LEX_STRING name); + find_n_remove_event(const LEX_CSTRING *db, const LEX_CSTRING *name); void - drop_matching_events(THD *thd, LEX_STRING pattern, - bool (*)(LEX_STRING, Event_basic *)); + drop_matching_events(THD *thd, const LEX_CSTRING *pattern, + bool (*)(const LEX_CSTRING*, Event_basic *)); void diff --git a/sql/event_scheduler.cc b/sql/event_scheduler.cc index 57bbf0e1eea..1e808a17604 100644 --- a/sql/event_scheduler.cc +++ b/sql/event_scheduler.cc @@ -301,7 +301,7 @@ Event_worker_thread::run(THD *thd, Event_queue_element_for_exec *event) if (res) goto end; - if ((res= db_repository->load_named_event(thd, event->dbname, event->name, + if ((res= db_repository->load_named_event(thd, &event->dbname, &event->name, &job_data))) { DBUG_PRINT("error", ("Got error from load_named_event")); diff --git a/sql/events.cc b/sql/events.cc index 11d54894d1d..6a38d4d3a1f 100644 --- a/sql/events.cc +++ b/sql/events.cc @@ -100,10 +100,11 @@ ulong Events::inited; 1 s > t */ -int sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs) +int sortcmp_lex_string(const LEX_CSTRING *s, const LEX_CSTRING *t, + const CHARSET_INFO *cs) { - return cs->coll->strnncollsp(cs, (uchar *) s.str,s.length, - (uchar *) t.str,t.length); + return cs->coll->strnncollsp(cs, (uchar *) s->str, s->length, + (uchar *) t->str, t->length); } @@ -354,7 +355,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data) save_binlog_format= thd->set_current_stmt_binlog_format_stmt(); if (thd->lex->create_info.or_replace() && event_queue) - event_queue->drop_event(thd, parse_data->dbname, parse_data->name); + event_queue->drop_event(thd, &parse_data->dbname, &parse_data->name); /* On error conditions my_error() is called so no need to handle here */ if (!(ret= db_repository->create_event(thd, parse_data, @@ -367,12 +368,12 @@ Events::create_event(THD *thd, Event_parse_data *parse_data) { if (!(new_element= new Event_queue_element())) ret= TRUE; // OOM - else if ((ret= db_repository->load_named_event(thd, parse_data->dbname, - parse_data->name, + else if ((ret= db_repository->load_named_event(thd, &parse_data->dbname, + &parse_data->name, new_element))) { - if (!db_repository->drop_event(thd, parse_data->dbname, - parse_data->name, TRUE)) + if (!db_repository->drop_event(thd, &parse_data->dbname, + &parse_data->name, TRUE)) dropped= 1; delete new_element; } @@ -440,7 +441,7 @@ Events::create_event(THD *thd, Event_parse_data *parse_data) bool Events::update_event(THD *thd, Event_parse_data *parse_data, - LEX_STRING *new_dbname, LEX_STRING *new_name) + LEX_CSTRING *new_dbname, LEX_CSTRING *new_name) { int ret; enum_binlog_format save_binlog_format; @@ -470,9 +471,9 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, if (new_dbname) /* It's a rename */ { /* Check that the new and the old names differ. */ - if ( !sortcmp_lex_string(parse_data->dbname, *new_dbname, + if ( !sortcmp_lex_string(&parse_data->dbname, new_dbname, system_charset_info) && - !sortcmp_lex_string(parse_data->name, *new_name, + !sortcmp_lex_string(&parse_data->name, new_name, system_charset_info)) { my_error(ER_EVENT_SAME_NAME, MYF(0)); @@ -513,12 +514,12 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, if (!(ret= db_repository->update_event(thd, parse_data, new_dbname, new_name))) { - LEX_STRING dbname= new_dbname ? *new_dbname : parse_data->dbname; - LEX_STRING name= new_name ? *new_name : parse_data->name; + LEX_CSTRING dbname= new_dbname ? *new_dbname : parse_data->dbname; + LEX_CSTRING name= new_name ? *new_name : parse_data->name; if (!(new_element= new Event_queue_element())) ret= TRUE; // OOM - else if ((ret= db_repository->load_named_event(thd, dbname, name, + else if ((ret= db_repository->load_named_event(thd, &dbname, &name, new_element))) delete new_element; else @@ -530,7 +531,7 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, it right away. */ if (event_queue) - event_queue->update_event(thd, parse_data->dbname, parse_data->name, + event_queue->update_event(thd, &parse_data->dbname, &parse_data->name, new_element); /* Binlog the alter event. */ DBUG_ASSERT(thd->query() && thd->query_length()); @@ -568,7 +569,8 @@ Events::update_event(THD *thd, Event_parse_data *parse_data, */ bool -Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) +Events::drop_event(THD *thd, const LEX_CSTRING *dbname, + const LEX_CSTRING *name, bool if_exists) { int ret; enum_binlog_format save_binlog_format; @@ -577,7 +579,7 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) if (check_if_system_tables_error()) DBUG_RETURN(TRUE); - if (check_access(thd, EVENT_ACL, dbname.str, NULL, NULL, 0, 0)) + if (check_access(thd, EVENT_ACL, dbname->str, NULL, NULL, 0, 0)) DBUG_RETURN(TRUE); /* @@ -587,7 +589,7 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) save_binlog_format= thd->set_current_stmt_binlog_format_stmt(); if (lock_object_name(thd, MDL_key::EVENT, - dbname.str, name.str)) + dbname->str, name->str)) DBUG_RETURN(TRUE); /* On error conditions my_error() is called so no need to handle here */ if (!(ret= db_repository->drop_event(thd, dbname, name, if_exists))) @@ -616,9 +618,9 @@ Events::drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists) */ void -Events::drop_schema_events(THD *thd, char *db) +Events::drop_schema_events(THD *thd, const char *db) { - LEX_STRING const db_lex= { db, strlen(db) }; + const LEX_CSTRING db_lex= { db, strlen(db) }; DBUG_ENTER("Events::drop_schema_events"); DBUG_PRINT("enter", ("dropping events from %s", db)); @@ -630,8 +632,8 @@ Events::drop_schema_events(THD *thd, char *db) are damaged, as intended. */ if (event_queue) - event_queue->drop_schema_events(thd, db_lex); - db_repository->drop_schema_events(thd, db_lex); + event_queue->drop_schema_events(thd, &db_lex); + db_repository->drop_schema_events(thd, &db_lex); DBUG_VOID_RETURN; } @@ -648,7 +650,7 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol) char show_str_buf[10 * STRING_BUFFER_USUAL_SIZE]; String show_str(show_str_buf, sizeof(show_str_buf), system_charset_info); List<Item> field_list; - LEX_STRING sql_mode; + LEX_CSTRING sql_mode; const String *tz_name; MEM_ROOT *mem_root= thd->mem_root; DBUG_ENTER("send_show_create_event"); @@ -731,18 +733,19 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol) */ bool -Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name) +Events::show_create_event(THD *thd, const LEX_CSTRING *dbname, + const LEX_CSTRING *name) { Event_timed et; bool ret; DBUG_ENTER("Events::show_create_event"); - DBUG_PRINT("enter", ("name: %s@%s", dbname.str, name.str)); + DBUG_PRINT("enter", ("name: %s@%s", dbname->str, name->str)); if (check_if_system_tables_error()) DBUG_RETURN(TRUE); - if (check_access(thd, EVENT_ACL, dbname.str, NULL, NULL, 0, 0)) + if (check_access(thd, EVENT_ACL, dbname->str, NULL, NULL, 0, 0)) DBUG_RETURN(TRUE); /* @@ -783,8 +786,9 @@ Events::show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name) int Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) { - char *db= NULL; + const char *db= NULL; int ret; + char db_tmp[SAFE_NAME_LEN]; DBUG_ENTER("Events::fill_schema_events"); /* @@ -808,10 +812,7 @@ Events::fill_schema_events(THD *thd, TABLE_LIST *tables, COND * /* cond */) check_access(thd, EVENT_ACL, thd->lex->select_lex.db, NULL, NULL, 0, 0)) DBUG_RETURN(1); - db= thd->lex->select_lex.db; - - if (lower_case_table_names) - my_casedn_str(system_charset_info, db); + db= normalize_db_name(thd->lex->select_lex.db, db_tmp, sizeof(db_tmp)); } ret= db_repository->fill_schema_events(thd, tables, db); diff --git a/sql/events.h b/sql/events.h index 368aa9a05d5..4b4505b6a02 100644 --- a/sql/events.h +++ b/sql/events.h @@ -36,7 +36,7 @@ extern PSI_stage_info stage_waiting_on_empty_queue; extern PSI_stage_info stage_waiting_for_next_activation; extern PSI_stage_info stage_waiting_for_scheduler_to_stop; -#include "sql_string.h" /* LEX_STRING */ +#include "sql_string.h" /* LEX_CSTRING */ #include "my_time.h" /* interval_type */ class Event_db_repository; @@ -48,7 +48,8 @@ class THD; typedef class Item COND; int -sortcmp_lex_string(LEX_STRING s, LEX_STRING t, CHARSET_INFO *cs); +sortcmp_lex_string(const LEX_CSTRING *s, const LEX_CSTRING *t, + const CHARSET_INFO *cs); /** @brief A facade to the functionality of the Event Scheduler. @@ -109,16 +110,18 @@ public: static bool update_event(THD *thd, Event_parse_data *parse_data, - LEX_STRING *new_dbname, LEX_STRING *new_name); + LEX_CSTRING *new_dbname, LEX_CSTRING *new_name); static bool - drop_event(THD *thd, LEX_STRING dbname, LEX_STRING name, bool if_exists); + drop_event(THD *thd, const LEX_CSTRING *dbname, const LEX_CSTRING *name, + bool if_exists); static void - drop_schema_events(THD *thd, char *db); + drop_schema_events(THD *thd, const char *db); static bool - show_create_event(THD *thd, LEX_STRING dbname, LEX_STRING name); + show_create_event(THD *thd, const LEX_CSTRING *dbname, + const LEX_CSTRING *name); /* Needed for both SHOW CREATE EVENT and INFORMATION_SCHEMA */ static int diff --git a/sql/field.cc b/sql/field.cc index 3a16f408fa3..c7aa0dce16e 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -51,15 +51,7 @@ *****************************************************************************/ static const char *zero_timestamp="0000-00-00 00:00:00.000000"; - -/* number of bytes to store second_part part of the TIMESTAMP(N) */ -static uint sec_part_bytes[MAX_DATETIME_PRECISION+1]= { 0, 1, 1, 2, 2, 3, 3 }; - -/* number of bytes to store DATETIME(N) */ -static uint datetime_hires_bytes[MAX_DATETIME_PRECISION+1]= { 5, 6, 6, 7, 7, 7, 8 }; - -/* number of bytes to store TIME(N) */ -static uint time_hires_bytes[MAX_DATETIME_PRECISION+1]= { 3, 4, 4, 5, 5, 5, 6 }; +LEX_CSTRING temp_lex_str= {"temp", 4}; uchar Field_null::null[1]={1}; const char field_separator=','; @@ -82,18 +74,35 @@ const char field_separator=','; following #defines describe that gap and how to canculate number of fields and index of field in this array. */ -#define FIELDTYPE_TEAR_FROM (MYSQL_TYPE_BIT + 1) -#define FIELDTYPE_TEAR_TO (MYSQL_TYPE_NEWDECIMAL - 1) -#define FIELDTYPE_NUM (FIELDTYPE_TEAR_FROM + (255 - FIELDTYPE_TEAR_TO)) +const int FIELDTYPE_TEAR_FROM= (MYSQL_TYPE_BIT + 1); +const int FIELDTYPE_TEAR_TO= (MYSQL_TYPE_NEWDECIMAL - 1); +const int FIELDTYPE_LAST= 254; +const int FIELDTYPE_NUM= FIELDTYPE_TEAR_FROM + (FIELDTYPE_LAST - + FIELDTYPE_TEAR_TO); + static inline int field_type2index (enum_field_types field_type) { + DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM || + real_type_to_type(field_type) > FIELDTYPE_TEAR_TO); + DBUG_ASSERT(field_type <= FIELDTYPE_LAST); field_type= real_type_to_type(field_type); - return (field_type < FIELDTYPE_TEAR_FROM ? - field_type : - ((int)FIELDTYPE_TEAR_FROM) + (field_type - FIELDTYPE_TEAR_TO) - 1); + if (field_type < FIELDTYPE_TEAR_FROM) + return field_type; + return FIELDTYPE_TEAR_FROM + (field_type - FIELDTYPE_TEAR_TO) - 1; } +/** + Implements data type merge rules for the built-in traditional data types. + Used for operations such as: + - UNION + - CASE and its abbreviations COALESCE, IF, IFNULL + - LEAST/GREATEST + + Given Fields A and B of real_types a and b, we find the result type of + COALESCE(A, B) by querying: + field_types_merge_rules[field_type_to_index(a)][field_type_to_index(b)]. +*/ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= { /* MYSQL_TYPE_DECIMAL -> */ @@ -107,7 +116,7 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 - MYSQL_TYPE_DECIMAL, MYSQL_TYPE_DECIMAL, + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_NEWDECIMAL, //MYSQL_TYPE_DATE MYSQL_TYPE_TIME MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR @@ -124,8 +133,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_TINY -> */ { @@ -155,8 +164,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_SHORT -> */ { @@ -186,8 +195,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_LONG -> */ { @@ -217,8 +226,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_FLOAT -> */ { @@ -248,8 +257,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_DOUBLE -> */ { @@ -279,8 +288,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_NULL -> */ { @@ -310,8 +319,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_TIMESTAMP -> */ { @@ -341,8 +350,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_LONGLONG -> */ { @@ -372,8 +381,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_INT24 -> */ { @@ -403,8 +412,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_DATE -> */ { @@ -434,8 +443,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_TIME -> */ { @@ -465,8 +474,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_DATETIME -> */ { @@ -496,13 +505,13 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_YEAR -> */ { //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY - MYSQL_TYPE_DECIMAL, MYSQL_TYPE_TINY, + MYSQL_TYPE_NEWDECIMAL, MYSQL_TYPE_TINY, //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG MYSQL_TYPE_SHORT, MYSQL_TYPE_LONG, //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE @@ -527,8 +536,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_NEWDATE -> */ { @@ -558,8 +567,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_VARCHAR -> */ { @@ -589,8 +598,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_BIT -> */ { @@ -620,8 +629,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_NEWDECIMAL -> */ { @@ -651,8 +660,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_ENUM -> */ { @@ -682,8 +691,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_SET -> */ { @@ -713,8 +722,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING }, /* MYSQL_TYPE_TINY_BLOB -> */ { @@ -744,8 +753,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_TINY_BLOB, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_TINY_BLOB, MYSQL_TYPE_TINY_BLOB + //MYSQL_TYPE_STRING + MYSQL_TYPE_TINY_BLOB }, /* MYSQL_TYPE_MEDIUM_BLOB -> */ { @@ -775,8 +784,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_MEDIUM_BLOB + //MYSQL_TYPE_STRING + MYSQL_TYPE_MEDIUM_BLOB }, /* MYSQL_TYPE_LONG_BLOB -> */ { @@ -806,8 +815,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_LONG_BLOB, MYSQL_TYPE_LONG_BLOB + //MYSQL_TYPE_STRING + MYSQL_TYPE_LONG_BLOB }, /* MYSQL_TYPE_BLOB -> */ { @@ -837,8 +846,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_BLOB, MYSQL_TYPE_BLOB + //MYSQL_TYPE_STRING + MYSQL_TYPE_BLOB }, /* MYSQL_TYPE_VAR_STRING -> */ { @@ -868,8 +877,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR + //MYSQL_TYPE_STRING + MYSQL_TYPE_VARCHAR }, /* MYSQL_TYPE_STRING -> */ { @@ -899,39 +908,8 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_STRING - }, - /* MYSQL_TYPE_GEOMETRY -> */ - { - //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP - MYSQL_TYPE_GEOMETRY, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_DATE MYSQL_TYPE_TIME - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_BIT <16>-<245> - MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB - MYSQL_TYPE_VARCHAR, MYSQL_TYPE_TINY_BLOB, - //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB - MYSQL_TYPE_MEDIUM_BLOB, MYSQL_TYPE_LONG_BLOB, - //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING - MYSQL_TYPE_BLOB, MYSQL_TYPE_VARCHAR, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - MYSQL_TYPE_STRING, MYSQL_TYPE_GEOMETRY + //MYSQL_TYPE_STRING + MYSQL_TYPE_STRING } }; @@ -948,46 +926,19 @@ static enum_field_types field_types_merge_rules [FIELDTYPE_NUM][FIELDTYPE_NUM]= enum_field_types Field::field_type_merge(enum_field_types a, enum_field_types b) { - DBUG_ASSERT(real_type_to_type(a) < FIELDTYPE_TEAR_FROM || - real_type_to_type(a) > FIELDTYPE_TEAR_TO); - DBUG_ASSERT(real_type_to_type(b) < FIELDTYPE_TEAR_FROM || - real_type_to_type(b) > FIELDTYPE_TEAR_TO); return field_types_merge_rules[field_type2index(a)] [field_type2index(b)]; } - -static Item_result field_types_result_type [FIELDTYPE_NUM]= +const Type_handler * +Type_handler::aggregate_for_result_traditional(const Type_handler *a, + const Type_handler *b) { - //MYSQL_TYPE_DECIMAL MYSQL_TYPE_TINY - DECIMAL_RESULT, INT_RESULT, - //MYSQL_TYPE_SHORT MYSQL_TYPE_LONG - INT_RESULT, INT_RESULT, - //MYSQL_TYPE_FLOAT MYSQL_TYPE_DOUBLE - REAL_RESULT, REAL_RESULT, - //MYSQL_TYPE_NULL MYSQL_TYPE_TIMESTAMP - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_LONGLONG MYSQL_TYPE_INT24 - INT_RESULT, INT_RESULT, - //MYSQL_TYPE_DATE MYSQL_TYPE_TIME - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_DATETIME MYSQL_TYPE_YEAR - STRING_RESULT, INT_RESULT, - //MYSQL_TYPE_NEWDATE MYSQL_TYPE_VARCHAR - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_BIT <16>-<245> - STRING_RESULT, - //MYSQL_TYPE_NEWDECIMAL MYSQL_TYPE_ENUM - DECIMAL_RESULT, STRING_RESULT, - //MYSQL_TYPE_SET MYSQL_TYPE_TINY_BLOB - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_MEDIUM_BLOB MYSQL_TYPE_LONG_BLOB - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_BLOB MYSQL_TYPE_VAR_STRING - STRING_RESULT, STRING_RESULT, - //MYSQL_TYPE_STRING MYSQL_TYPE_GEOMETRY - STRING_RESULT, STRING_RESULT -}; + enum_field_types ta= a->real_field_type(); + enum_field_types tb= b->real_field_type(); + return + Type_handler::get_handler_by_real_type(Field::field_type_merge(ta, tb)); +} /* @@ -1034,57 +985,11 @@ int compare(unsigned int a, unsigned int b) CPP_UNNAMED_NS_END -/** - Detect Item_result by given field type of UNION merge result. - - @param field_type given field type - - @return - Item_result (type of internal MySQL expression result) -*/ - -Item_result Field::result_merge_type(enum_field_types field_type) -{ - DBUG_ASSERT(real_type_to_type(field_type) < FIELDTYPE_TEAR_FROM || - real_type_to_type(field_type) > FIELDTYPE_TEAR_TO); - return field_types_result_type[field_type2index(field_type)]; -} /***************************************************************************** Static help functions *****************************************************************************/ -/** - Check whether a field type can be partially indexed by a key. - - This is a static method, rather than a virtual function, because we need - to check the type of a non-Field in mysql_alter_table(). - - @param type field type - - @retval - TRUE Type can have a prefixed key - @retval - FALSE Type can not have a prefixed key -*/ - -bool Field::type_can_have_key_part(enum enum_field_types type) -{ - switch (type) { - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_GEOMETRY: - return TRUE; - default: - return FALSE; - } -} - void Field::make_sort_key(uchar *buff,uint length) { @@ -1265,7 +1170,7 @@ bool Field::test_if_equality_guarantees_uniqueness(const Item *item) const bool Field::can_be_substituted_to_equal_item(const Context &ctx, const Item_equal *item_equal) { - DBUG_ASSERT(item_equal->compare_type() != STRING_RESULT); + DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT); DBUG_ASSERT(cmp_type() != STRING_RESULT); switch (ctx.subst_constraint()) { case ANY_SUBST: @@ -1278,7 +1183,7 @@ bool Field::can_be_substituted_to_equal_item(const Context &ctx, Items don't know the context they are in and there are functions like IF (<hex_string>, 'yes', 'no'). */ - return ctx.compare_type() == item_equal->compare_type(); + return ctx.compare_type_handler() == item_equal->compare_type_handler(); case IDENTITY_SUBST: return true; } @@ -1311,14 +1216,14 @@ bool Field::can_optimize_group_min_max(const Item_bool_func *cond, /* - This covers all numeric types, ENUM, SET, BIT + This covers all numeric types, BIT */ bool Field::can_optimize_range(const Item_bool_func *cond, const Item *item, bool is_eq_func) const { DBUG_ASSERT(cmp_type() != TIME_RESULT); // Handled in Field_temporal - DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_longstr + DBUG_ASSERT(cmp_type() != STRING_RESULT); // Handled in Field_str descendants return item->cmp_type() != TIME_RESULT; } @@ -1353,7 +1258,7 @@ warn: */ Field_num::Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg, bool zero_arg, bool unsigned_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), @@ -1394,7 +1299,7 @@ Item *Field_num::get_equal_zerofill_const_item(THD *thd, const Context &ctx, break; } DBUG_ASSERT(const_item->const_item()); - DBUG_ASSERT(ctx.compare_type() != STRING_RESULT); + DBUG_ASSERT(ctx.compare_type_handler()->cmp_type() != STRING_RESULT); return const_item; } @@ -1683,9 +1588,9 @@ String *Field::val_int_as_str(String *val_buffer, bool unsigned_val) /// This is used as a table name when the table structure is not set up Field::Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, - utype unireg_check_arg, const char *field_name_arg) + utype unireg_check_arg, const LEX_CSTRING *field_name_arg) :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(0), - table_name(0), field_name(field_name_arg), option_list(0), + table_name(0), field_name(*field_name_arg), option_list(0), option_struct(0), key_start(0), part_of_key(0), part_of_key_not_clustered(0), part_of_sortkey(0), unireg_check(unireg_check_arg), field_length(length_arg), @@ -1971,10 +1876,9 @@ void Field::make_field(Send_field *field) else { field->table_name= ""; - field->org_col_name= ""; + field->org_col_name= empty_clex_str; } field->col_name= field_name; - field->charsetnr= charset()->number; field->length=field_length; field->type=type(); field->flags=table->maybe_null ? (flags & ~NOT_NULL_FLAG) : flags; @@ -2079,21 +1983,22 @@ bool Field_num::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) longlong nr= val_int(); bool neg= !(flags & UNSIGNED_FLAG) && nr < 0; return int_to_datetime_with_warn(neg, neg ? -nr : nr, ltime, fuzzydate, - field_name); + field_name.str); } Field_str::Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, CHARSET_INFO *charset_arg) + const LEX_CSTRING *field_name_arg, + const DTCollation &collation) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) { - field_charset= charset_arg; - if (charset_arg->state & MY_CS_BINSORT) + field_charset= collation.collation; + if (collation.collation->state & MY_CS_BINSORT) flags|=BINARY_FLAG; - field_derivation= DERIVATION_IMPLICIT; - field_repertoire= my_charset_repertoire(charset_arg); + field_derivation= collation.derivation; + field_repertoire= collation.repertoire; } @@ -2126,11 +2031,11 @@ bool Field_str::test_if_equality_guarantees_uniqueness(const Item *item) const bool Field_str::can_be_substituted_to_equal_item(const Context &ctx, const Item_equal *item_equal) { - DBUG_ASSERT(item_equal->compare_type() == STRING_RESULT); + DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() == STRING_RESULT); switch (ctx.subst_constraint()) { case ANY_SUBST: - return ctx.compare_type() == item_equal->compare_type() && - (ctx.compare_type() != STRING_RESULT || + return ctx.compare_type_handler() == item_equal->compare_type_handler() && + (ctx.compare_type_handler()->cmp_type() != STRING_RESULT || ctx.compare_collation() == item_equal->compare_collation()); case IDENTITY_SUBST: return ((charset()->state & MY_CS_BINSORT) && @@ -2980,7 +2885,7 @@ Field_new_decimal::Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg,bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, @@ -2994,68 +2899,6 @@ Field_new_decimal::Field_new_decimal(uchar *ptr_arg, } -Field_new_decimal::Field_new_decimal(uint32 len_arg, - bool maybe_null_arg, - const char *name, - uint8 dec_arg, - bool unsigned_arg) - :Field_num((uchar*) 0, len_arg, - maybe_null_arg ? (uchar*) "": 0, 0, - NONE, name, dec_arg, 0, unsigned_arg) -{ - precision= my_decimal_length_to_precision(len_arg, dec_arg, unsigned_arg); - set_if_smaller(precision, DECIMAL_MAX_PRECISION); - DBUG_ASSERT((precision <= DECIMAL_MAX_PRECISION) && - (dec <= DECIMAL_MAX_SCALE)); - bin_size= my_decimal_get_binary_size(precision, dec); -} - - -Field *Field_new_decimal::create_from_item(MEM_ROOT *mem_root, Item *item) -{ - uint8 dec= item->decimals; - uint8 intg= item->decimal_precision() - dec; - uint32 len= item->max_char_length(); - - DBUG_ASSERT (item->result_type() == DECIMAL_RESULT); - - /* - Trying to put too many digits overall in a DECIMAL(prec,dec) - will always throw a warning. We must limit dec to - DECIMAL_MAX_SCALE however to prevent an assert() later. - */ - - if (dec > 0) - { - signed int overflow; - - dec= MY_MIN(dec, DECIMAL_MAX_SCALE); - - /* - If the value still overflows the field with the corrected dec, - we'll throw out decimals rather than integers. This is still - bad and of course throws a truncation warning. - +1: for decimal point - */ - - const int required_length= - my_decimal_precision_to_length(intg + dec, dec, - item->unsigned_flag); - - overflow= required_length - len; - - if (overflow > 0) - dec= MY_MAX(0, dec - overflow); // too long, discard fract - else - /* Corrected value fits. */ - len= required_length; - } - return new (mem_root) - Field_new_decimal(len, item->maybe_null, item->name, - dec, item->unsigned_flag); -} - - int Field_new_decimal::reset(void) { store_value(&decimal_zero); @@ -3356,7 +3199,7 @@ bool Field_new_decimal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { my_decimal value; return decimal_to_datetime_with_warn(val_decimal(&value), - ltime, fuzzydate, field_name); + ltime, fuzzydate, field_name.str); } @@ -3438,7 +3281,7 @@ bool Field_new_decimal::compatible_field_size(uint field_metadata, uint Field_new_decimal::is_equal(Create_field *new_field) { - return ((new_field->sql_type == real_type()) && + return ((new_field->type_handler() == type_handler()) && ((new_field->flags & UNSIGNED_FLAG) == (uint) (flags & UNSIGNED_FLAG)) && ((new_field->flags & AUTO_INCREMENT_FLAG) == @@ -3526,7 +3369,8 @@ Item *Field_new_decimal::get_equal_const_item(THD *thd, const Context &ctx, Field_time::get_equal_const_item(). */ my_decimal_round(E_DEC_FATAL_ERROR, val, decimals(), true, &val_buffer2); - return new (thd->mem_root) Item_decimal(thd, field_name, &val_buffer2, + return new (thd->mem_root) Item_decimal(thd, field_name.str, + &val_buffer2, decimals(), field_length); } break; @@ -4818,7 +4662,7 @@ bool Field_real::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) { ASSERT_COLUMN_MARKED_FOR_READ; double nr= val_real(); - return double_to_datetime_with_warn(nr, ltime, fuzzydate, field_name); + return double_to_datetime_with_warn(nr, ltime, fuzzydate, field_name.str); } @@ -4973,7 +4817,7 @@ void Field_double::sql_type(String &res) const Field_timestamp::Field_timestamp(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share) :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) @@ -5380,14 +5224,14 @@ static longlong read_bigendian(const uchar *from, uint bytes) void Field_timestamp_hires::store_TIME(my_time_t timestamp, ulong sec_part) { mi_int4store(ptr, timestamp); - store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes[dec]); + store_bigendian(sec_part_shift(sec_part, dec), ptr+4, sec_part_bytes(dec)); } my_time_t Field_timestamp_hires::get_timestamp(const uchar *pos, ulong *sec_part) const { ASSERT_COLUMN_MARKED_FOR_READ; - *sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes[dec]), dec); + *sec_part= (long)sec_part_unshift(read_bigendian(pos+4, sec_part_bytes(dec)), dec); return mi_uint4korr(pos); } @@ -5454,19 +5298,14 @@ int Field_timestamp_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) int32 a,b; ulong a_sec_part, b_sec_part; a= mi_uint4korr(a_ptr); - a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes[dec]); + a_sec_part= (ulong)read_bigendian(a_ptr+4, sec_part_bytes(dec)); b= mi_uint4korr(b_ptr); - b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes[dec]); + b_sec_part= (ulong)read_bigendian(b_ptr+4, sec_part_bytes(dec)); return ((uint32) a < (uint32) b) ? -1 : ((uint32) a > (uint32) b) ? 1 : a_sec_part < b_sec_part ? -1 : a_sec_part > b_sec_part ? 1 : 0; } -uint32 Field_timestamp_hires::pack_length() const -{ - return 4 + sec_part_bytes[dec]; -} - void Field_timestamp_with_dec::make_field(Send_field *field) { Field::make_field(field); @@ -5501,7 +5340,7 @@ my_time_t Field_timestampf::get_timestamp(const uchar *pos, /*************************************************************/ uint Field_temporal::is_equal(Create_field *new_field) { - return new_field->sql_type == real_type() && + return new_field->type_handler() == type_handler() && new_field->length == max_display_length(); } @@ -5913,7 +5752,7 @@ bool Field_time::check_zero_in_date_with_warn(ulonglong fuzzydate) THD *thd= get_thd(); push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, - ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name, + ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), field_name.str, thd->get_stmt_da()->current_row_for_warning()); return true; } @@ -6013,6 +5852,39 @@ int Field_time::store_decimal(const my_decimal *d) } +bool Field_time::can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal) +{ + DBUG_ASSERT(item_equal->compare_type_handler()->cmp_type() != STRING_RESULT); + switch (ctx.subst_constraint()) { + case ANY_SUBST: + /* + A TIME field in a DATETIME comparison can be substituted to + Item_equal with TIME comparison. + + SET timestamp=UNIX_TIMESTAMP('2015-08-30 10:20:30'); + CREATE OR REPLACE TABLE t1 (a TIME); + INSERT INTO t1 VALUES ('00:00:00'),('00:00:01'); + SELECT * FROM t1 WHERE a>=TIMESTAMP'2015-08-30 00:00:00' + AND a='00:00:00'; + + The above query can be simplified to: + SELECT * FROM t1 WHERE TIME'00:00:00'>=TIMESTAMP'2015-08-30 00:00:00' + AND a='00:00:00'; + And further to: + SELECT * FROM t1 WHERE a=TIME'00:00:00'; + */ + if (ctx.compare_type_handler() == &type_handler_datetime && + item_equal->compare_type_handler() == &type_handler_time) + return true; + return ctx.compare_type_handler() == item_equal->compare_type_handler(); + case IDENTITY_SUBST: + return true; + } + return false; +} + + Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, Item *const_item) { @@ -6072,11 +5944,6 @@ Item *Field_time::get_equal_const_item(THD *thd, const Context &ctx, } -uint32 Field_time_hires::pack_length() const -{ - return time_hires_bytes[dec]; -} - longlong Field_time_with_dec::val_int(void) { ASSERT_COLUMN_MARKED_FOR_READ; @@ -6294,7 +6161,7 @@ bool Field_year::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) if (tmp || field_length != 4) tmp+= 1900; return int_to_datetime_with_warn(false, tmp * 10000, - ltime, fuzzydate, field_name); + ltime, fuzzydate, field_name.str); } @@ -6779,11 +6646,6 @@ bool Field_datetime_hires::get_TIME(MYSQL_TIME *ltime, const uchar *pos, } -uint32 Field_datetime_hires::pack_length() const -{ - return datetime_hires_bytes[dec]; -} - int Field_datetime_hires::cmp(const uchar *a_ptr, const uchar *b_ptr) { ulonglong a=read_bigendian(a_ptr, Field_datetime_hires::pack_length()); @@ -6974,15 +6836,15 @@ int Field_str::store(double nr) uint Field::is_equal(Create_field *new_field) { - return (new_field->sql_type == real_type()); + return new_field->type_handler() == type_handler(); } uint Field_str::is_equal(Create_field *new_field) { - return ((new_field->sql_type == real_type()) && - new_field->charset == field_charset && - new_field->length == max_display_length()); + return new_field->type_handler() == type_handler() && + new_field->charset == field_charset && + new_field->length == max_display_length(); } @@ -7150,7 +7012,7 @@ check_field_for_37426(const void *param_arg) Check_field_param *param= (Check_field_param*) param_arg; DBUG_ASSERT(param->field->real_type() == MYSQL_TYPE_STRING); DBUG_PRINT("debug", ("Field %s - type: %d, size: %d", - param->field->field_name, + param->field->field_name.str, param->field->real_type(), param->field->row_pack_length())); return param->field->row_pack_length() > 255; @@ -7232,7 +7094,8 @@ uchar *Field_string::pack(uchar *to, const uchar *from, uint max_length) { uint length= MY_MIN(field_length,max_length); uint local_char_length= max_length/field_charset->mbmaxlen; - DBUG_PRINT("debug", ("Packing field '%s' - length: %u ", field_name, length)); + DBUG_PRINT("debug", ("Packing field '%s' - length: %u ", field_name.str, + length)); if (length > local_char_length) local_char_length= my_charpos(field_charset, from, from+length, @@ -7404,7 +7267,7 @@ Field *Field_string::make_new_field(MEM_ROOT *root, TABLE *new_table, if (type() != MYSQL_TYPE_VAR_STRING || keep_type) field= Field::make_new_field(root, new_table, keep_type); else if ((field= new (root) Field_varstring(field_length, maybe_null(), - field_name, + &field_name, new_table->s, charset()))) { /* @@ -7839,7 +7702,7 @@ Field *Field_varstring::new_key_field(MEM_ROOT *root, TABLE *new_table, uint Field_varstring::is_equal(Create_field *new_field) { - if (new_field->sql_type == real_type() && + if (new_field->type_handler() == type_handler() && new_field->charset == field_charset) { if (new_field->length == max_display_length()) @@ -7875,12 +7738,13 @@ void Field_varstring::hash(ulong *nr, ulong *nr2) ****************************************************************************/ Field_blob::Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share, uint blob_pack_length, - CHARSET_INFO *cs) + const DTCollation &collation) :Field_longstr(ptr_arg, BLOB_PACK_LENGTH_TO_MAX_LENGH(blob_pack_length), null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, - cs), + collation), packlength(blob_pack_length) { DBUG_ASSERT(blob_pack_length <= 4); // Only pack lengths 1-4 supported currently @@ -8229,8 +8093,10 @@ Field *Field_blob::new_key_field(MEM_ROOT *root, TABLE *new_table, uchar *new_null_ptr, uint new_null_bit) { Field_varstring *res= new (root) Field_varstring(new_ptr, length, 2, - new_null_ptr, new_null_bit, Field::NONE, - field_name, table->s, charset()); + new_null_ptr, + new_null_bit, Field::NONE, + &field_name, + table->s, charset()); res->init(new_table); return res; } @@ -8295,6 +8161,22 @@ void Field_blob::sort_string(uchar *to,uint length) } +/* + Return the data type handler, according to packlength. + Implemented in field.cc rather than in field.h + to avoid exporting type_handler_xxx with MYSQL_PLUGIN_IMPORT. +*/ +const Type_handler *Field_blob::type_handler() const +{ + switch (packlength) { + case 1: return &type_handler_tiny_blob; + case 2: return &type_handler_blob; + case 3: return &type_handler_medium_blob; + } + return &type_handler_long_blob; +} + + void Field_blob::sql_type(String &res) const { const char *str; @@ -8395,9 +8277,9 @@ uint Field_blob::max_packed_col_length(uint max_length) uint Field_blob::is_equal(Create_field *new_field) { - return ((new_field->sql_type == get_blob_type_from_length(max_data_length())) - && new_field->charset == field_charset && - new_field->pack_length == pack_length()); + return new_field->type_handler() == type_handler() && + new_field->charset == field_charset && + new_field->pack_length == pack_length(); } @@ -8424,7 +8306,7 @@ uint gis_field_options_image(uchar *buff, List<Create_field> &create_fields) Create_field *field; while ((field= it++)) { - if (field->sql_type != MYSQL_TYPE_GEOMETRY) + if (field->real_field_type() != MYSQL_TYPE_GEOMETRY) continue; if (buff) { @@ -8581,7 +8463,7 @@ int Field_geom::store(const char *from, uint length, CHARSET_INFO *cs) my_error(ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, MYF(0), Geometry::ci_collection[geom_type]->m_name.str, Geometry::ci_collection[wkb_type]->m_name.str, - field_name, + field_name.str, (ulong) table->in_use->get_stmt_da()-> current_row_for_warning()); goto err_exit; @@ -8617,7 +8499,7 @@ Field::geometry_type Field_geom::geometry_type_merge(geometry_type a, uint Field_geom::is_equal(Create_field *new_field) { - return new_field->sql_type == MYSQL_TYPE_GEOMETRY && + return new_field->type_handler() == type_handler() && /* - Allow ALTER..INPLACE to supertype (GEOMETRY), e.g. POINT to GEOMETRY or POLYGON to GEOMETRY. @@ -8696,12 +8578,16 @@ int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) { tmp=0; set_warning(WARN_DATA_TRUNCATED, 1); + err= 1; } - if (!get_thd()->count_cuted_fields) + if (!get_thd()->count_cuted_fields && !length) err= 0; } else + { set_warning(WARN_DATA_TRUNCATED, 1); + err= 1; + } } store_type((ulonglong) tmp); return err; @@ -8875,6 +8761,7 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) { tmp=0; set_warning(WARN_DATA_TRUNCATED, 1); + err= 1; } } else if (got_warning) @@ -9039,7 +8926,7 @@ uint Field_enum::is_equal(Create_field *new_field) The fields are compatible if they have the same flags, type, charset and have the same underlying length. */ - if (new_field->sql_type != real_type() || + if (new_field->type_handler() != type_handler() || new_field->charset != field_charset || new_field->pack_length != pack_length()) return IS_EQUAL_NO; @@ -9104,7 +8991,7 @@ bool Field_num::eq_def(const Field *field) const uint Field_num::is_equal(Create_field *new_field) { - return ((new_field->sql_type == real_type()) && + return ((new_field->type_handler() == type_handler()) && ((new_field->flags & UNSIGNED_FLAG) == (uint) (flags & UNSIGNED_FLAG)) && ((new_field->flags & AUTO_INCREMENT_FLAG) == @@ -9113,12 +9000,17 @@ uint Field_num::is_equal(Create_field *new_field) } +bool Field_enum::can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const +{ + return item->cmp_type() != TIME_RESULT; +} + + bool Field_enum::can_optimize_keypart_ref(const Item_bool_func *cond, const Item *item) const { - DBUG_ASSERT(cmp_type() == INT_RESULT); - DBUG_ASSERT(result_type() == STRING_RESULT); - switch (item->cmp_type()) { case TIME_RESULT: @@ -9168,7 +9060,8 @@ bool Field_enum::can_optimize_keypart_ref(const Item_bool_func *cond, Field_bit::Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg, - enum utype unireg_check_arg, const char *field_name_arg) + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg) : Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), bit_ptr(bit_ptr_arg), bit_ofs(bit_ofs_arg), bit_len(len_arg & 7), @@ -9252,8 +9145,8 @@ Field *Field_bit::new_key_field(MEM_ROOT *root, TABLE *new_table, uint Field_bit::is_equal(Create_field *new_field) { - return (new_field->sql_type == real_type() && - new_field->length == max_display_length()); + return new_field->type_handler() == type_handler() && + new_field->length == max_display_length(); } @@ -9683,7 +9576,7 @@ void Field_bit::set_default() Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg) + const LEX_CSTRING *field_name_arg) :Field_bit(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, 0, 0, unireg_check_arg, field_name_arg) { @@ -9734,60 +9627,192 @@ void Field_bit_as_char::sql_type(String &res) const Handling of field and Create_field *****************************************************************************/ -/** - Convert create_field::length from number of characters to number of bytes. -*/ - -void Column_definition::create_length_to_internal_length(void) -{ - switch (sql_type) { - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_GEOMETRY: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_VARCHAR: - length*= charset->mbmaxlen; - DBUG_ASSERT(length <= UINT_MAX32); - key_length= (uint32)length; - pack_length= calc_pack_length(sql_type, key_length); - break; - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - /* Pack_length already calculated in sql_parse.cc */ - length*= charset->mbmaxlen; - key_length= pack_length; - break; - case MYSQL_TYPE_BIT: - if (f_bit_as_char(pack_flag)) +bool Column_definition::create_interval_from_interval_list(MEM_ROOT *mem_root, + bool reuse_interval_list_values) +{ + DBUG_ENTER("Column_definition::create_interval_from_interval_list"); + DBUG_ASSERT(!interval); + if (!(interval= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB)))) + DBUG_RETURN(true); // EOM + + List_iterator<String> it(interval_list); + StringBuffer<64> conv; + char comma_buf[5]; /* 5 bytes for 'filename' charset */ + DBUG_ASSERT(sizeof(comma_buf) >= charset->mbmaxlen); + int comma_length= charset->cset->wc_mb(charset, ',', + (uchar*) comma_buf, + (uchar*) comma_buf + + sizeof(comma_buf)); + DBUG_ASSERT(comma_length >= 0 && comma_length <= (int) sizeof(comma_buf)); + + if (!multi_alloc_root(mem_root, + &interval->type_names, + sizeof(char*) * (interval_list.elements + 1), + &interval->type_lengths, + sizeof(uint) * (interval_list.elements + 1), + NullS)) + goto err; // EOM + + interval->name= ""; + interval->count= interval_list.elements; + + for (uint i= 0; i < interval->count; i++) + { + uint32 dummy; + String *tmp= it++; + LEX_CSTRING value; + if (String::needs_conversion(tmp->length(), tmp->charset(), + charset, &dummy)) { - key_length= pack_length= ((length + 7) & ~7) / 8; + uint cnv_errs; + conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), charset, &cnv_errs); + value.str= strmake_root(mem_root, conv.ptr(), conv.length()); + value.length= conv.length(); } else { - pack_length= (uint)(length / 8); - /* We need one extra byte to store the bits we save among the null bits */ - key_length= pack_length + MY_TEST(length & 7); + value.str= reuse_interval_list_values ? tmp->ptr() : + strmake_root(mem_root, + tmp->ptr(), + tmp->length()); + value.length= tmp->length(); } - break; - case MYSQL_TYPE_NEWDECIMAL: - key_length= pack_length= - my_decimal_get_binary_size(my_decimal_length_to_precision((uint)length, - decimals, - flags & - UNSIGNED_FLAG), - decimals); - break; - default: - key_length= pack_length= calc_pack_length(sql_type, (uint)length); - break; + if (!value.str) + goto err; // EOM + + // Strip trailing spaces. + value.length= charset->cset->lengthsp(charset, value.str, value.length); + ((char*) value.str)[value.length]= '\0'; + + if (real_field_type() == MYSQL_TYPE_SET) + { + if (charset->coll->instr(charset, value.str, value.length, + comma_buf, comma_length, NULL, 0)) + { + ErrConvString err(tmp); + my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr()); + goto err; + } + } + interval->type_names[i]= value.str; + interval->type_lengths[i]= value.length; + } + interval->type_names[interval->count]= 0; // End marker + interval->type_lengths[interval->count]= 0; + interval_list.empty(); // Don't need interval_list anymore + DBUG_RETURN(false); +err: + interval= NULL; // Avoid having both non-empty interval_list and interval + DBUG_RETURN(true); +} + + +bool Column_definition::prepare_interval_field(MEM_ROOT *mem_root, + bool reuse_interval_list_values) +{ + DBUG_ENTER("Column_definition::prepare_interval_field"); + DBUG_ASSERT(real_field_type() == MYSQL_TYPE_ENUM || + real_field_type() == MYSQL_TYPE_SET); + /* + Interval values are either in "interval" or in "interval_list", + but not in both at the same time, and are not empty at the same time. + - Values are in "interval_list" when we're coming from the parser + in CREATE TABLE or in CREATE {FUNCTION|PROCEDURE}. + - Values are in "interval" when we're in ALTER TABLE. + + In a corner case with an empty set like SET(''): + - after the parser we have interval_list.elements==1 + - in ALTER TABLE we have a non-NULL interval with interval->count==1, + with interval->type_names[0]=="" and interval->type_lengths[0]==0. + So the assert is still valid for this corner case. + + ENUM and SET with no values at all (e.g. ENUM(), SET()) are not possible, + as the parser requires at least one element, so for a ENUM or SET field it + should never happen that both internal_list.elements and interval are 0. + */ + DBUG_ASSERT((interval == NULL) == (interval_list.elements > 0)); + + /* + Create typelib from interval_list, and if necessary + convert strings from client character set to the + column character set. + */ + if (interval_list.elements && + create_interval_from_interval_list(mem_root, + reuse_interval_list_values)) + DBUG_RETURN(true); + + if (!reuse_interval_list_values) + { + /* + We're initializing from an existing table or view Field_enum + (e.g. for a %TYPE variable) rather than from the parser. + The constructor Column_definition(THD*,Field*,Field*) has already + copied the TYPELIB pointer from the original Field_enum. + Now we need to make a permanent copy of that TYPELIB, + as the original field can be freed before the end of the life + cycle of "this". + */ + DBUG_ASSERT(interval); + if (!(interval= copy_typelib(mem_root, interval))) + DBUG_RETURN(true); } + prepare_interval_field_calc_length(); + DBUG_RETURN(false); } -bool check_expression(Virtual_column_info *vcol, const char *name, +void Column_definition::set_attributes(const Lex_field_type_st &type, + CHARSET_INFO *cs) +{ + DBUG_ASSERT(type_handler() == &type_handler_null); + DBUG_ASSERT(charset == &my_charset_bin || charset == NULL); + DBUG_ASSERT(length == 0); + DBUG_ASSERT(decimals == 0); + + set_handler(type.type_handler()); + charset= cs; + + if (type.length()) + { + int err; + length= my_strtoll10(type.length(), NULL, &err); + if (err) + length= ~0ULL; // safety + } + + if (type.dec()) + decimals= (uint) atoi(type.dec()); +} + + +void Column_definition::create_length_to_internal_length_bit() +{ + if (f_bit_as_char(pack_flag)) + { + key_length= pack_length= ((length + 7) & ~7) / 8; + } + else + { + pack_length= (uint) length / 8; + /* We need one extra byte to store the bits we save among the null bits */ + key_length= pack_length + MY_TEST(length & 7); + } +} + + +void Column_definition::create_length_to_internal_length_newdecimal() +{ + key_length= pack_length= + my_decimal_get_binary_size(my_decimal_length_to_precision((uint) length, + decimals, + flags & + UNSIGNED_FLAG), + decimals); +} + + +bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name, enum_vcol_info_type type) { @@ -9795,7 +9820,7 @@ bool check_expression(Virtual_column_info *vcol, const char *name, Item::vcol_func_processor_result res; if (!vcol->name.length) - vcol->name.str= const_cast<char*>(name); + vcol->name= *name; /* Walk through the Item tree checking if all items are valid @@ -9812,7 +9837,7 @@ bool check_expression(Virtual_column_info *vcol, const char *name, if (ret || (res.errors & filter)) { my_error(ER_GENERATED_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), res.name, - vcol_type_name(type), name); + vcol_type_name(type), name->str); return TRUE; } /* @@ -9825,31 +9850,118 @@ bool check_expression(Virtual_column_info *vcol, const char *name, } +bool Column_definition::check_length(uint mysql_errno, uint limit) const +{ + if (length <= limit) + return false; + my_error(mysql_errno, MYF(0), field_name.str, static_cast<ulong>(limit)); + return true; +} + + +bool Column_definition::fix_attributes_int(uint default_length) +{ + if (length) + return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH); + length= default_length; + return false; +} + + +bool Column_definition::fix_attributes_real(uint default_length) +{ + /* change FLOAT(precision) to FLOAT or DOUBLE */ + if (!length && !decimals) + { + length= default_length; + decimals= NOT_FIXED_DEC; + } + if (length < decimals && decimals != NOT_FIXED_DEC) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str); + return true; + } + if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), + field_name.str, static_cast<uint>(FLOATING_POINT_DECIMALS-1)); + return true; + } + return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_CHARLENGTH); +} + + +bool Column_definition::fix_attributes_decimal() +{ + if (decimals >= NOT_FIXED_DEC) + { + my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), + field_name.str, static_cast<uint>(NOT_FIXED_DEC - 1)); + return true; + } + my_decimal_trim(&length, &decimals); + if (length > DECIMAL_MAX_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name.str, + DECIMAL_MAX_PRECISION); + return true; + } + if (length < decimals) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name.str); + return true; + } + length= my_decimal_precision_to_length((uint) length, decimals, + flags & UNSIGNED_FLAG); + pack_length= my_decimal_get_binary_size((uint) length, decimals); + return false; +} + + +bool Column_definition::fix_attributes_bit() +{ + if (!length) + length= 1; + pack_length= ((uint) length + 7) / 8; + return check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_BIT_FIELD_LENGTH); +} + + +bool Column_definition::fix_attributes_temporal_with_time(uint int_part_length) +{ + if (length > MAX_DATETIME_PRECISION) + { + my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name.str, + MAX_DATETIME_PRECISION); + return true; + } + length+= int_part_length + (length ? 1 : 0); + return false; +} + + bool Column_definition::check(THD *thd) { - const uint conditional_type_modifiers= AUTO_INCREMENT_FLAG; - uint sign_len, allowed_type_modifier= 0; - ulong max_field_charlength= MAX_FIELD_CHARLENGTH; DBUG_ENTER("Column_definition::check"); /* Initialize data for a computed field */ if (vcol_info) { DBUG_ASSERT(vcol_info->expr); - vcol_info->set_field_type(sql_type); - if (check_expression(vcol_info, field_name, vcol_info->stored_in_db + vcol_info->set_field_type(real_field_type()); + if (check_expression(vcol_info, &field_name, vcol_info->stored_in_db ? VCOL_GENERATED_STORED : VCOL_GENERATED_VIRTUAL)) DBUG_RETURN(TRUE); } if (check_constraint && - check_expression(check_constraint, field_name, VCOL_CHECK_FIELD)) + check_expression(check_constraint, &field_name, VCOL_CHECK_FIELD)) DBUG_RETURN(1); if (default_value) { Item *def_expr= default_value->expr; - if (check_expression(default_value, field_name, VCOL_DEFAULT)) + if (check_expression(default_value, &field_name, VCOL_DEFAULT)) DBUG_RETURN(TRUE); /* Constant's are stored in the 'empty_record', except for blobs */ @@ -9860,7 +9972,7 @@ bool Column_definition::check(THD *thd) default_value= 0; if ((flags & (NOT_NULL_FLAG | AUTO_INCREMENT_FLAG)) == NOT_NULL_FLAG) { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); DBUG_RETURN(1); } } @@ -9869,12 +9981,12 @@ bool Column_definition::check(THD *thd) if (default_value && (flags & AUTO_INCREMENT_FLAG)) { - my_error(ER_INVALID_DEFAULT, MYF(0), field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); DBUG_RETURN(1); } if (default_value && !default_value->expr->basic_const_item() && - mysql_type_to_time_type(sql_type) == MYSQL_TIMESTAMP_DATETIME && + mysql_timestamp_type() == MYSQL_TIMESTAMP_DATETIME && default_value->expr->type() == Item::FUNC_ITEM) { /* @@ -9892,10 +10004,10 @@ bool Column_definition::check(THD *thd) if (on_update) { - if (mysql_type_to_time_type(sql_type) != MYSQL_TIMESTAMP_DATETIME || + if (mysql_timestamp_type() != MYSQL_TIMESTAMP_DATETIME || on_update->decimals < length) { - my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name); + my_error(ER_INVALID_ON_UPDATE, MYF(0), field_name.str); DBUG_RETURN(TRUE); } unireg_check= unireg_check == Field::NONE ? Field::TIMESTAMP_UN_FIELD @@ -9904,186 +10016,9 @@ bool Column_definition::check(THD *thd) else if (flags & AUTO_INCREMENT_FLAG) unireg_check= Field::NEXT_NUMBER; - sign_len= flags & UNSIGNED_FLAG ? 0 : 1; + if (type_handler()->Column_definition_fix_attributes(this)) + DBUG_RETURN(true); - switch (sql_type) { - case MYSQL_TYPE_TINY: - if (!length) - length= MAX_TINYINT_WIDTH+sign_len; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_SHORT: - if (!length) - length= MAX_SMALLINT_WIDTH+sign_len; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_INT24: - if (!length) - length= MAX_MEDIUMINT_WIDTH+sign_len; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_LONG: - if (!length) - length= MAX_INT_WIDTH+sign_len; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_LONGLONG: - if (!length) - length= MAX_BIGINT_WIDTH; - allowed_type_modifier= AUTO_INCREMENT_FLAG; - break; - case MYSQL_TYPE_NULL: - break; - case MYSQL_TYPE_NEWDECIMAL: - if (decimals >= NOT_FIXED_DEC) - { - my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), - field_name, static_cast<ulong>(NOT_FIXED_DEC - 1)); - DBUG_RETURN(TRUE); - } - my_decimal_trim(&length, &decimals); - if (length > DECIMAL_MAX_PRECISION) - { - my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, - DECIMAL_MAX_PRECISION); - DBUG_RETURN(TRUE); - } - if (length < decimals) - { - my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); - DBUG_RETURN(TRUE); - } - length= - my_decimal_precision_to_length((uint)length, decimals, flags & UNSIGNED_FLAG); - pack_length= - my_decimal_get_binary_size((uint)length, decimals); - break; - case MYSQL_TYPE_VARCHAR: - /* - Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table - if they don't have a default value - */ - max_field_charlength= MAX_FIELD_VARCHARLENGTH; - break; - case MYSQL_TYPE_STRING: - break; - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_GEOMETRY: - flags|= BLOB_FLAG; - break; - case MYSQL_TYPE_YEAR: - if (!length || length != 2) - length= 4; /* Default length */ - flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; - break; - case MYSQL_TYPE_FLOAT: - /* change FLOAT(precision) to FLOAT or DOUBLE */ - allowed_type_modifier= AUTO_INCREMENT_FLAG; - if (!length && !decimals) - { - length= MAX_FLOAT_STR_LENGTH; - decimals= NOT_FIXED_DEC; - } - if (length < decimals && - decimals != NOT_FIXED_DEC) - { - my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); - DBUG_RETURN(TRUE); - } - if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS) - { - my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), - field_name, static_cast<ulong>(FLOATING_POINT_DECIMALS-1)); - DBUG_RETURN(TRUE); - } - break; - case MYSQL_TYPE_DOUBLE: - allowed_type_modifier= AUTO_INCREMENT_FLAG; - if (!length && !decimals) - { - length= DBL_DIG+7; - decimals= NOT_FIXED_DEC; - } - if (length < decimals && - decimals != NOT_FIXED_DEC) - { - my_error(ER_M_BIGGER_THAN_D, MYF(0), field_name); - DBUG_RETURN(TRUE); - } - if (decimals != NOT_FIXED_DEC && decimals >= FLOATING_POINT_DECIMALS) - { - my_error(ER_TOO_BIG_SCALE, MYF(0), static_cast<ulonglong>(decimals), - field_name, static_cast<ulong>(FLOATING_POINT_DECIMALS-1)); - DBUG_RETURN(TRUE); - } - break; - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_TIMESTAMP2: - if (length > MAX_DATETIME_PRECISION) - { - my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, - MAX_DATETIME_PRECISION); - DBUG_RETURN(TRUE); - } - length+= MAX_DATETIME_WIDTH + (length ? 1 : 0); - flags|= UNSIGNED_FLAG; - break; - case MYSQL_TYPE_DATE: - /* We don't support creation of MYSQL_TYPE_DATE anymore */ - sql_type= MYSQL_TYPE_NEWDATE; - /* fall through */ - case MYSQL_TYPE_NEWDATE: - length= MAX_DATE_WIDTH; - break; - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_TIME2: - if (length > MAX_DATETIME_PRECISION) - { - my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, - MAX_DATETIME_PRECISION); - DBUG_RETURN(TRUE); - } - length+= MIN_TIME_WIDTH + (length ? 1 : 0); - break; - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_DATETIME2: - if (length > MAX_DATETIME_PRECISION) - { - my_error(ER_TOO_BIG_PRECISION, MYF(0), length, field_name, - MAX_DATETIME_PRECISION); - DBUG_RETURN(TRUE); - } - length+= MAX_DATETIME_WIDTH + (length ? 1 : 0); - break; - case MYSQL_TYPE_SET: - pack_length= get_set_pack_length(interval_list.elements); - break; - case MYSQL_TYPE_ENUM: - /* Should be safe. */ - pack_length= get_enum_pack_length(interval_list.elements); - break; - case MYSQL_TYPE_VAR_STRING: - DBUG_ASSERT(0); /* Impossible. */ - break; - case MYSQL_TYPE_BIT: - { - if (!length) - length= 1; - if (length > MAX_BIT_FIELD_LENGTH) - { - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, - static_cast<ulong>(MAX_BIT_FIELD_LENGTH)); - DBUG_RETURN(TRUE); - } - pack_length= ((uint)length + 7) / 8; - break; - } - case MYSQL_TYPE_DECIMAL: - DBUG_ASSERT(0); /* Was obsolete */ - } /* Remember the value of length */ char_length= (uint)length; @@ -10100,37 +10035,17 @@ bool Column_definition::check(THD *thd) explicit_defaults_for_timestamp is not set. */ if (opt_explicit_defaults_for_timestamp || - !is_timestamp_type(sql_type)) + !is_timestamp_type()) { flags|= NO_DEFAULT_VALUE_FLAG; } } - if (!(flags & BLOB_FLAG) && - ((length > max_field_charlength && - sql_type != MYSQL_TYPE_VARCHAR) || - (length == 0 && - sql_type != MYSQL_TYPE_ENUM && sql_type != MYSQL_TYPE_SET && - sql_type != MYSQL_TYPE_STRING && sql_type != MYSQL_TYPE_VARCHAR && - sql_type != MYSQL_TYPE_GEOMETRY))) - { - my_error((sql_type == MYSQL_TYPE_VAR_STRING || - sql_type == MYSQL_TYPE_VARCHAR || - sql_type == MYSQL_TYPE_STRING) ? ER_TOO_BIG_FIELDLENGTH : - ER_TOO_BIG_DISPLAYWIDTH, - MYF(0), - field_name, max_field_charlength); /* purecov: inspected */ - DBUG_RETURN(TRUE); - } - else if (length > MAX_FIELD_BLOBLENGTH) - { - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name, MAX_FIELD_BLOBLENGTH); - DBUG_RETURN(1); - } - if ((~allowed_type_modifier) & flags & conditional_type_modifiers) + if ((flags & AUTO_INCREMENT_FLAG) && + !type_handler()->type_can_have_auto_increment_attribute()) { - my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name); + my_error(ER_WRONG_FIELD_SPEC, MYF(0), field_name.str); DBUG_RETURN(TRUE); } @@ -10152,64 +10067,6 @@ enum_field_types get_blob_type_from_length(ulong length) } -/* - Make a field from the .frm file info -*/ - -uint32 calc_pack_length(enum_field_types type,uint32 length) -{ - switch (type) { - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_DECIMAL: return (length); - case MYSQL_TYPE_VARCHAR: return (length + (length < 256 ? 1: 2)); - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_TINY : return 1; - case MYSQL_TYPE_SHORT : return 2; - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_NEWDATE: return 3; - case MYSQL_TYPE_TIME: return length > MIN_TIME_WIDTH - ? time_hires_bytes[length - 1 - MIN_TIME_WIDTH] - : 3; - case MYSQL_TYPE_TIME2: - return length > MIN_TIME_WIDTH ? - my_time_binary_length(length - MIN_TIME_WIDTH - 1) : 3; - case MYSQL_TYPE_TIMESTAMP: - return length > MAX_DATETIME_WIDTH - ? 4 + sec_part_bytes[length - 1 - MAX_DATETIME_WIDTH] - : 4; - case MYSQL_TYPE_TIMESTAMP2: - return length > MAX_DATETIME_WIDTH ? - my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4; - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_LONG : return 4; - case MYSQL_TYPE_FLOAT : return sizeof(float); - case MYSQL_TYPE_DOUBLE: return sizeof(double); - case MYSQL_TYPE_DATETIME: - return length > MAX_DATETIME_WIDTH - ? datetime_hires_bytes[length - 1 - MAX_DATETIME_WIDTH] - : 8; - case MYSQL_TYPE_DATETIME2: - return length > MAX_DATETIME_WIDTH ? - my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5; - case MYSQL_TYPE_LONGLONG: return 8; /* Don't crash if no longlong */ - case MYSQL_TYPE_NULL : return 0; - case MYSQL_TYPE_TINY_BLOB: return 1+portable_sizeof_char_ptr; - case MYSQL_TYPE_BLOB: return 2+portable_sizeof_char_ptr; - case MYSQL_TYPE_MEDIUM_BLOB: return 3+portable_sizeof_char_ptr; - case MYSQL_TYPE_LONG_BLOB: return 4+portable_sizeof_char_ptr; - case MYSQL_TYPE_GEOMETRY: return 4+portable_sizeof_char_ptr; - case MYSQL_TYPE_SET: - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_NEWDECIMAL: - abort(); return 0; // This shouldn't happen - case MYSQL_TYPE_BIT: return length / 8; - default: - return 0; - } -} - - uint pack_length_to_packflag(uint type) { switch (type) { @@ -10228,17 +10085,17 @@ Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, uchar *null_pos, uchar null_bit, uint pack_flag, - enum_field_types field_type, + const Type_handler *handler, CHARSET_INFO *field_charset, Field::geometry_type geom_type, uint srid, Field::utype unireg_check, TYPELIB *interval, - const char *field_name) + const LEX_CSTRING *field_name) { uchar *UNINIT_VAR(bit_ptr); uchar UNINIT_VAR(bit_offset); - if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) + if (handler->real_field_type() == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { bit_ptr= null_pos; bit_offset= null_bit; @@ -10259,8 +10116,8 @@ Field *make_field(TABLE_SHARE *share, null_bit= ((uchar) 1) << null_bit; } - DBUG_PRINT("debug", ("field_type: %d, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s", - field_type, field_length, interval, + DBUG_PRINT("debug", ("field_type: %s, field_length: %u, interval: %p, pack_flag: %s%s%s%s%s", + handler->name().ptr(), field_length, interval, FLAGSTR(pack_flag, FIELDFLAG_BINARY), FLAGSTR(pack_flag, FIELDFLAG_INTERVAL), FLAGSTR(pack_flag, FIELDFLAG_NUMBER), @@ -10271,6 +10128,7 @@ Field *make_field(TABLE_SHARE *share, { if (!f_is_packed(pack_flag)) { + enum_field_types field_type= handler->real_field_type(); if (field_type == MYSQL_TYPE_STRING || field_type == MYSQL_TYPE_DECIMAL || // 3.23 or 4.0 string field_type == MYSQL_TYPE_VAR_STRING) @@ -10289,9 +10147,12 @@ Field *make_field(TABLE_SHARE *share, return 0; // Error } - uint pack_length=calc_pack_length((enum_field_types) - f_packtype(pack_flag), - field_length); + // MYSQL_TYPE_VAR_STRING is handled above + DBUG_ASSERT(f_packtype(pack_flag) != MYSQL_TYPE_VAR_STRING); + const Type_handler *tmp; + tmp= Type_handler::get_handler_by_real_type((enum_field_types) + f_packtype(pack_flag)); + uint pack_length= tmp->calc_pack_length(field_length); #ifdef HAVE_SPATIAL if (f_is_geom(pack_flag)) @@ -10323,7 +10184,7 @@ Field *make_field(TABLE_SHARE *share, } } - switch (field_type) { + switch (handler->real_field_type()) { case MYSQL_TYPE_DECIMAL: return new (mem_root) Field_decimal(ptr,field_length,null_pos,null_bit, @@ -10474,13 +10335,14 @@ Field *make_field(TABLE_SHARE *share, Column_definition::Column_definition(THD *thd, Field *old_field, Field *orig_field) { + on_update= NULL; field_name= old_field->field_name; length= old_field->field_length; flags= old_field->flags; unireg_check=old_field->unireg_check; pack_length=old_field->pack_length(); key_length= old_field->key_length(); - sql_type= old_field->real_type(); + set_handler(old_field->type_handler()); charset= old_field->charset(); // May be NULL ptr comment= old_field->comment; decimals= old_field->decimals(); @@ -10488,22 +10350,20 @@ Column_definition::Column_definition(THD *thd, Field *old_field, default_value= orig_field ? orig_field->default_value : 0; check_constraint= orig_field ? orig_field->check_constraint : 0; option_list= old_field->option_list; + pack_flag= 0; - switch (sql_type) { + switch (real_field_type()) { + case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_BLOB: - switch (pack_length - portable_sizeof_char_ptr) { - case 1: sql_type= MYSQL_TYPE_TINY_BLOB; break; - case 2: sql_type= MYSQL_TYPE_BLOB; break; - case 3: sql_type= MYSQL_TYPE_MEDIUM_BLOB; break; - default: sql_type= MYSQL_TYPE_LONG_BLOB; break; - } + case MYSQL_TYPE_MEDIUM_BLOB: + case MYSQL_TYPE_LONG_BLOB: length/= charset->mbmaxlen; key_length/= charset->mbmaxlen; break; case MYSQL_TYPE_STRING: /* Change CHAR -> VARCHAR if dynamic record length */ if (old_field->type() == MYSQL_TYPE_VAR_STRING) - sql_type= MYSQL_TYPE_VARCHAR; + set_handler(&type_handler_varchar); /* fall through */ case MYSQL_TYPE_ENUM: @@ -10547,6 +10407,9 @@ Column_definition::Column_definition(THD *thd, Field *old_field, interval= ((Field_enum*) old_field)->typelib; else interval=0; + + interval_list.empty(); // prepare_interval_field() needs this + char_length= (uint)length; /* @@ -10586,6 +10449,33 @@ Column_definition::Column_definition(THD *thd, Field *old_field, /** + The common part for data type redefinition: + CREATE TABLE t1 (a INT) AS SELECT a FROM t2; + See Type_handler::Column_definition_redefine_stage1() + for data type specific code. +*/ +void +Column_definition::redefine_stage1_common(const Column_definition *dup_field, + const handler *file, + const Schema_specification_st *schema) +{ + set_handler(dup_field->type_handler()); + default_value= dup_field->default_value; + charset= dup_field->charset ? dup_field->charset : + schema->default_table_charset; + length= dup_field->char_length; + pack_length= dup_field->pack_length; + key_length= dup_field->key_length; + decimals= dup_field->decimals; + unireg_check= dup_field->unireg_check; + flags= dup_field->flags; + interval= dup_field->interval; + vcol_info= dup_field->vcol_info; +} + + + +/** maximum possible character length for blob. This method is used in Item_field::set_field to calculate @@ -10610,7 +10500,7 @@ uint32 Field_blob::char_length() const case 3: return 16777215; case 4: - return (uint32) 4294967295U; + return (uint32) UINT_MAX32; default: DBUG_ASSERT(0); // we should never go here return 0; @@ -10663,7 +10553,7 @@ uint32 Field_blob::max_display_length() case 3: return 16777215 * field_charset->mbmaxlen; case 4: - return (uint32) 4294967295U; + return (uint32) UINT_MAX32; default: DBUG_ASSERT(0); // we should never go here return 0; @@ -10707,7 +10597,7 @@ Field::set_warning(Sql_condition::enum_warning_level level, uint code, if (thd->count_cuted_fields) { thd->cuted_fields+= cut_increment; - push_warning_printf(thd, level, code, ER_THD(thd, code), field_name, + push_warning_printf(thd, level, code, ER_THD(thd, code), field_name.str, thd->get_stmt_da()->current_row_for_warning()); return 0; } @@ -10740,7 +10630,7 @@ void Field::set_datetime_warning(Sql_condition::enum_warning_level level, { THD *thd= get_thd(); if (thd->really_abort_on_warning() && level >= Sql_condition::WARN_LEVEL_WARN) - make_truncated_value_warning(thd, level, str, ts_type, field_name); + make_truncated_value_warning(thd, level, str, ts_type, field_name.str); else set_warning(level, code, cuted_increment); } @@ -10753,7 +10643,7 @@ void Field::set_warning_truncated_wrong_value(const char *type_arg, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), - type_arg, value, field_name, + type_arg, value, field_name.str, static_cast<ulong>(thd->get_stmt_da()-> current_row_for_warning())); } @@ -10822,7 +10712,7 @@ bool Field::validate_value_in_record_with_warn(THD *thd, const uchar *record) push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_INVALID_DEFAULT_VALUE_FOR_FIELD, ER_THD(thd, ER_INVALID_DEFAULT_VALUE_FOR_FIELD), - ErrConvString(&tmp).ptr(), field_name); + ErrConvString(&tmp).ptr(), field_name.str); } dbug_tmp_restore_column_map(table->read_set, old_map); return rc; @@ -10857,7 +10747,7 @@ bool Field::save_in_field_default_value(bool view_error_processing) push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD, ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD), - field_name); + field_name.str); } return true; } @@ -10890,3 +10780,22 @@ void Field::register_field_in_read_map() } bitmap_set_bit(table->read_set, field_index); } + + +bool Field::val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to) +{ + StringBuffer<MAX_FIELD_WIDTH> str; + bool rc= false; + THD *thd= get_thd(); + sql_mode_t sql_mode_backup= thd->variables.sql_mode; + thd->variables.sql_mode&= ~MODE_PAD_CHAR_TO_FULL_LENGTH; + + val_str(&str); + if (!(to->length= str.length())) + *to= empty_clex_str; + else if ((rc= !(to->str= strmake_root(mem_root, str.ptr(), str.length())))) + to->length= 0; + + thd->variables.sql_mode= sql_mode_backup; + return rc; +} diff --git a/sql/field.h b/sql/field.h index 91e97c8dce7..17b84e058a8 100644 --- a/sql/field.h +++ b/sql/field.h @@ -31,6 +31,7 @@ #include "my_decimal.h" /* my_decimal */ #include "sql_error.h" /* Sql_condition */ #include "compat56.h" +#include "sql_type.h" /* Type_std_attributes */ class Send_field; class Copy_field; @@ -211,7 +212,8 @@ protected: CHARSET_INFO *cs, const char *str, size_t length, my_decimal *buf) { - m_error= str2my_decimal(mask, str,(uint) length, cs, + DBUG_ASSERT(length < UINT_MAX32); + m_error= str2my_decimal(mask, str, (uint) length, cs, buf, (const char **) &m_end_of_num); // E_DEC_TRUNCATED means a very minor truncation: '1e-100' -> 0 m_edom= m_error && m_error != E_DEC_TRUNCATED; @@ -364,24 +366,25 @@ public: Subst_constraint m_subst_constraint; /* Comparison type. - Impostant only when ANY_SUBSTS. + Important only when ANY_SUBSTS. */ - Item_result m_compare_type; + const Type_handler *m_compare_handler; /* Collation of the comparison operation. Important only when ANY_SUBST. */ CHARSET_INFO *m_compare_collation; public: - Context(Subst_constraint subst, Item_result type, CHARSET_INFO *cs) + Context(Subst_constraint subst, const Type_handler *h, CHARSET_INFO *cs) :m_subst_constraint(subst), - m_compare_type(type), - m_compare_collation(cs) { } + m_compare_handler(h), + m_compare_collation(cs) + { DBUG_ASSERT(h == h->type_handler_for_comparison()); } Subst_constraint subst_constraint() const { return m_subst_constraint; } - Item_result compare_type() const + const Type_handler *compare_type_handler() const { DBUG_ASSERT(m_subst_constraint == ANY_SUBST); - return m_compare_type; + return m_compare_handler; } CHARSET_INFO *compare_collation() const { @@ -393,35 +396,21 @@ public: { // Use this to request only exact value, no invariants. public: Context_identity() - :Context(IDENTITY_SUBST, STRING_RESULT, &my_charset_bin) { } + :Context(IDENTITY_SUBST, &type_handler_long_blob, &my_charset_bin) { } }; class Context_boolean: public Context { // Use this when an item is [a part of] a boolean expression public: - Context_boolean() :Context(ANY_SUBST, INT_RESULT, &my_charset_bin) { } + Context_boolean() + :Context(ANY_SUBST, &type_handler_longlong, &my_charset_bin) { } }; }; -enum Derivation -{ - DERIVATION_IGNORABLE= 6, - DERIVATION_NUMERIC= 5, - DERIVATION_COERCIBLE= 4, - DERIVATION_SYSCONST= 3, - DERIVATION_IMPLICIT= 2, - DERIVATION_NONE= 1, - DERIVATION_EXPLICIT= 0 -}; - - #define STORAGE_TYPE_MASK 7 #define COLUMN_FORMAT_MASK 7 #define COLUMN_FORMAT_SHIFT 3 -#define my_charset_numeric my_charset_latin1 -#define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII - /* The length of the header part for each virtual column in the .frm file */ #define FRM_VCOL_OLD_HEADER_SIZE(b) (3 + MY_TEST(b)) #define FRM_VCOL_NEW_BASE_SIZE 16 @@ -474,16 +463,6 @@ inline bool is_temporal_type_with_date(enum_field_types type) /** - Recognizer for concrete data type (called real_type for some reason), - returning true if it is one of the TIMESTAMP types. -*/ -inline bool is_timestamp_type(enum_field_types type) -{ - return type == MYSQL_TYPE_TIMESTAMP || type == MYSQL_TYPE_TIMESTAMP2; -} - - -/** Convert temporal real types as retuned by field->real_type() to field type as returned by field->type(). @@ -539,27 +518,6 @@ inline bool is_temporal_type(enum_field_types type) } -/** - Tests if field type is temporal and has time part, - i.e. represents TIME, DATETIME or TIMESTAMP types in SQL. - - @param type Field type, as returned by field->type(). - @retval true If field type is temporal type with time part. - @retval false If field type is not temporal type with time part. -*/ -inline bool is_temporal_type_with_time(enum_field_types type) -{ - switch (type) - { - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIMESTAMP: - return true; - default: - return false; - } -} - enum enum_vcol_info_type { VCOL_GENERATED_VIRTUAL, VCOL_GENERATED_STORED, @@ -629,7 +587,7 @@ public: bool stored_in_db; bool utf8; /* Already in utf8 */ Item *expr; - LEX_STRING name; /* Name of constraint */ + LEX_CSTRING name; /* Name of constraint */ uint flags; Virtual_column_info() @@ -705,7 +663,10 @@ public: static void *operator new(size_t size, MEM_ROOT *mem_root) throw () { return alloc_root(mem_root, size); } static void *operator new(size_t size) throw () - { return thd_alloc(current_thd, size); } + { + DBUG_ASSERT(size < UINT_MAX32); + return thd_alloc(current_thd, (uint) size); + } static void operator delete(void *ptr_arg, size_t size) { TRASH(ptr_arg, size); } static void operator delete(void *ptr, MEM_ROOT *mem_root) { DBUG_ASSERT(0); } @@ -722,12 +683,12 @@ public: */ TABLE *table; // Pointer for table TABLE *orig_table; // Pointer to original table - const char * const *table_name; - const char *field_name; + const char * const *table_name; // Pointer to alias in TABLE + LEX_CSTRING field_name; + LEX_CSTRING comment; /** reference to the list of options or NULL */ engine_option_value *option_list; ha_field_option_struct *option_struct; /* structure with parsed options */ - LEX_STRING comment; /* Field is part of the following keys */ key_map key_start, part_of_key, part_of_key_not_clustered; @@ -816,8 +777,20 @@ public: Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg); + const LEX_CSTRING *field_name_arg); virtual ~Field() {} + + DTCollation dtcollation() const + { + return DTCollation(charset(), derivation(), repertoire()); + } + Type_std_attributes type_std_attributes() const + { + return Type_std_attributes(field_length, decimals(), + MY_TEST(flags & UNSIGNED_FLAG), + dtcollation()); + } + /** Convenience definition of a copy function returned by Field::get_copy_func() @@ -848,7 +821,20 @@ public: int store(const char *to, uint length, CHARSET_INFO *cs, enum_check_fields check_level); int store(const LEX_STRING *ls, CHARSET_INFO *cs) - { return store(ls->str, (uint32) ls->length, cs); } + { + DBUG_ASSERT(ls->length < UINT_MAX32); + return store(ls->str, (uint) ls->length, cs); + } + int store(const LEX_CSTRING *ls, CHARSET_INFO *cs) + { + DBUG_ASSERT(ls->length < UINT_MAX32); + return store(ls->str, (uint) ls->length, cs); + } + int store(const LEX_CSTRING &ls, CHARSET_INFO *cs) + { + DBUG_ASSERT(ls.length < UINT_MAX32); + return store(ls.str, (uint) ls.length, cs); + } virtual double val_real(void)=0; virtual longlong val_int(void)=0; virtual bool val_bool(void)= 0; @@ -868,17 +854,36 @@ public: */ virtual String *val_str(String*,String *)=0; String *val_int_as_str(String *val_buffer, bool unsigned_flag); + /* + Return the field value as a LEX_CSTRING, without padding to full length + (MODE_PAD_CHAR_TO_FULL_LENGTH is temporarily suppressed during the call). + + In case of an empty value, to[0] is assigned to empty_clex_string, + memory is not allocated. + In case of a non-empty value, the memory is allocated on mem_root. + In case of a memory allocation failure, to[0] is assigned to {NULL,0}. + + @param [IN] mem_root store non-empty values here + @param [OUT to return the string here + @retval false (success) + @retval true (EOM) + */ + bool val_str_nopad(MEM_ROOT *mem_root, LEX_CSTRING *to); fast_field_copier get_fast_field_copier(const Field *from); /* str_needs_quotes() returns TRUE if the value returned by val_str() needs to be quoted when used in constructing an SQL query. */ virtual bool str_needs_quotes() { return FALSE; } - virtual Item_result result_type () const=0; - virtual Item_result cmp_type () const { return result_type(); } - static bool type_can_have_key_part(enum_field_types); + Item_result result_type () const + { + return type_handler()->result_type(); + } + Item_result cmp_type () const + { + return type_handler()->cmp_type(); + } static enum_field_types field_type_merge(enum_field_types, enum_field_types); - static Item_result result_merge_type(enum_field_types); virtual bool eq(Field *field) { return (ptr == field->ptr && null_ptr == field->null_ptr && @@ -991,8 +996,15 @@ public: virtual bool zero_pack() const { return 1; } virtual enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } virtual uint32 key_length() const { return pack_length(); } - virtual enum_field_types type() const =0; - virtual enum_field_types real_type() const { return type(); } + virtual const Type_handler *type_handler() const= 0; + virtual enum_field_types type() const + { + return type_handler()->field_type(); + } + virtual enum_field_types real_type() const + { + return type_handler()->real_field_type(); + } virtual enum_field_types binlog_type() const { /* @@ -1309,6 +1321,7 @@ public: uint fill_cache_field(struct st_cache_field *copy); virtual bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); bool get_time(MYSQL_TIME *ltime) { return get_date(ltime, TIME_TIME_ONLY); } + virtual TYPELIB *get_typelib() const { return NULL; } virtual CHARSET_INFO *charset(void) const { return &my_charset_bin; } virtual CHARSET_INFO *charset_for_protocol(void) const { return binary() ? &my_charset_bin : charset(); } @@ -1317,9 +1330,6 @@ public: virtual enum Derivation derivation(void) const { return DERIVATION_IMPLICIT; } virtual uint repertoire(void) const { return MY_REPERTOIRE_UNICODE30; } - virtual void set_derivation(enum Derivation derivation_arg, - uint repertoire_arg) - { } virtual int set_time() { return 1; } bool set_warning(Sql_condition::enum_warning_level, unsigned int code, int cuted_increment) const; @@ -1332,7 +1342,7 @@ protected: { return set_warning(Sql_condition::WARN_LEVEL_NOTE, code, cuted_increment); } - void set_datetime_warning(Sql_condition::enum_warning_level, uint code, + void set_datetime_warning(Sql_condition::enum_warning_level, uint code, const ErrConv *str, timestamp_type ts_type, int cuted_increment) const; void set_datetime_warning(uint code, @@ -1548,7 +1558,7 @@ protected: return to + size; } - const uchar *unpack_int(uchar* to, const uchar *from, + const uchar *unpack_int(uchar* to, const uchar *from, const uchar *from_end, size_t size) { if (from + size > from_end) @@ -1607,9 +1617,8 @@ public: bool zerofill,unsigned_flag; // Purify cannot handle bit fields Field_num(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg, bool zero_arg, bool unsigned_arg); - enum Item_result result_type () const { return INT_RESULT; } enum Derivation derivation(void) const { return DERIVATION_NUMERIC; } uint repertoire(void) const { return MY_REPERTOIRE_NUMERIC; } CHARSET_INFO *charset(void) const { return &my_charset_numeric; } @@ -1671,8 +1680,8 @@ public: const Item_equal *item_equal); Field_str(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, CHARSET_INFO *charset); - Item_result result_type () const { return STRING_RESULT; } + const LEX_CSTRING *field_name_arg, + const DTCollation &collation); uint decimals() const { return NOT_FIXED_DEC; } int save_in_field(Field *to) { return save_in_field_str(to); } bool memcpy_field_possible(const Field *from) const @@ -1692,12 +1701,6 @@ public: uint repertoire(void) const { return field_repertoire; } CHARSET_INFO *charset(void) const { return field_charset; } enum Derivation derivation(void) const { return field_derivation; } - void set_derivation(enum Derivation derivation_arg, - uint repertoire_arg) - { - field_derivation= derivation_arg; - field_repertoire= repertoire_arg; - } bool binary() const { return field_charset == &my_charset_bin; } uint32 max_display_length() { return field_length; } friend class Create_field; @@ -1739,9 +1742,10 @@ protected: public: Field_longstr(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, CHARSET_INFO *charset_arg) + const LEX_CSTRING *field_name_arg, + const DTCollation &collation) :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, - field_name_arg, charset_arg) + field_name_arg, collation) {} int store_decimal(const my_decimal *d); @@ -1774,13 +1778,12 @@ public: Field_real(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg, bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg), not_fixed(dec_arg >= FLOATING_POINT_DECIMALS) {} - Item_result result_type () const { return REAL_RESULT; } Copy_func *get_copy_func(const Field *from) const { return do_field_real; @@ -1811,13 +1814,13 @@ class Field_decimal :public Field_real { public: Field_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg, zero_arg, unsigned_arg) {} - enum_field_types type() const { return MYSQL_TYPE_DECIMAL;} + const Type_handler *type_handler() const { return &type_handler_olddecimal; } enum ha_base_keytype key_type() const { return zerofill ? HA_KEYTYPE_BINARY : HA_KEYTYPE_NUM; } Copy_func *get_copy_func(const Field *from) const @@ -1859,14 +1862,11 @@ public: */ Field_new_decimal(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg, bool zero_arg, bool unsigned_arg); - Field_new_decimal(uint32 len_arg, bool maybe_null_arg, - const char *field_name_arg, uint8 dec_arg, - bool unsigned_arg); - enum_field_types type() const { return MYSQL_TYPE_NEWDECIMAL;} + const Type_handler *type_handler() const { return &type_handler_newdecimal; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } - Item_result result_type () const { return DECIMAL_RESULT; } Copy_func *get_copy_func(const Field *from) const { // if (from->real_type() == MYSQL_TYPE_BIT) // QQ: why? @@ -1916,7 +1916,6 @@ public: uint16 mflags, int *order_var); uint is_equal(Create_field *new_field); virtual const uchar *unpack(uchar* to, const uchar *from, const uchar *from_end, uint param_data); - static Field *create_from_item(MEM_ROOT *root, Item *); Item *get_equal_const_item(THD *thd, const Context &ctx, Item *const_item); }; @@ -1925,13 +1924,13 @@ class Field_tiny :public Field_num { public: Field_tiny(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 0, zero_arg,unsigned_arg) {} - enum_field_types type() const { return MYSQL_TYPE_TINY;} + const Type_handler *type_handler() const { return &type_handler_tiny; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_BINARY : HA_KEYTYPE_INT8; } int store(const char *to,uint length,CHARSET_INFO *charset); @@ -1969,18 +1968,19 @@ class Field_short :public Field_num { public: Field_short(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 0, zero_arg,unsigned_arg) {} - Field_short(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + Field_short(uint32 len_arg,bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, bool unsigned_arg) :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, NONE, field_name_arg, 0, 0, unsigned_arg) {} - enum_field_types type() const { return MYSQL_TYPE_SHORT;} + const Type_handler *type_handler() const { return &type_handler_short; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_USHORT_INT : HA_KEYTYPE_SHORT_INT;} int store(const char *to,uint length,CHARSET_INFO *charset); @@ -2009,13 +2009,13 @@ class Field_medium :public Field_num { public: Field_medium(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 0, zero_arg,unsigned_arg) {} - enum_field_types type() const { return MYSQL_TYPE_INT24;} + const Type_handler *type_handler() const { return &type_handler_int24; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_UINT24 : HA_KEYTYPE_INT24; } int store(const char *to,uint length,CHARSET_INFO *charset); @@ -2043,18 +2043,19 @@ class Field_long :public Field_num { public: Field_long(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 0, zero_arg,unsigned_arg) {} - Field_long(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, + Field_long(uint32 len_arg,bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, bool unsigned_arg) :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, NONE, field_name_arg,0,0,unsigned_arg) {} - enum_field_types type() const { return MYSQL_TYPE_LONG;} + const Type_handler *type_handler() const { return &type_handler_long; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_ULONG_INT : HA_KEYTYPE_LONG_INT; } int store(const char *to,uint length,CHARSET_INFO *charset); @@ -2088,19 +2089,19 @@ class Field_longlong :public Field_num { public: Field_longlong(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, bool zero_arg, bool unsigned_arg) :Field_num(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 0, zero_arg,unsigned_arg) {} Field_longlong(uint32 len_arg,bool maybe_null_arg, - const char *field_name_arg, - bool unsigned_arg) + const LEX_CSTRING *field_name_arg, + bool unsigned_arg) :Field_num((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0,0, NONE, field_name_arg,0,0,unsigned_arg) {} - enum_field_types type() const { return MYSQL_TYPE_LONGLONG;} + const Type_handler *type_handler() const { return &type_handler_longlong; } enum ha_base_keytype key_type() const { return unsigned_flag ? HA_KEYTYPE_ULONGLONG : HA_KEYTYPE_LONGLONG; } int store(const char *to,uint length,CHARSET_INFO *charset); @@ -2137,7 +2138,7 @@ class Field_float :public Field_real { public: Field_float(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, @@ -2146,15 +2147,15 @@ public: if (dec_arg >= FLOATING_POINT_DECIMALS) dec_arg= NOT_FIXED_DEC; } - Field_float(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, - uint8 dec_arg) + Field_float(uint32 len_arg, bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg) :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, (uint) 0, NONE, field_name_arg, dec_arg, 0, 0) { if (dec_arg >= FLOATING_POINT_DECIMALS) dec_arg= NOT_FIXED_DEC; } - enum_field_types type() const { return MYSQL_TYPE_FLOAT;} + const Type_handler *type_handler() const { return &type_handler_float; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_FLOAT; } int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); @@ -2178,7 +2179,7 @@ class Field_double :public Field_real { public: Field_double(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, uint8 dec_arg,bool zero_arg,bool unsigned_arg) :Field_real(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, @@ -2187,15 +2188,16 @@ public: if (dec_arg >= FLOATING_POINT_DECIMALS) dec_arg= NOT_FIXED_DEC; } - Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, - uint8 dec_arg) + Field_double(uint32 len_arg, bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg) :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, NONE, field_name_arg, dec_arg, 0, 0) { if (dec_arg >= FLOATING_POINT_DECIMALS) dec_arg= NOT_FIXED_DEC; } - Field_double(uint32 len_arg, bool maybe_null_arg, const char *field_name_arg, + Field_double(uint32 len_arg, bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, uint8 dec_arg, bool not_fixed_arg) :Field_real((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "" : 0, (uint) 0, NONE, field_name_arg, dec_arg, 0, 0) @@ -2204,7 +2206,7 @@ public: if (dec_arg >= FLOATING_POINT_DECIMALS) dec_arg= NOT_FIXED_DEC; } - enum_field_types type() const { return MYSQL_TYPE_DOUBLE;} + const Type_handler *type_handler() const { return &type_handler_double; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_DOUBLE; } int store(const char *to,uint length,CHARSET_INFO *charset); int store(double nr); @@ -2236,12 +2238,12 @@ class Field_null :public Field_str { static uchar null[1]; public: Field_null(uchar *ptr_arg, uint32 len_arg, - enum utype unireg_check_arg, const char *field_name_arg, - CHARSET_INFO *cs) + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, + const DTCollation &collation) :Field_str(ptr_arg, len_arg, null, 1, - unireg_check_arg, field_name_arg, cs) + unireg_check_arg, field_name_arg, collation) {} - enum_field_types type() const { return MYSQL_TYPE_NULL;} + const Type_handler *type_handler() const { return &type_handler_null; } Copy_func *get_copy_func(const Field *from) const { return do_field_string; @@ -2287,11 +2289,10 @@ protected: public: Field_temporal(uchar *ptr_arg,uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg) + const LEX_CSTRING *field_name_arg) :Field(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) { flags|= BINARY_FLAG; } - Item_result result_type () const { return STRING_RESULT; } int store_hex_hybrid(const char *str, uint length) { return store(str, length, &my_charset_bin); @@ -2312,7 +2313,6 @@ public: CHARSET_INFO *charset(void) const { return &my_charset_numeric; } CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; } bool binary() const { return true; } - enum Item_result cmp_type () const { return TIME_RESULT; } bool val_bool() { return val_real() != 0e0; } uint is_equal(Create_field *new_field); bool eq_def(const Field *field) const @@ -2366,7 +2366,7 @@ public: Field_temporal_with_date(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, - const char *field_name_arg) + const LEX_CSTRING *field_name_arg) :Field_temporal(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) {} @@ -2386,9 +2386,10 @@ protected: public: Field_timestamp(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share); - enum_field_types type() const { return MYSQL_TYPE_TIMESTAMP;} + const Type_handler *type_handler() const { return &type_handler_timestamp; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } Copy_func *get_copy_func(const Field *from) const; int store(const char *to,uint length,CHARSET_INFO *charset); @@ -2457,7 +2458,7 @@ public: Field_timestamp_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share, uint dec_arg) : Field_timestamp(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), null_ptr_arg, @@ -2487,11 +2488,15 @@ public: class Field_timestamp_hires :public Field_timestamp_with_dec { + uint sec_part_bytes(uint dec) const + { + return Type_handler_timestamp::sec_part_bytes(dec); + } public: Field_timestamp_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share, uint dec_arg) : Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, share, dec_arg) @@ -2501,7 +2506,7 @@ public: my_time_t get_timestamp(const uchar *pos, ulong *sec_part) const; void store_TIME(my_time_t timestamp, ulong sec_part); int cmp(const uchar *,const uchar *); - uint32 pack_length() const; + uint32 pack_length() const { return 4 + sec_part_bytes(dec); } uint size_of() const { return sizeof(*this); } }; @@ -2512,19 +2517,19 @@ public: class Field_timestampf :public Field_timestamp_with_dec { int do_save_field_metadata(uchar *metadata_ptr) { - *metadata_ptr= decimals(); + *metadata_ptr= (uchar) decimals(); return 1; } public: Field_timestampf(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, TABLE_SHARE *share, uint dec_arg) : Field_timestamp_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, share, dec_arg) {} - enum_field_types real_type() const { return MYSQL_TYPE_TIMESTAMP2; } + const Type_handler *type_handler() const { return &type_handler_timestamp2; } enum_field_types binlog_type() const { return MYSQL_TYPE_TIMESTAMP2; } uint32 pack_length() const { @@ -2551,18 +2556,23 @@ class Field_year :public Field_tiny { public: Field_year(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg) + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg) :Field_tiny(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, 1, 1) {} - enum_field_types type() const { return MYSQL_TYPE_YEAR;} + const Type_handler *type_handler() const { return &type_handler_year; } Copy_func *get_copy_func(const Field *from) const { if (eq_def(from)) return get_identical_copy_func(); switch (from->cmp_type()) { case STRING_RESULT: + { + const Type_handler *handler= from->type_handler(); + if (handler == &type_handler_enum || handler == &type_handler_set) + return do_field_int; return do_field_string; + } case TIME_RESULT: return do_field_temporal; case DECIMAL_RESULT: @@ -2597,10 +2607,10 @@ class Field_date :public Field_temporal_with_date { bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; public: Field_date(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg) + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg) :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) {} - enum_field_types type() const { return MYSQL_TYPE_DATE;} + const Type_handler *type_handler() const { return &type_handler_date; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONG_INT; } int reset(void) { ptr[0]=ptr[1]=ptr[2]=ptr[3]=0; return 0; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) @@ -2632,12 +2642,11 @@ class Field_newdate :public Field_temporal_with_date { bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; public: Field_newdate(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg) + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg) :Field_temporal_with_date(ptr_arg, MAX_DATE_WIDTH, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) {} - enum_field_types type() const { return MYSQL_TYPE_DATE;} - enum_field_types real_type() const { return MYSQL_TYPE_NEWDATE; } + const Type_handler *type_handler() const { return &type_handler_newdate; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_UINT24; } int reset(void) { ptr[0]=ptr[1]=ptr[2]=0; return 0; } double val_real(void); @@ -2671,11 +2680,13 @@ protected: public: Field_time(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg) + const LEX_CSTRING *field_name_arg) :Field_temporal(ptr_arg, length_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), curdays(0) {} - enum_field_types type() const { return MYSQL_TYPE_TIME;} + bool can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal); + const Type_handler *type_handler() const { return &type_handler_time; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_INT24; } Copy_func *get_copy_func(const Field *from) const { @@ -2723,7 +2734,8 @@ protected: uint dec; public: Field_time_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, + const LEX_CSTRING *field_name_arg, uint dec_arg) :Field_time(ptr_arg, MIN_TIME_WIDTH + dec_arg + MY_TEST(dec_arg), null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), @@ -2747,7 +2759,7 @@ class Field_time_hires :public Field_time_with_dec { void store_TIME(MYSQL_TIME *ltime); public: Field_time_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, uint dec_arg) :Field_time_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, @@ -2761,7 +2773,7 @@ public: bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); int cmp(const uchar *,const uchar *); void sort_string(uchar *buff,uint length); - uint32 pack_length() const; + uint32 pack_length() const { return Type_handler_time::hires_bytes(dec); } uint size_of() const { return sizeof(*this); } }; @@ -2773,12 +2785,12 @@ class Field_timef :public Field_time_with_dec { void store_TIME(MYSQL_TIME *ltime); int do_save_field_metadata(uchar *metadata_ptr) { - *metadata_ptr= decimals(); + *metadata_ptr= (uchar) decimals(); return 1; } public: Field_timef(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, uint dec_arg) :Field_time_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, @@ -2786,7 +2798,7 @@ public: { DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); } - enum_field_types real_type() const { return MYSQL_TYPE_TIME2; } + const Type_handler *type_handler() const { return &type_handler_time2; } enum_field_types binlog_type() const { return MYSQL_TYPE_TIME2; } uint32 pack_length() const { @@ -2820,7 +2832,7 @@ class Field_datetime :public Field_temporal_with_date { public: Field_datetime(uchar *ptr_arg, uint length_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg) + const LEX_CSTRING *field_name_arg) :Field_temporal_with_date(ptr_arg, length_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg) { @@ -2828,7 +2840,7 @@ public: unireg_check == TIMESTAMP_DNUN_FIELD) flags|= ON_UPDATE_NOW_FLAG; } - enum_field_types type() const { return MYSQL_TYPE_DATETIME;} + const Type_handler *type_handler() const { return &type_handler_datetime; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_ULONGLONG; } double val_real(void); longlong val_int(void); @@ -2877,7 +2889,7 @@ protected: public: Field_datetime_with_dec(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, uint dec_arg) + const LEX_CSTRING *field_name_arg, uint dec_arg) :Field_datetime(ptr_arg, MAX_DATETIME_WIDTH + dec_arg + MY_TEST(dec_arg), null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg), dec(dec_arg) @@ -2913,14 +2925,14 @@ class Field_datetime_hires :public Field_datetime_with_dec { public: Field_datetime_hires(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, uint dec_arg) + const LEX_CSTRING *field_name_arg, uint dec_arg) :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg) { DBUG_ASSERT(dec); } int cmp(const uchar *,const uchar *); - uint32 pack_length() const; + uint32 pack_length() const { return Type_handler_datetime::hires_bytes(dec); } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) { return Field_datetime_hires::get_TIME(ltime, ptr, fuzzydate); } uint size_of() const { return sizeof(*this); } @@ -2935,17 +2947,17 @@ class Field_datetimef :public Field_datetime_with_dec { bool get_TIME(MYSQL_TIME *ltime, const uchar *pos, ulonglong fuzzydate) const; int do_save_field_metadata(uchar *metadata_ptr) { - *metadata_ptr= decimals(); + *metadata_ptr= (uchar) decimals(); return 1; } public: Field_datetimef(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, enum utype unireg_check_arg, - const char *field_name_arg, uint dec_arg) + const LEX_CSTRING *field_name_arg, uint dec_arg) :Field_datetime_with_dec(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, dec_arg) {} - enum_field_types real_type() const { return MYSQL_TYPE_DATETIME2; } + const Type_handler *type_handler() const { return &type_handler_datetime2; } enum_field_types binlog_type() const { return MYSQL_TYPE_DATETIME2; } uint32 pack_length() const { @@ -2971,7 +2983,8 @@ public: static inline Field_timestamp * new_Field_timestamp(MEM_ROOT *root,uchar *ptr, uchar *null_ptr, uchar null_bit, - enum Field::utype unireg_check, const char *field_name, + enum Field::utype unireg_check, + const LEX_CSTRING *field_name, TABLE_SHARE *share, uint dec) { if (dec==0) @@ -2987,7 +3000,7 @@ new_Field_timestamp(MEM_ROOT *root,uchar *ptr, uchar *null_ptr, uchar null_bit, static inline Field_time * new_Field_time(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit, - enum Field::utype unireg_check, const char *field_name, + enum Field::utype unireg_check, const LEX_CSTRING *field_name, uint dec) { if (dec == 0) @@ -3003,7 +3016,7 @@ new_Field_time(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit, static inline Field_datetime * new_Field_datetime(MEM_ROOT *root, uchar *ptr, uchar *null_ptr, uchar null_bit, enum Field::utype unireg_check, - const char *field_name, uint dec) + const LEX_CSTRING *field_name, uint dec) { if (dec == 0) return new (root) @@ -3022,28 +3035,35 @@ class Field_string :public Field_longstr { public: Warn_filter_string(const THD *thd, const Field_string *field); }; + bool is_var_string() const + { + return can_alter_field_type && + orig_table && + (orig_table->s->db_create_options & HA_OPTION_PACK_RECORD) && + field_length >= 4 && + orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR; + } public: bool can_alter_field_type; Field_string(uchar *ptr_arg, uint32 len_arg,uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - CHARSET_INFO *cs) + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, + const DTCollation &collation) :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, cs), + unireg_check_arg, field_name_arg, collation), can_alter_field_type(1) {}; - Field_string(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - CHARSET_INFO *cs) + Field_string(uint32 len_arg,bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, + const DTCollation &collation) :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0, - NONE, field_name_arg, cs), + NONE, field_name_arg, collation), can_alter_field_type(1) {}; - enum_field_types type() const + const Type_handler *type_handler() const { - return ((can_alter_field_type && orig_table && - orig_table->s->db_create_options & HA_OPTION_PACK_RECORD && - field_length >= 4) && - orig_table->s->frm_version < FRM_VER_TRUE_VARCHAR ? - MYSQL_TYPE_VAR_STRING : MYSQL_TYPE_STRING); + if (is_var_string()) + return &type_handler_var_string; + return &type_handler_string; } enum ha_base_keytype key_type() const { return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; } @@ -3085,7 +3105,6 @@ public: uint packed_col_length(const uchar *to, uint length); uint max_packed_col_length(uint max_length); uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return MYSQL_TYPE_STRING; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); @@ -3115,25 +3134,25 @@ public: Field_varstring(uchar *ptr_arg, uint32 len_arg, uint length_bytes_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - TABLE_SHARE *share, CHARSET_INFO *cs) + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, + TABLE_SHARE *share, const DTCollation &collation) :Field_longstr(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, cs), + unireg_check_arg, field_name_arg, collation), length_bytes(length_bytes_arg) { share->varchar_fields++; } Field_varstring(uint32 len_arg,bool maybe_null_arg, - const char *field_name_arg, - TABLE_SHARE *share, CHARSET_INFO *cs) + const LEX_CSTRING *field_name_arg, + TABLE_SHARE *share, const DTCollation &collation) :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0, - NONE, field_name_arg, cs), + NONE, field_name_arg, collation), length_bytes(len_arg < 256 ? 1 :2) { share->varchar_fields++; } - enum_field_types type() const { return MYSQL_TYPE_VARCHAR; } + const Type_handler *type_handler() const { return &type_handler_varchar; } enum ha_base_keytype key_type() const; uint row_pack_length() const { return field_length; } bool zero_pack() const { return 0; } @@ -3177,7 +3196,6 @@ public: uint max_packed_col_length(uint max_length); uint32 data_length(); uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return MYSQL_TYPE_VARCHAR; } bool has_charset(void) const { return charset() == &my_charset_bin ? FALSE : TRUE; } Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); @@ -3192,6 +3210,8 @@ private: }; +extern LEX_CSTRING temp_lex_str; + class Field_blob :public Field_longstr { protected: /** @@ -3214,20 +3234,22 @@ protected: static void do_conv_blob(Copy_field *copy); public: Field_blob(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, - TABLE_SHARE *share, uint blob_pack_length, CHARSET_INFO *cs); - Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - CHARSET_INFO *cs) + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, + TABLE_SHARE *share, uint blob_pack_length, + const DTCollation &collation); + Field_blob(uint32 len_arg,bool maybe_null_arg, const LEX_CSTRING *field_name_arg, + const DTCollation &collation) :Field_longstr((uchar*) 0, len_arg, maybe_null_arg ? (uchar*) "": 0, 0, - NONE, field_name_arg, cs), + NONE, field_name_arg, collation), packlength(4) { flags|= BLOB_FLAG; } - Field_blob(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - CHARSET_INFO *cs, bool set_packlength) + Field_blob(uint32 len_arg,bool maybe_null_arg, + const LEX_CSTRING *field_name_arg, + const DTCollation &collation, bool set_packlength) :Field_longstr((uchar*) 0,len_arg, maybe_null_arg ? (uchar*) "": 0, 0, - NONE, field_name_arg, cs) + NONE, field_name_arg, collation) { flags|= BLOB_FLAG; packlength= 4; @@ -3239,10 +3261,27 @@ public: } } Field_blob(uint32 packlength_arg) - :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, "temp", system_charset_info), + :Field_longstr((uchar*) 0, 0, (uchar*) "", 0, NONE, &temp_lex_str, + system_charset_info), packlength(packlength_arg) {} + const Type_handler *type_handler() const; /* Note that the default copy constructor is used, in clone() */ - enum_field_types type() const { return MYSQL_TYPE_BLOB;} + enum_field_types type() const + { + /* + We cannot return type_handler()->field_type() here. + Some pieces of the code (e.g. in engines) rely on the fact + that Field::type(), Field::real_type() and Item_field::field_type() + return MYSQL_TYPE_BLOB for all blob variants. + We should eventually fix all such code pieces to expect + all BLOB type codes. + */ + return MYSQL_TYPE_BLOB; + } + enum_field_types real_type() const + { + return MYSQL_TYPE_BLOB; + } enum ha_base_keytype key_type() const { return binary() ? HA_KEYTYPE_VARBINARY2 : HA_KEYTYPE_VARTEXT2; } Copy_func *get_copy_func(const Field *from) const @@ -3420,19 +3459,26 @@ public: enum storage_type { GEOM_STORAGE_WKB= 0, GEOM_STORAGE_BINARY= 1}; enum storage_type storage; - Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uint null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + Field_geom(uchar *ptr_arg, uchar *null_ptr_arg, uchar null_bit_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, TABLE_SHARE *share, uint blob_pack_length, enum geometry_type geom_type_arg, uint field_srid) - :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, + :Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, share, blob_pack_length, &my_charset_bin) { geom_type= geom_type_arg; srid= field_srid; } - Field_geom(uint32 len_arg,bool maybe_null_arg, const char *field_name_arg, - TABLE_SHARE *share, enum geometry_type geom_type_arg) - :Field_blob(len_arg, maybe_null_arg, field_name_arg, &my_charset_bin) - { geom_type= geom_type_arg; srid= 0; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_VARBINARY2; } - enum_field_types type() const { return MYSQL_TYPE_GEOMETRY; } + const Type_handler *type_handler() const + { + return &type_handler_geometry; + } + enum_field_types type() const + { + return MYSQL_TYPE_GEOMETRY; + } + enum_field_types real_type() const + { + return MYSQL_TYPE_GEOMETRY; + } bool can_optimize_range(const Item_bool_func *cond, const Item *item, bool is_eq_func) const; @@ -3478,19 +3524,18 @@ public: TYPELIB *typelib; Field_enum(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, uint packlength_arg, TYPELIB *typelib_arg, - CHARSET_INFO *charset_arg) + const DTCollation &collation) :Field_str(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, - unireg_check_arg, field_name_arg, charset_arg), + unireg_check_arg, field_name_arg, collation), packlength(packlength_arg),typelib(typelib_arg) { flags|=ENUM_FLAG; } Field *make_new_field(MEM_ROOT *root, TABLE *new_table, bool keep_type); - enum_field_types type() const { return MYSQL_TYPE_STRING; } - enum Item_result cmp_type () const { return INT_RESULT; } + const Type_handler *type_handler() const { return &type_handler_enum; } enum ha_base_keytype key_type() const; Copy_func *get_copy_func(const Field *from) const { @@ -3531,7 +3576,6 @@ public: void store_type(ulonglong value); void sql_type(String &str) const; uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return MYSQL_TYPE_ENUM; } uint pack_length_from_metadata(uint field_metadata) { return (field_metadata & 0x00ff); } uint row_pack_length() const { return pack_length(); } @@ -3542,6 +3586,7 @@ public: /* enum and set are sorted as integers */ CHARSET_INFO *sort_charset(void) const { return &my_charset_bin; } uint decimals() const { return 0; } + TYPELIB *get_typelib() const { return typelib; } virtual uchar *pack(uchar *to, const uchar *from, uint max_length); virtual const uchar *unpack(uchar *to, const uchar *from, @@ -3561,6 +3606,9 @@ public: */ return false; } + bool can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const; private: int do_save_field_metadata(uchar *first_byte); uint is_equal(Create_field *new_field); @@ -3571,14 +3619,14 @@ class Field_set :public Field_enum { public: Field_set(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg, + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg, uint32 packlength_arg, - TYPELIB *typelib_arg, CHARSET_INFO *charset_arg) + TYPELIB *typelib_arg, const DTCollation &collation) :Field_enum(ptr_arg, len_arg, null_ptr_arg, null_bit_arg, unireg_check_arg, field_name_arg, packlength_arg, - typelib_arg,charset_arg), - empty_set_string("", 0, charset_arg) + typelib_arg, collation), + empty_set_string("", 0, collation.collation) { flags=(flags & ~ENUM_FLAG) | SET_FLAG; } @@ -3591,7 +3639,7 @@ public: String *val_str(String*,String *); void sql_type(String &str) const; uint size_of() const { return sizeof(*this); } - enum_field_types real_type() const { return MYSQL_TYPE_SET; } + const Type_handler *type_handler() const { return &type_handler_set; } bool has_charset(void) const { return TRUE; } private: const String empty_set_string; @@ -3620,14 +3668,13 @@ public: uint bytes_in_rec; Field_bit(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, uchar *bit_ptr_arg, uchar bit_ofs_arg, - enum utype unireg_check_arg, const char *field_name_arg); - enum_field_types type() const { return MYSQL_TYPE_BIT; } + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg); + const Type_handler *type_handler() const { return &type_handler_bit; } enum ha_base_keytype key_type() const { return HA_KEYTYPE_BIT; } uint32 key_length() const { return (uint32) (field_length + 7) / 8; } uint32 max_data_length() const { return (field_length + 7) / 8; } uint32 max_display_length() { return field_length; } uint size_of() const { return sizeof(*this); } - Item_result result_type () const { return INT_RESULT; } int reset(void) { bzero(ptr, bytes_in_rec); if (bit_ptr && (bit_len > 0)) // reset odd bits among null bits @@ -3756,7 +3803,7 @@ class Field_bit_as_char: public Field_bit { public: Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, uchar *null_ptr_arg, uchar null_bit_arg, - enum utype unireg_check_arg, const char *field_name_arg); + enum utype unireg_check_arg, const LEX_CSTRING *field_name_arg); enum ha_base_keytype key_type() const { return HA_KEYTYPE_BINARY; } uint size_of() const { return sizeof(*this); } int store(const char *to, uint length, CHARSET_INFO *charset); @@ -3767,28 +3814,69 @@ public: }; -extern const LEX_STRING null_lex_str; - +extern const LEX_CSTRING null_clex_str; Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, uchar *ptr, uint32 field_length, uchar *null_pos, uchar null_bit, - uint pack_flag, enum_field_types field_type, + uint pack_flag, const Type_handler *handler, CHARSET_INFO *cs, Field::geometry_type geom_type, uint srid, Field::utype unireg_check, - TYPELIB *interval, const char *field_name); + TYPELIB *interval, const LEX_CSTRING *field_name); /* Create field class for CREATE TABLE */ -class Column_definition: public Sql_alloc +class Column_definition: public Sql_alloc, + public Type_handler_hybrid_field_type { + /** + Create "interval" from "interval_list". + @param mem_root - memory root to create the TYPELIB + instance and its values on + @param reuse_interval_list_values - determines if TYPELIB can reuse strings + from interval_list, or should always + allocate a copy on mem_root, even if + character set conversion is not needed + @retval false on success + @retval true on error (bad values, or EOM) + */ + bool create_interval_from_interval_list(MEM_ROOT *mem_root, + bool reuse_interval_list_values); + + /* + Calculate TYPELIB (set or enum) max and total lengths + + @param cs charset+collation pair of the interval + @param max_length length of the longest item + @param tot_length sum of the item lengths + + After this method call: + - ENUM uses max_length + - SET uses tot_length. + */ + void calculate_interval_lengths(uint32 *max_length, uint32 *tot_length) + { + const char **pos; + uint *len; + *max_length= *tot_length= 0; + for (pos= interval->type_names, len= interval->type_lengths; + *pos ; pos++, len++) + { + size_t length= charset->cset->numchars(charset, *pos, *pos + *len); + DBUG_ASSERT(length < UINT_MAX32); + *tot_length+= (uint) length; + set_if_bigger(*max_length, (uint32)length); + } + } + bool prepare_stage1_check_typelib_default(); + bool prepare_stage1_convert_default(THD *, MEM_ROOT *, CHARSET_INFO *to); + const Type_handler *field_type() const; // Prevent using this public: - const char *field_name; - LEX_STRING comment; // Comment for field + LEX_CSTRING field_name; + LEX_CSTRING comment; // Comment for field Item *on_update; // ON UPDATE NOW() - enum enum_field_types sql_type; /* At various stages in execution this can be length of field in bytes or max number of characters. @@ -3820,19 +3908,112 @@ public: *default_value, // Default value *check_constraint; // Check constraint - Column_definition(): - comment(null_lex_str), - on_update(0), sql_type(MYSQL_TYPE_NULL), + Column_definition() + :Type_handler_hybrid_field_type(&type_handler_null), + comment(null_clex_str), + on_update(NULL), length(0), decimals(0), flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE), - interval(0), srid(0), geom_type(Field::GEOM_GEOMETRY), - option_list(NULL), + interval(0), charset(&my_charset_bin), + srid(0), geom_type(Field::GEOM_GEOMETRY), + option_list(NULL), pack_flag(0), vcol_info(0), default_value(0), check_constraint(0) { interval_list.empty(); } - Column_definition(THD *thd, Field *field, Field *orig_field); - void create_length_to_internal_length(void); + void set_attributes(const Lex_field_type_st &type, CHARSET_INFO *cs); + void create_length_to_internal_length_null() + { + DBUG_ASSERT(length == 0); + key_length= pack_length= 0; + } + void create_length_to_internal_length_simple() + { + key_length= pack_length= type_handler()->calc_pack_length((uint32) length); + } + void create_length_to_internal_length_string() + { + length*= charset->mbmaxlen; + DBUG_ASSERT(length <= UINT_MAX32); + key_length= (uint) length; + pack_length= type_handler()->calc_pack_length((uint32) length); + } + void create_length_to_internal_length_typelib() + { + /* Pack_length already calculated in sql_parse.cc */ + length*= charset->mbmaxlen; + key_length= pack_length; + } + void create_length_to_internal_length_bit(); + void create_length_to_internal_length_newdecimal(); + + /** + Prepare a SET/ENUM field. + Create "interval" from "interval_list" if needed, and adjust "length". + @param mem_root - Memory root to allocate TYPELIB and + its values on + @param reuse_interval_list_values - determines if TYPELIB can reuse value + buffers from interval_list, or should + always allocate a copy on mem_root, + even if character set conversion + is not needed + */ + bool prepare_interval_field(MEM_ROOT *mem_root, + bool reuse_interval_list_values); + + void prepare_interval_field_calc_length() + { + uint32 field_length, dummy; + if (real_field_type() == MYSQL_TYPE_SET) + { + calculate_interval_lengths(&dummy, &field_length); + length= field_length + (interval->count - 1); + } + else /* MYSQL_TYPE_ENUM */ + { + calculate_interval_lengths(&field_length, &dummy); + length= field_length; + } + set_if_smaller(length, MAX_FIELD_WIDTH - 1); + } + + bool prepare_blob_field(THD *thd); + + bool sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root); + + bool prepare_stage1(THD *thd, MEM_ROOT *mem_root, + handler *file, ulonglong table_flags); + bool prepare_stage1_typelib(THD *thd, MEM_ROOT *mem_root, + handler *file, ulonglong table_flags); + bool prepare_stage1_string(THD *thd, MEM_ROOT *mem_root, + handler *file, ulonglong table_flags); + bool prepare_stage1_bit(THD *thd, MEM_ROOT *mem_root, + handler *file, ulonglong table_flags); + + void redefine_stage1_common(const Column_definition *dup_field, + const handler *file, + const Schema_specification_st *schema); + bool redefine_stage1(const Column_definition *dup_field, const handler *file, + const Schema_specification_st *schema) + { + const Type_handler *handler= dup_field->type_handler(); + return handler->Column_definition_redefine_stage1(this, dup_field, + file, schema); + } + bool prepare_stage2(handler *handler, ulonglong table_flags); + bool prepare_stage2_blob(handler *handler, + ulonglong table_flags, uint field_flags); + bool prepare_stage2_varchar(ulonglong table_flags); + bool prepare_stage2_typelib(const char *type_name, uint field_flags, + uint *dup_val_count); + uint pack_flag_numeric(uint dec) const; + uint sign_length() const { return flags & UNSIGNED_FLAG ? 0 : 1; } + bool check_length(uint mysql_errno, uint max_allowed_length) const; + bool fix_attributes_real(uint default_length); + bool fix_attributes_int(uint default_length); + bool fix_attributes_decimal(); + bool fix_attributes_temporal_with_time(uint int_part_length); + bool fix_attributes_bit(); bool check(THD *thd); @@ -3857,16 +4038,16 @@ public: Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, uchar *ptr, uchar *null_pos, uchar null_bit, - const char *field_name_arg) const + const LEX_CSTRING *field_name_arg) const { return ::make_field(share, mem_root, ptr, (uint32)length, null_pos, null_bit, - pack_flag, sql_type, charset, + pack_flag, type_handler(), charset, geom_type, srid, unireg_check, interval, field_name_arg); } Field *make_field(TABLE_SHARE *share, MEM_ROOT *mem_root, - const char *field_name_arg) + const LEX_CSTRING *field_name_arg) const { return make_field(share, mem_root, (uchar *) 0, (uchar *) "", 0, field_name_arg); @@ -3879,14 +4060,162 @@ public: return unireg_check == Field::TIMESTAMP_DN_FIELD || unireg_check == Field::TIMESTAMP_DNUN_FIELD; } + + // Replace the entire value by another definition + void set_column_definition(const Column_definition *def) + { + *this= *def; + } +}; + + +/** + List of ROW element definitions, e.g.: + DECLARE a ROW(a INT,b VARCHAR(10)) +*/ +class Row_definition_list: public List<class Spvar_definition> +{ +public: + inline bool eq_name(const Spvar_definition *def, const LEX_CSTRING *name) const; + /** + Find a ROW field by name. + @param [IN] name - the name + @param [OUT] offset - if the ROW field found, its offset it returned here + @retval NULL - the ROW field was not found + @retval !NULL - the pointer to the found ROW field + */ + Spvar_definition *find_row_field_by_name(const LEX_CSTRING *name, uint *offset) const + { + // Cast-off the "const" qualifier + List_iterator<Spvar_definition> it(*((List<Spvar_definition>*)this)); + Spvar_definition *def; + for (*offset= 0; (def= it++); (*offset)++) + { + if (eq_name(def, name)) + return def; + } + return 0; + } + bool adjust_formal_params_to_actual_params(THD *thd, List<Item> *args); + bool adjust_formal_params_to_actual_params(THD *thd, + Item **args, uint arg_count); + bool resolve_type_refs(THD *); }; +/** + This class is used during a stored routine or a trigger execution, + at sp_rcontext::create() time. + Currently it can represent: + - variables with explicit data types: DECLARE a INT; + - variables with data type references: DECLARE a t1.a%TYPE; + - ROW type variables + + Notes: + - Scalar variables have m_field_definitions==NULL. + - ROW variables are defined as having MYSQL_TYPE_NULL, + with a non-empty m_field_definitions. + + Data type references to other object types will be added soon, e.g.: + - DECLARE a table_name%ROWTYPE; + - DECLARE a cursor_name%ROWTYPE; + - DECLARE a record_name%TYPE; + - DECLARE a variable_name%TYPE; +*/ +class Spvar_definition: public Column_definition +{ + class Qualified_column_ident *m_column_type_ref; // for %TYPE + class Table_ident *m_table_rowtype_ref; // for table%ROWTYPE + bool m_cursor_rowtype_ref; // for cursor%ROWTYPE + Row_definition_list *m_row_field_definitions; // for ROW +public: + Spvar_definition() + :m_column_type_ref(NULL), + m_table_rowtype_ref(NULL), + m_cursor_rowtype_ref(false), + m_row_field_definitions(NULL) + { } + Spvar_definition(THD *thd, Field *field) + :Column_definition(thd, field, NULL), + m_column_type_ref(NULL), + m_table_rowtype_ref(NULL), + m_cursor_rowtype_ref(false), + m_row_field_definitions(NULL) + { } + const Type_handler *type_handler() const + { + return is_row() || is_table_rowtype_ref() || is_cursor_rowtype_ref() ? + &type_handler_row : + Type_handler_hybrid_field_type::type_handler(); + } + bool is_column_type_ref() const { return m_column_type_ref != 0; } + bool is_table_rowtype_ref() const { return m_table_rowtype_ref != 0; } + bool is_cursor_rowtype_ref() const { return m_cursor_rowtype_ref; } + class Qualified_column_ident *column_type_ref() const + { + return m_column_type_ref; + } + void set_column_type_ref(class Qualified_column_ident *ref) + { + m_column_type_ref= ref; + } + + class Table_ident *table_rowtype_ref() const + { + return m_table_rowtype_ref; + } + void set_table_rowtype_ref(class Table_ident *ref) + { + m_table_rowtype_ref= ref; + } + void set_cursor_rowtype_ref(bool ref) + { + m_cursor_rowtype_ref= ref; + } + + /* + Find a ROW field by name. + See Row_field_list::find_row_field_by_name() for details. + */ + Spvar_definition *find_row_field_by_name(const LEX_CSTRING *name, uint *offset) const + { + DBUG_ASSERT(m_row_field_definitions); + return m_row_field_definitions->find_row_field_by_name(name, offset); + } + uint is_row() const + { + return m_row_field_definitions != NULL; + } + // Check if "this" defines a ROW variable with n elements + uint is_row(uint n) const + { + return m_row_field_definitions != NULL && + m_row_field_definitions->elements == n; + } + Row_definition_list *row_field_definitions() const + { + return m_row_field_definitions; + } + void set_row_field_definitions(Row_definition_list *list) + { + m_row_field_definitions= list; + } + +}; + + +inline bool Row_definition_list::eq_name(const Spvar_definition *def, + const LEX_CSTRING *name) const +{ + return def->field_name.length == name->length && my_strcasecmp(system_charset_info, def->field_name.str, name->str) == 0; +} + + class Create_field :public Column_definition { public: - const char *change; // If done with alter table - const char *after; // Put column after this one + LEX_CSTRING change; // If done with alter table + LEX_CSTRING after; // Put column after this one Field *field; // For alter table TYPELIB *save_interval; // Temporary copy for the above // Used only for UCS2 intervals @@ -3898,16 +4227,20 @@ public: bool create_if_not_exists; // Used in ALTER TABLE IF NOT EXISTS Create_field(): - Column_definition(), change(0), after(0), + Column_definition(), field(0), option_struct(NULL), create_if_not_exists(false) - { } + { + change= after= null_clex_str; + } Create_field(THD *thd, Field *old_field, Field *orig_field): Column_definition(thd, old_field, orig_field), - change(old_field->field_name), after(0), + change(old_field->field_name), field(old_field), option_struct(old_field->option_struct), create_if_not_exists(false) - { } + { + after= null_clex_str; + } /* Used to make a clone of this object for ALTER/CREATE TABLE */ Create_field *clone(MEM_ROOT *mem_root) const; }; @@ -3921,9 +4254,9 @@ class Send_field :public Sql_alloc { public: const char *db_name; const char *table_name,*org_table_name; - const char *col_name,*org_col_name; + LEX_CSTRING col_name, org_col_name; ulong length; - uint charsetnr, flags, decimals; + uint flags, decimals; enum_field_types type; Send_field() {} }; @@ -3971,11 +4304,10 @@ public: uint pack_length_to_packflag(uint type); enum_field_types get_blob_type_from_length(ulong length); -uint32 calc_pack_length(enum_field_types type,uint32 length); int set_field_to_null(Field *field); int set_field_to_null_with_conversions(Field *field, bool no_conversions); int convert_null_to_field_value_or_error(Field *field); -bool check_expression(Virtual_column_info *vcol, const char *name, +bool check_expression(Virtual_column_info *vcol, LEX_CSTRING *name, enum_vcol_info_type type); /* diff --git a/sql/field_conv.cc b/sql/field_conv.cc index c3ad0c878b5..932188d56a8 100644 --- a/sql/field_conv.cc +++ b/sql/field_conv.cc @@ -125,7 +125,7 @@ static int set_bad_null_error(Field *field, int err) return 0; case CHECK_FIELD_ERROR_FOR_NULL: if (!field->table->in_use->no_errors) - my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name); + my_error(ER_BAD_NULL_ERROR, MYF(0), field->field_name.str); return -1; } DBUG_ASSERT(0); // impossible diff --git a/sql/filesort.cc b/sql/filesort.cc index f772011241f..fc6453b904b 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -564,7 +564,8 @@ const char* dbug_print_table_row(TABLE *table) else output.append(","); - output.append((*pfield)->field_name? (*pfield)->field_name: "NULL"); + output.append((*pfield)->field_name.str ? + (*pfield)->field_name.str: "NULL"); } output.append(")=("); @@ -615,7 +616,8 @@ static void dbug_print_record(TABLE *table, bool print_rowid) fprintf(DBUG_FILE, "record ("); for (pfield= table->field; *pfield ; pfield++) - fprintf(DBUG_FILE, "%s%s", (*pfield)->field_name, (pfield[1])? ", ":""); + fprintf(DBUG_FILE, "%s%s", (*pfield)->field_name.str, + (pfield[1])? ", ":""); fprintf(DBUG_FILE, ") = "); fprintf(DBUG_FILE, "("); @@ -1179,7 +1181,8 @@ static void make_sortkey(register Sort_param *param, } else { // Item - sort_field->item->make_sort_key(to, sort_field->item, sort_field, param); + sort_field->item->type_handler()->make_sort_key(to, sort_field->item, + sort_field, param); if ((maybe_null= sort_field->item->maybe_null)) to++; } @@ -1968,7 +1971,8 @@ sortlength(THD *thd, SORT_FIELD *sortorder, uint s_length, } else { - sortorder->item->sortlength(thd, sortorder->item, sortorder); + sortorder->item->type_handler()->sortlength(thd, sortorder->item, + sortorder); if (use_strnxfrm(sortorder->item->collation.collation)) { *multi_byte_charset= true; diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index a0fbdffec90..2076379cd61 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -77,7 +77,9 @@ HA_DUPLICATE_POS | \ HA_CAN_SQL_HANDLER | \ HA_CAN_INSERT_DELAYED | \ - HA_READ_BEFORE_WRITE_REMOVAL) + HA_READ_BEFORE_WRITE_REMOVAL |\ + HA_CAN_TABLES_WITHOUT_ROLLBACK) + static const char *ha_par_ext= ".par"; /**************************************************************************** @@ -1533,8 +1535,8 @@ int ha_partition::prepare_new_partition(TABLE *tbl, That file name may be different from part_name, which will be attached in append_file_to_dir(). */ - truncate_partition_filename(p_elem->data_file_name); - truncate_partition_filename(p_elem->index_file_name); + truncate_partition_filename((char*) p_elem->data_file_name); + truncate_partition_filename((char*) p_elem->index_file_name); if ((error= set_up_table_before_create(tbl, part_name, create_info, p_elem))) goto error_create; @@ -2116,7 +2118,7 @@ void ha_partition::update_create_info(HA_CREATE_INFO *create_info) my_bool from_alter = (create_info->data_file_name == (const char*) -1); create_info->data_file_name= create_info->index_file_name = NULL; - create_info->connect_string= null_lex_str; + create_info->connect_string= null_clex_str; /* We do not need to update the individual partition DATA DIRECTORY settings @@ -2991,27 +2993,29 @@ bool ha_partition::read_par_file(const char *name) m_file_buffer= file_buffer; // Will be freed in clear_handler_file() m_name_buffer_ptr= (char*) (tot_name_len_offset + PAR_WORD_SIZE); - if (!(m_connect_string= (LEX_STRING*) - alloc_root(&m_mem_root, m_tot_parts * sizeof(LEX_STRING)))) + if (!(m_connect_string= (LEX_CSTRING*) + alloc_root(&m_mem_root, m_tot_parts * sizeof(LEX_CSTRING)))) goto err2; - bzero(m_connect_string, m_tot_parts * sizeof(LEX_STRING)); + bzero(m_connect_string, m_tot_parts * sizeof(LEX_CSTRING)); /* Read connection arguments (for federated X engine) */ for (i= 0; i < m_tot_parts; i++) { - LEX_STRING connect_string; + LEX_CSTRING connect_string; uchar buffer[4]; + char *tmp; if (my_read(file, buffer, 4, MYF(MY_NABP))) { /* No extra options; Probably not a federatedx engine */ break; } connect_string.length= uint4korr(buffer); - connect_string.str= (char*) alloc_root(&m_mem_root, connect_string.length+1); + connect_string.str= tmp= (char*) alloc_root(&m_mem_root, + connect_string.length+1); if (my_read(file, (uchar*) connect_string.str, connect_string.length, MYF(MY_NABP))) break; - connect_string.str[connect_string.length]= 0; + tmp[connect_string.length]= 0; m_connect_string[i]= connect_string; } @@ -4240,7 +4244,7 @@ exit: old_data is always record[1] */ -int ha_partition::update_row(const uchar *old_data, uchar *new_data) +int ha_partition::update_row(const uchar *old_data, const uchar *new_data) { THD *thd= ha_thd(); uint32 new_part_id, old_part_id; @@ -4316,7 +4320,7 @@ int ha_partition::update_row(const uchar *old_data, uchar *new_data) DBUG_PRINT("info", ("Update from partition %d to partition %d", old_part_id, new_part_id)); tmp_disable_binlog(thd); /* Do not replicate the low-level changes. */ - error= m_file[new_part_id]->ha_write_row(new_data); + error= m_file[new_part_id]->ha_write_row((uchar*) new_data); reenable_binlog(thd); table->next_number_field= saved_next_number_field; if (error) @@ -7183,6 +7187,7 @@ int ha_partition::extra(enum ha_extra_function operation) case HA_EXTRA_QUICK: case HA_EXTRA_PREPARE_FOR_DROP: case HA_EXTRA_FLUSH_CACHE: + case HA_EXTRA_PREPARE_FOR_ALTER_TABLE: { DBUG_RETURN(loop_extra(operation)); } @@ -7987,7 +7992,7 @@ void ha_partition::append_row_to_str(String &str) { Field *field= key_part->field; str.append(" "); - str.append(field->field_name); + str.append(&field->field_name); str.append(":"); field_unpack(&str, field, rec, 0, false); } @@ -8007,7 +8012,7 @@ void ha_partition::append_row_to_str(String &str) { Field *field= *field_ptr; str.append(" "); - str.append(field->field_name); + str.append(&field->field_name); str.append(":"); field_unpack(&str, field, rec, 0, false); } diff --git a/sql/ha_partition.h b/sql/ha_partition.h index a301cc3871c..42081546988 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -137,7 +137,7 @@ private: handler **m_new_file; // Array of references to new handlers handler **m_reorged_file; // Reorganised partitions handler **m_added_file; // Added parts kept for errors - LEX_STRING *m_connect_string; + LEX_CSTRING *m_connect_string; partition_info *m_part_info; // local reference to partition Field **m_part_field_array; // Part field array locally to save acc uchar *m_ordered_rec_buffer; // Row and key buffer for ord. idx scan @@ -426,6 +426,8 @@ public: virtual THR_LOCK_DATA **store_lock(THD * thd, THR_LOCK_DATA ** to, enum thr_lock_type lock_type); virtual int external_lock(THD * thd, int lock_type); + LEX_CSTRING *engine_name() + { return hton_name(table->part_info->default_engine_type); } /* When table is locked a statement is started by calling start_stmt instead of external_lock @@ -481,7 +483,7 @@ public: number of calls to write_row. */ virtual int write_row(uchar * buf); - virtual int update_row(const uchar * old_data, uchar * new_data); + virtual int update_row(const uchar * old_data, const uchar * new_data); virtual int delete_row(const uchar * buf); virtual int delete_all_rows(void); virtual int truncate(); diff --git a/sql/ha_sequence.cc b/sql/ha_sequence.cc new file mode 100644 index 00000000000..7f2248bf3cb --- /dev/null +++ b/sql/ha_sequence.cc @@ -0,0 +1,438 @@ +/* + Copyright (c) 2017, Aliyun and/or its affiliates. + Copyright (c) 2017, MariaDB corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include <my_global.h> +#include "sql_list.h" +#include "table.h" +#include "sql_sequence.h" +#include "ha_sequence.h" +#include "sql_plugin.h" +#include "mysql/plugin.h" +#include "sql_priv.h" +#include "sql_parse.h" +#include "sql_table.h" +#include "sql_update.h" +#include "sql_base.h" +#include "log_event.h" + +/* + Table flags we should inherit and disable from the original engine. + We add HA_STATS_RECORDS_IS_EXACT as ha_sequence::info() will ensure + that records is always 1 +*/ + +#define SEQUENCE_ENABLED_TABLE_FLAGS (HA_STATS_RECORDS_IS_EXACT | \ + HA_PERSISTENT_TABLE) +#define SEQUENCE_DISABLED_TABLE_FLAGS (HA_CAN_SQL_HANDLER | \ + HA_CAN_INSERT_DELAYED | \ + HA_BINLOG_STMT_CAPABLE) +handlerton *sql_sequence_hton; + +/* + Create a sequence handler +*/ + +ha_sequence::ha_sequence(handlerton *hton, TABLE_SHARE *share) + :handler(hton, share), write_locked(0) +{ + sequence= share->sequence; + DBUG_ASSERT(share->sequence); +} + +/** + Destructor method must remove the underlying handler +*/ +ha_sequence::~ha_sequence() +{ + delete file; +} + +/** + Sequence table open method + + @param name Path to file (dbname and tablename) + @param mode mode + @param flags Flags how to open file + + RETURN VALUES + @retval 0 Success + @retval != 0 Failure +*/ + +int ha_sequence::open(const char *name, int mode, uint flags) +{ + int error; + DBUG_ENTER("ha_sequence::open"); + DBUG_ASSERT(table->s == table_share && file); + + file->table= table; + if (!(error= file->open(name, mode, flags))) + { + /* + Copy values set by handler::open() in the underlying handler + Reuse original storage engine data for duplicate key reference + It would be easier to do this if we would have another handler + call: fixup_after_open()... + */ + ref= file->ref; + ref_length= file->ref_length; + dup_ref= file->dup_ref; + + /* + ha_open() sets the following for us. We have to set this for the + underlying handler + */ + file->cached_table_flags= file->table_flags(); + + file->reset_statistics(); + internal_tmp_table= file->internal_tmp_table= + MY_TEST(flags & HA_OPEN_INTERNAL_TABLE); + reset_statistics(); + + /* Don't try to read the inital row the call is part of create code */ + if (!(flags & (HA_OPEN_FOR_CREATE | HA_OPEN_FOR_REPAIR))) + { + if ((error= table->s->sequence->read_initial_values(table))) + file->ha_close(); + } + else + table->m_needs_reopen= true; + } + DBUG_RETURN(error); +} + +/* + Clone the sequence. Needed if table is used by range optimization + (Very, very unlikely) +*/ + +handler *ha_sequence::clone(const char *name, MEM_ROOT *mem_root) +{ + ha_sequence *new_handler; + DBUG_ENTER("ha_sequence::clone"); + if (!(new_handler= new (mem_root) ha_sequence(ht, table_share))) + DBUG_RETURN(NULL); + + /* + Allocate new_handler->ref here because otherwise ha_open will allocate it + on this->table->mem_root and we will not be able to reclaim that memory + when the clone handler object is destroyed. + */ + + if (!(new_handler->ref= (uchar*) alloc_root(mem_root, + ALIGN_SIZE(ref_length)*2))) + goto err; + + if (new_handler->ha_open(table, name, + table->db_stat, + HA_OPEN_IGNORE_IF_LOCKED | HA_OPEN_NO_PSI_CALL)) + goto err; + + /* Reuse original storage engine data for duplicate key reference */ + new_handler->ref= file->ref; + new_handler->ref_length= file->ref_length; + new_handler->dup_ref= file->dup_ref; + + DBUG_RETURN((handler*) new_handler); + +err: + delete new_handler; + DBUG_RETURN(NULL); +} + + +/* + Map the create table to the original storage engine +*/ + +int ha_sequence::create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info) +{ + DBUG_ASSERT(create_info->sequence); + /* Sequence tables has one and only one row */ + create_info->max_rows= create_info->min_rows= 1; + return (file->create(name, form, create_info)); +} + +/** + Sequence write row method. + + A sequence table has only one row. Any inserts in the table + will update this row. + + @retval 0 Success + @retval != 0 Failure + + NOTES: + write_locked is set if we are called from SEQUENCE::next_value + In this case the mutex is already locked and we should not update + the sequence with 'buf' as the sequence object is already up to date. +*/ + +int ha_sequence::write_row(uchar *buf) +{ + int error; + sequence_definition tmp_seq; + bool sequence_locked; + DBUG_ENTER("ha_sequence::write_row"); + DBUG_ASSERT(table->record[0] == buf); + + row_already_logged= 0; + if (unlikely(sequence->initialized == SEQUENCE::SEQ_IN_PREPARE)) + { + /* This calls is from ha_open() as part of create table */ + DBUG_RETURN(file->write_row(buf)); + } + if (unlikely(sequence->initialized == SEQUENCE::SEQ_IN_ALTER)) + { + int error= 0; + /* This is called from alter table */ + tmp_seq.read_fields(table); + if (tmp_seq.check_and_adjust(0)) + DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA); + sequence->copy(&tmp_seq); + if (!(error= file->write_row(buf))) + sequence->initialized= SEQUENCE::SEQ_READY_TO_USE; + DBUG_RETURN(error); + } + if (unlikely(sequence->initialized != SEQUENCE::SEQ_READY_TO_USE)) + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + + sequence_locked= write_locked; + if (!write_locked) // If not from next_value() + { + /* + User tries to write a full row directly to the sequence table with + INSERT or LOAD DATA. + + - Get an exclusive lock for the table. This is needed to ensure that + we excute all full inserts (same as ALTER SEQUENCE) in same order + on master and slaves + - Check that we are only using one table. + This is to avoid deadlock problems when upgrading lock to exlusive. + - Check that the new row is an accurate SEQUENCE object + */ + + THD *thd= table->in_use; + if (thd->lock->table_count != 1) + DBUG_RETURN(ER_WRONG_INSERT_INTO_SEQUENCE); + if (thd->mdl_context.upgrade_shared_lock(table->mdl_ticket, MDL_EXCLUSIVE, + thd->variables.lock_wait_timeout)) + DBUG_RETURN(ER_LOCK_WAIT_TIMEOUT); + + tmp_seq.read_fields(table); + if (tmp_seq.check_and_adjust(0)) + DBUG_RETURN(HA_ERR_SEQUENCE_INVALID_DATA); + + /* + Lock sequence to ensure that no one can come in between + while sequence, table and binary log are updated. + */ + sequence->write_lock(table); + } + + if (!(error= file->update_first_row(buf))) + { + Log_func *log_func= Write_rows_log_event::binlog_row_logging_function; + if (!sequence_locked) + sequence->copy(&tmp_seq); + rows_changed++; + /* We have to do the logging while we hold the sequence mutex */ + error= binlog_log_row(table, 0, buf, log_func); + row_already_logged= 1; + } + + sequence->all_values_used= 0; + if (!sequence_locked) + sequence->write_unlock(table); + DBUG_RETURN(error); +} + + +/* + Inherit the sequence base table flags. +*/ + +handler::Table_flags ha_sequence::table_flags() const +{ + DBUG_ENTER("ha_sequence::table_flags"); + DBUG_RETURN((file->table_flags() & ~SEQUENCE_DISABLED_TABLE_FLAGS) | + SEQUENCE_ENABLED_TABLE_FLAGS); +} + + +int ha_sequence::info(uint flag) +{ + DBUG_ENTER("ha_sequence::info"); + file->info(flag); + /* Inform optimizer that we have always only one record */ + stats= file->stats; + stats.records= 1; + DBUG_RETURN(false); +} + + +int ha_sequence::extra(enum ha_extra_function operation) +{ + if (operation == HA_EXTRA_PREPARE_FOR_ALTER_TABLE) + { + /* In case of ALTER TABLE allow ::write_row() to copy rows */ + sequence->initialized= SEQUENCE::SEQ_IN_ALTER; + } + return file->extra(operation); +} + +bool ha_sequence::check_if_incompatible_data(HA_CREATE_INFO *create_info, + uint table_changes) +{ + /* Table definition is locked for SEQUENCE tables */ + return(COMPATIBLE_DATA_YES); +} + + +int ha_sequence::external_lock(THD *thd, int lock_type) +{ + int error= file->external_lock(thd, lock_type); + + /* + Copy lock flag to satisfy DBUG_ASSERT checks in ha_* functions in + handler.cc when we later call it with file->ha_..() + */ + file->m_lock_type= lock_type; + return error; +} + +/* + Squence engine error deal method +*/ + +void ha_sequence::print_error(int error, myf errflag) +{ + const char *sequence_db= table_share->db.str; + const char *sequence_name= table_share->table_name.str; + DBUG_ENTER("ha_sequence::print_error"); + + switch (error) { + case HA_ERR_SEQUENCE_INVALID_DATA: + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(errflag), sequence_db, + sequence_name); + DBUG_VOID_RETURN; + } + case HA_ERR_SEQUENCE_RUN_OUT: + { + my_error(ER_SEQUENCE_RUN_OUT, MYF(errflag), sequence_db, sequence_name); + DBUG_VOID_RETURN; + } + case HA_ERR_WRONG_COMMAND: + my_error(ER_ILLEGAL_HA, MYF(0), "SEQUENCE", sequence_db, sequence_name); + DBUG_VOID_RETURN; + case ER_WRONG_INSERT_INTO_SEQUENCE: + my_error(error, MYF(0)); + DBUG_VOID_RETURN; + } + file->print_error(error, errflag); + DBUG_VOID_RETURN; +} + +/***************************************************************************** + Sequence plugin interface +*****************************************************************************/ + +/* + Create an new handler +*/ + +static handler *sequence_create_handler(handlerton *hton, + TABLE_SHARE *share, + MEM_ROOT *mem_root) +{ + DBUG_ENTER("sequence_create_handler"); + DBUG_RETURN(new (mem_root) ha_sequence(hton, share)); +} + + +/* + Sequence engine end. + + SYNOPSIS + sequence_end() + p handlerton. + type panic type. + RETURN VALUES + 0 Success + !=0 Failure +*/ +static int sequence_end(handlerton* hton, + ha_panic_function type __attribute__((unused))) +{ + DBUG_ENTER("sequence_end"); + DBUG_RETURN(0); +} + + +/* + Sequence engine init. + + SYNOPSIS + sequence_initialize() + + @param p handlerton. + + retval 0 Success + retval !=0 Failure +*/ + +static int sequence_initialize(void *p) +{ + handlerton *local_sequence_hton= (handlerton *)p; + DBUG_ENTER("sequence_initialize"); + + local_sequence_hton->state= SHOW_OPTION_YES; + local_sequence_hton->db_type= DB_TYPE_SEQUENCE; + local_sequence_hton->create= sequence_create_handler; + local_sequence_hton->panic= sequence_end; + local_sequence_hton->flags= (HTON_NOT_USER_SELECTABLE | + HTON_HIDDEN | + HTON_TEMPORARY_NOT_SUPPORTED | + HTON_ALTER_NOT_SUPPORTED | + HTON_NO_PARTITION); + DBUG_RETURN(0); +} + + +static struct st_mysql_storage_engine sequence_storage_engine= +{ MYSQL_HANDLERTON_INTERFACE_VERSION }; + +maria_declare_plugin(sql_sequence) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &sequence_storage_engine, + "SQL_SEQUENCE", + "jianwei.zhao @ Aliyun & Monty @ MariaDB corp", + "Sequence Storage Engine for CREATE SEQUENCE", + PLUGIN_LICENSE_GPL, + sequence_initialize, /* Plugin Init */ + NULL, /* Plugin Deinit */ + 0x0100, /* 1.0 */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_ALPHA /* maturity */ +} +maria_declare_plugin_end; diff --git a/sql/ha_sequence.h b/sql/ha_sequence.h new file mode 100644 index 00000000000..fd9da05b591 --- /dev/null +++ b/sql/ha_sequence.h @@ -0,0 +1,165 @@ +#ifndef HA_SEQUENCE_INCLUDED +#define HA_SEQUENCE_INCLUDED +/* + Copyright (c) 2017 Aliyun and/or its affiliates. + Copyright (c) 2017 MariaDB corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "sql_sequence.h" +#include "table.h" +#include "handler.h" + +extern handlerton *sql_sequence_hton; + +/* + Sequence engine handler. + + The sequence engine is a logic engine. It doesn't store any data. + All the sequence data stored into the base table which must support + non rollback writes (HA_CAN_TABLES_WITHOUT_ROLLBACK) + + The sequence data (SEQUENCE class) is stored in TABLE_SHARE->sequence + + TABLE RULES: + 1. When table is created, one row is automaticlly inserted into + the table. The table will always have one and only one row. + 2. Any inserts or updates to the table will be validated. + 3. Inserts will overwrite the original row. + 4. DELETE and TRUNCATE will not affect the table. + Instead a warning will be given. + 5. Cache will be reset for any updates. + + CACHE RULES: + SEQUENCE class is used to cache values that sequence defined. + 1. If hit cache, we can query back the sequence nextval directly + instead of reading the underlying table. + + 2. When run out of values, the sequence engine will reserve new values + in update the base table. + + 3. The cache is invalidated if any update on based table. +*/ + +class ha_sequence :public handler +{ +private: + handler *file; + SEQUENCE *sequence; /* From table_share->sequence */ + +public: + /* Set when handler is write locked */ + bool write_locked; + + ha_sequence(handlerton *hton, TABLE_SHARE *share); + ~ha_sequence(); + + /* virtual function that are re-implemented for sequence */ + int open(const char *name, int mode, uint test_if_locked); + int create(const char *name, TABLE *form, + HA_CREATE_INFO *create_info); + handler *clone(const char *name, MEM_ROOT *mem_root); + int write_row(uchar *buf); + Table_flags table_flags() const; + /* One can't update or delete from sequence engine */ + int update_row(const uchar *old_data, const uchar *new_data) + { return HA_ERR_WRONG_COMMAND; } + int delete_row(const uchar *buf) + { return HA_ERR_WRONG_COMMAND; } + /* One can't delete from sequence engine */ + int truncate() + { return HA_ERR_WRONG_COMMAND; } + /* Can't use query cache */ + uint8 table_cache_type() + { return HA_CACHE_TBL_NOCACHE; } + void print_error(int error, myf errflag); + int info(uint); + LEX_CSTRING *engine_name() { return hton_name(file->ht); } + int external_lock(THD *thd, int lock_type); + int extra(enum ha_extra_function operation); + /* For ALTER ONLINE TABLE */ + bool check_if_incompatible_data(HA_CREATE_INFO *create_info, + uint table_changes); + void write_lock() { write_locked= 1;} + void unlock() { write_locked= 0; } + bool is_locked() { return write_locked; } + + /* Functions that are directly mapped to the underlying handler */ + int rnd_init(bool scan) + { return file->rnd_init(scan); } + /* + We need to have a lock here to protect engines like MyISAM from + simultaneous read and write. For sequence's this is not critical + as this function is used extremely seldom. + */ + int rnd_next(uchar *buf) + { + int error; + table->s->sequence->read_lock(table); + error= file->rnd_next(buf); + table->s->sequence->read_unlock(table); + return error; + } + int rnd_end() + { return file->rnd_end(); } + int rnd_pos(uchar *buf, uchar *pos) + { + int error; + table->s->sequence->read_lock(table); + error= file->rnd_pos(buf, pos); + table->s->sequence->read_unlock(table); + return error; + } + void position(const uchar *record) + { return file->position(record); } + const char *table_type() const + { return file->table_type(); } + ulong index_flags(uint inx, uint part, bool all_parts) const + { return file->index_flags(inx, part, all_parts); } + THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, + enum thr_lock_type lock_type) + { return file->store_lock(thd, to, lock_type); } + int close(void) + { return file->close(); } + const char **bas_ext() const + { return file->bas_ext(); } + int delete_table(const char*name) + { return file->delete_table(name); } + int rename_table(const char *from, const char *to) + { return file->rename_table(from, to); } + void unbind_psi() + { return file->unbind_psi(); } + void rebind_psi() + { return file->rebind_psi(); } + + bool auto_repair(int error) const + { return file->auto_repair(error); } + int repair(THD* thd, HA_CHECK_OPT* check_opt) + { return file->repair(thd, check_opt); } + bool check_and_repair(THD *thd) + { return file->check_and_repair(thd); } + bool is_crashed() const + { return file->is_crashed(); } + void column_bitmaps_signal() + { return file->column_bitmaps_signal(); } + + /* New methods */ + void register_original_handler(handler *file_arg) + { + file= file_arg; + init(); /* Update cached_table_flags */ + } +}; +#endif diff --git a/sql/handler.cc b/sql/handler.cc index 2e499238dba..d072e750e09 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -41,6 +41,7 @@ #include <mysql/psi/mysql_table.h> #include "debug_sync.h" // DEBUG_SYNC #include "sql_audit.h" +#include "ha_sequence.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -83,12 +84,12 @@ ulong failed_ha_2pc= 0; /* size of savepoint storage area (see ha_init) */ ulong savepoint_alloc_size= 0; -static const LEX_STRING sys_table_aliases[]= +static const LEX_CSTRING sys_table_aliases[]= { - { C_STRING_WITH_LEN("INNOBASE") }, { C_STRING_WITH_LEN("INNODB") }, - { C_STRING_WITH_LEN("HEAP") }, { C_STRING_WITH_LEN("MEMORY") }, - { C_STRING_WITH_LEN("MERGE") }, { C_STRING_WITH_LEN("MRG_MYISAM") }, - { C_STRING_WITH_LEN("Maria") }, { C_STRING_WITH_LEN("Aria") }, + { STRING_WITH_LEN("INNOBASE") }, { STRING_WITH_LEN("INNODB") }, + { STRING_WITH_LEN("HEAP") }, { STRING_WITH_LEN("MEMORY") }, + { STRING_WITH_LEN("MERGE") }, { STRING_WITH_LEN("MRG_MYISAM") }, + { STRING_WITH_LEN("Maria") }, { STRING_WITH_LEN("Aria") }, {NullS, 0} }; @@ -167,9 +168,10 @@ handlerton *ha_default_tmp_handlerton(THD *thd) RETURN pointer to storage engine plugin handle */ -plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name, bool tmp_table) +plugin_ref ha_resolve_by_name(THD *thd, const LEX_CSTRING *name, + bool tmp_table) { - const LEX_STRING *table_alias; + const LEX_CSTRING *table_alias; plugin_ref plugin; redo: @@ -299,7 +301,6 @@ handler *get_ha_partition(partition_info *part_info) } #endif - static const char **handler_errmsgs; C_MODE_START @@ -413,7 +414,7 @@ static int full_discover_for_existence(handlerton *, const char *, const char *) static int ext_based_existence(handlerton *, const char *, const char *) { return 0; } -static int hton_ext_based_table_discovery(handlerton *hton, LEX_STRING *db, +static int hton_ext_based_table_discovery(handlerton *hton, LEX_CSTRING *db, MY_DIR *dir, handlerton::discovered_list *result) { /* @@ -626,7 +627,7 @@ int ha_initialize_handlerton(st_plugin_int *plugin) /* This is entirely for legacy. We will create a new "disk based" hton and a "memory" hton which will be configurable longterm. We should be able to - remove partition and myisammrg. + remove partition. */ switch (hton->db_type) { case DB_TYPE_HEAP: @@ -638,6 +639,9 @@ int ha_initialize_handlerton(st_plugin_int *plugin) case DB_TYPE_PARTITION_DB: partition_hton= hton; break; + case DB_TYPE_SEQUENCE: + sql_sequence_hton= hton; + break; default: break; }; @@ -2418,6 +2422,11 @@ err: return NULL; } +LEX_CSTRING *handler::engine_name() +{ + return hton_name(ht); +} + double handler::keyread_time(uint index, uint ranges, ha_rows rows) { @@ -2541,6 +2550,7 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, } reset_statistics(); internal_tmp_table= MY_TEST(test_if_locked & HA_OPEN_INTERNAL_TABLE); + DBUG_RETURN(error); } @@ -2789,10 +2799,11 @@ int handler::ha_rnd_init_with_error(bool scan) /** - Read first row (only) from a table. + Read first row (only) from a table. Used for reading tables with + only one row, either based on table statistics or if table is a SEQUENCE. - This is never called for InnoDB tables, as these table types - has the HA_STATS_RECORDS_IS_EXACT set. + This is never called for normal InnoDB tables, as these table types + does not have HA_STATS_RECORDS_IS_EXACT set. */ int handler::read_first_row(uchar * buf, uint primary_key) { @@ -3593,7 +3604,7 @@ void handler::print_error(int error, myf errflag) break; case HA_ERR_AUTOINC_ERANGE: textno= error; - my_error(textno, errflag, table->next_number_field->field_name, + my_error(textno, errflag, table->next_number_field->field_name.str, table->in_use->get_stmt_da()->current_row_for_warning()); DBUG_VOID_RETURN; break; @@ -3994,7 +4005,7 @@ void handler::mark_trx_read_write_internal() */ if (ha_info->is_started()) { - DBUG_ASSERT(has_transactions()); + DBUG_ASSERT(has_transaction_manager()); /* table_share can be NULL in ha_delete_table(). See implementation of standalone function ha_delete_table() in sql_base.cc. @@ -4034,7 +4045,7 @@ int handler::ha_repair(THD* thd, HA_CHECK_OPT* check_opt) */ int -handler::ha_bulk_update_row(const uchar *old_data, uchar *new_data, +handler::ha_bulk_update_row(const uchar *old_data, const uchar *new_data, uint *dup_key_found) { DBUG_ASSERT(table_share->tmp_table != NO_TMP_TABLE || @@ -5040,22 +5051,28 @@ private: loaded, frm is invalid), the return value will be true, but *hton will be NULL. */ + bool ha_table_exists(THD *thd, const char *db, const char *table_name, - handlerton **hton) + handlerton **hton, bool *is_sequence) { handlerton *dummy; + bool dummy2; DBUG_ENTER("ha_table_exists"); if (hton) *hton= 0; else if (engines_with_discover) hton= &dummy; + if (!is_sequence) + is_sequence= &dummy2; + *is_sequence= 0; TDC_element *element= tdc_lock_share(thd, db, table_name); if (element && element != MY_ERRPTR) { if (hton) *hton= element->share->db_type(); + *is_sequence= element->share->table_type == TABLE_TYPE_SEQUENCE; tdc_unlock_share(element); DBUG_RETURN(TRUE); } @@ -5071,11 +5088,12 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, if (hton) { char engine_buf[NAME_CHAR_LEN + 1]; - LEX_STRING engine= { engine_buf, 0 }; + LEX_CSTRING engine= { engine_buf, 0 }; - if (dd_frm_type(thd, path, &engine) != FRMTYPE_VIEW) + if (dd_frm_type(thd, path, &engine, is_sequence) != TABLE_TYPE_VIEW) { - plugin_ref p= plugin_lock_by_name(thd, &engine, MYSQL_STORAGE_ENGINE_PLUGIN); + plugin_ref p= plugin_lock_by_name(thd, &engine, + MYSQL_STORAGE_ENGINE_PLUGIN); *hton= p ? plugin_hton(p) : NULL; if (*hton) // verify that the table really exists @@ -5096,7 +5114,6 @@ bool ha_table_exists(THD *thd, const char *db, const char *table_name, DBUG_RETURN(TRUE); } - if (need_full_discover_for_existence) { TABLE_LIST table; @@ -5138,7 +5155,7 @@ static int cmp_file_names(const void *a, const void *b) return my_strnncoll(cs, (uchar*)aa, strlen(aa), (uchar*)bb, strlen(bb)); } -static int cmp_table_names(LEX_STRING * const *a, LEX_STRING * const *b) +static int cmp_table_names(LEX_CSTRING * const *a, LEX_CSTRING * const *b) { return my_strnncoll(&my_charset_bin, (uchar*)((*a)->str), (*a)->length, (uchar*)((*b)->str), (*b)->length); @@ -5147,8 +5164,8 @@ static int cmp_table_names(LEX_STRING * const *a, LEX_STRING * const *b) } Discovered_table_list::Discovered_table_list(THD *thd_arg, - Dynamic_array<LEX_STRING*> *tables_arg, - const LEX_STRING *wild_arg) : + Dynamic_array<LEX_CSTRING*> *tables_arg, + const LEX_CSTRING *wild_arg) : thd(thd_arg), with_temps(false), tables(tables_arg) { if (wild_arg->str && wild_arg->str[0]) @@ -5172,7 +5189,7 @@ bool Discovered_table_list::add_table(const char *tname, size_t tlen) wild_prefix, wild_one, wild_many)) return 0; - LEX_STRING *name= thd->make_lex_string(tname, tlen); + LEX_CSTRING *name= thd->make_clex_string(tname, tlen); if (!name || tables->append(name)) return 1; return 0; @@ -5198,11 +5215,11 @@ void Discovered_table_list::sort() void Discovered_table_list::remove_duplicates() { - LEX_STRING **src= tables->front(); - LEX_STRING **dst= src; + LEX_CSTRING **src= tables->front(); + LEX_CSTRING **dst= src; while (++dst <= tables->back()) { - LEX_STRING *s= *src, *d= *dst; + LEX_CSTRING *s= *src, *d= *dst; DBUG_ASSERT(strncmp(s->str, d->str, MY_MIN(s->length, d->length)) <= 0); if ((s->length != d->length || strncmp(s->str, d->str, d->length))) { @@ -5216,7 +5233,7 @@ void Discovered_table_list::remove_duplicates() struct st_discover_names_args { - LEX_STRING *db; + LEX_CSTRING *db; MY_DIR *dirp; Discovered_table_list *result; uint possible_duplicates; @@ -5261,7 +5278,7 @@ static my_bool discover_names(THD *thd, plugin_ref plugin, for DROP DATABASE (as it needs to know and delete non-table files). */ -int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp, +int ha_discover_table_names(THD *thd, LEX_CSTRING *db, MY_DIR *dirp, Discovered_table_list *result, bool reusable) { int error; @@ -5611,7 +5628,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat) { if (db_type->state != SHOW_OPTION_YES) { - const LEX_STRING *name= hton_name(db_type); + const LEX_CSTRING *name= hton_name(db_type); result= stat_print(thd, name->str, name->length, "", 0, "DISABLED", 8) ? 1 : 0; } @@ -5799,8 +5816,6 @@ static int write_locked_table_maps(THD *thd) } -typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*); - static int check_wsrep_max_ws_rows(); static int binlog_log_row_internal(TABLE* table, @@ -5841,10 +5856,10 @@ static int binlog_log_row_internal(TABLE* table, return error ? HA_ERR_RBR_LOGGING_FAILED : 0; } -static inline int binlog_log_row(TABLE* table, - const uchar *before_record, - const uchar *after_record, - Log_func *log_func) +int binlog_log_row(TABLE* table, + const uchar *before_record, + const uchar *after_record, + Log_func *log_func) { if (!table->file->check_table_binlog_row_based(1)) return 0; @@ -5996,7 +6011,7 @@ int handler::ha_write_row(uchar *buf) { error= write_row(buf); }) MYSQL_INSERT_ROW_DONE(error); - if (likely(!error)) + if (likely(!error) && !row_already_logged) { rows_changed++; error= binlog_log_row(table, 0, buf, log_func); @@ -6006,7 +6021,7 @@ int handler::ha_write_row(uchar *buf) } -int handler::ha_update_row(const uchar *old_data, uchar *new_data) +int handler::ha_update_row(const uchar *old_data, const uchar *new_data) { int error; Log_func *log_func= Update_rows_log_event::binlog_row_logging_function; @@ -6028,7 +6043,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) { error= update_row(old_data, new_data);}) MYSQL_UPDATE_ROW_DONE(error); - if (likely(!error)) + if (likely(!error) && !row_already_logged) { rows_changed++; error= binlog_log_row(table, old_data, new_data, log_func); @@ -6036,6 +6051,34 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) return error; } +/* + Update first row. Only used by sequence tables +*/ + +int handler::update_first_row(uchar *new_data) +{ + int error; + if (!(error= ha_rnd_init(1))) + { + int end_error; + if (!(error= ha_rnd_next(table->record[1]))) + { + /* + We have to do the memcmp as otherwise we may get error 169 from InnoDB + */ + if (memcmp(table->record[0], table->record[1], table->s->reclength)) + error= update_row(table->record[1], new_data); + } + end_error= ha_rnd_end(); + if (!error) + error= end_error; + /* Logging would be wrong if update_row works but ha_rnd_end fails */ + DBUG_ASSERT(!end_error || error != 0); + } + return error; +} + + int handler::ha_delete_row(const uchar *buf) { int error; @@ -6483,7 +6526,7 @@ int del_global_index_stats_for_table(THD *thd, uchar* cache_key, uint cache_key_ /* Remove a table from global table statistics */ -int del_global_table_stat(THD *thd, LEX_STRING *db, LEX_STRING *table) +int del_global_table_stat(THD *thd, LEX_CSTRING *db, LEX_CSTRING *table) { TABLE_STATS *table_stats; int res = 0; diff --git a/sql/handler.h b/sql/handler.h index 486ba564050..9ada2ccbe7c 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -42,9 +42,11 @@ #include <ft_global.h> #include <keycache.h> #include <mysql/psi/mysql_table.h> +#include "sql_sequence.h" class Alter_info; class Virtual_column_info; +class sequence_definition; // the following is for checking tables @@ -262,6 +264,24 @@ enum enum_alter_inplace_result { #define HA_CONCURRENT_OPTIMIZE (1ULL << 46) /* + If the storage engine support tables that will not roll back on commit + In addition the table should not lock rows and support READ and WRITE + UNCOMMITTED. + This is useful for implementing things like SEQUENCE but can also in + the future be useful to do logging that should never roll back. +*/ +#define HA_CAN_TABLES_WITHOUT_ROLLBACK (1ULL << 47) + +/* + Mainly for usage by SEQUENCE engine. Setting this flag means + that the table will never roll back and that all operations + for this table should stored in the non transactional log + space that will always be written, even on rollback. +*/ + +#define HA_PERSISTENT_TABLE (1ULL << 48) + +/* Set of all binlog flags. Currently only contain the capabilities flags. */ @@ -380,6 +400,7 @@ enum enum_alter_inplace_result { #define HA_LEX_CREATE_TMP_TABLE 1U #define HA_CREATE_TMP_ALTER 8U +#define HA_LEX_CREATE_SEQUENCE 16U #define HA_MAX_REC_LENGTH 65535 @@ -434,7 +455,8 @@ enum legacy_db_type DB_TYPE_PERFORMANCE_SCHEMA=28, DB_TYPE_ARIA=42, DB_TYPE_TOKUDB=43, - DB_TYPE_FIRST_DYNAMIC=44, + DB_TYPE_SEQUENCE=44, + DB_TYPE_FIRST_DYNAMIC=45, DB_TYPE_DEFAULT=127 // Must be last }; /* @@ -522,6 +544,8 @@ given at all. */ */ #define HA_CREATE_USED_STATS_SAMPLE_PAGES (1UL << 24) +/* Create a sequence */ +#define HA_CREATE_USED_SEQUENCE (1UL << 25) /* This is master database for most of system tables. However there @@ -1316,7 +1340,7 @@ struct handlerton Returns 0 on success and 1 on error. */ - int (*discover_table_names)(handlerton *hton, LEX_STRING *db, MY_DIR *dir, + int (*discover_table_names)(handlerton *hton, LEX_CSTRING *db, MY_DIR *dir, discovered_list *result); /* @@ -1362,7 +1386,7 @@ struct handlerton }; -static inline LEX_STRING *hton_name(const handlerton *hton) +static inline LEX_CSTRING *hton_name(const handlerton *hton) { return &(hton2plugin[hton->slot]->name); } @@ -1458,14 +1482,16 @@ struct THD_TRANS unsigned int m_unsafe_rollback_flags; /* - Define the type of statemens which cannot be rolled back safely. + Define the type of statements which cannot be rolled back safely. Each type occupies one bit in m_unsafe_rollback_flags. */ - static unsigned int const MODIFIED_NON_TRANS_TABLE= 0x01; - static unsigned int const CREATED_TEMP_TABLE= 0x02; - static unsigned int const DROPPED_TEMP_TABLE= 0x04; - static unsigned int const DID_WAIT= 0x08; - static unsigned int const DID_DDL= 0x10; + enum unsafe_statement_types + { + CREATED_TEMP_TABLE= 2, + DROPPED_TEMP_TABLE= 4, + DID_WAIT= 8, + DID_DDL= 0x10 + }; void mark_created_temp_table() { @@ -1657,9 +1683,9 @@ struct Table_scope_and_contents_source_st { CHARSET_INFO *table_charset; LEX_CUSTRING tabledef_version; - LEX_STRING connect_string; + LEX_CSTRING connect_string; + LEX_CSTRING comment; const char *password, *tablespace; - LEX_STRING comment; const char *data_file_name, *index_file_name; const char *alias; ulonglong max_rows,min_rows; @@ -1697,6 +1723,7 @@ struct Table_scope_and_contents_source_st engine_option_value *option_list; ///< list of table create options enum_stats_auto_recalc stats_auto_recalc; bool varchar; ///< 1 if table has a VARCHAR + bool sequence; // If SEQUENCE=1 was used List<Virtual_column_info> *check_constraint_list; @@ -1710,6 +1737,7 @@ struct Table_scope_and_contents_source_st TABLE_LIST *pos_in_locked_tables; MDL_ticket *mdl_ticket; bool table_was_deleted; + sequence_definition *seq_create_info; void init() { @@ -2148,8 +2176,8 @@ typedef struct st_key_create_information { enum ha_key_alg algorithm; ulong block_size; - LEX_STRING parser_name; - LEX_STRING comment; + LEX_CSTRING parser_name; + LEX_CSTRING comment; /** A flag to determine if we will check for duplicate indexes. This typically means that the key information was specified @@ -2655,6 +2683,8 @@ public: bool mark_trx_read_write_done; /* mark_trx_read_write was called */ bool check_table_binlog_row_based_done; /* check_table_binlog.. was called */ bool check_table_binlog_row_based_result; /* cached check_table_binlog... */ + /* Set to 1 if handler logged last insert/update/delete operation */ + bool row_already_logged; /* TRUE <=> the engine guarantees that returned records are within the range being scanned. @@ -2757,6 +2787,7 @@ public: mark_trx_read_write_done(0), check_table_binlog_row_based_done(0), check_table_binlog_row_based_result(0), + row_already_logged(0), in_range_check_pushed_down(FALSE), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), keyread(MAX_KEY), @@ -2860,7 +2891,7 @@ public: */ int ha_external_lock(THD *thd, int lock_type); int ha_write_row(uchar * buf); - int ha_update_row(const uchar * old_data, uchar * new_data); + int ha_update_row(const uchar * old_data, const uchar * new_data); int ha_delete_row(const uchar * buf); void ha_release_auto_increment(); @@ -2898,7 +2929,7 @@ public: int ret= end_bulk_insert(); DBUG_RETURN(ret); } - int ha_bulk_update_row(const uchar *old_data, uchar *new_data, + int ha_bulk_update_row(const uchar *old_data, const uchar *new_data, uint *dup_key_found); int ha_delete_all_rows(); int ha_truncate(); @@ -2993,8 +3024,24 @@ public: virtual double keyread_time(uint index, uint ranges, ha_rows rows); virtual const key_map *keys_to_use_for_scanning() { return &key_map_empty; } + + /* + True if changes to the table is persistent (no rollback) + This is manly used to decide how to log changes to the table in + the binary log. + */ bool has_transactions() - { return (ha_table_flags() & HA_NO_TRANSACTIONS) == 0; } + { + return ((ha_table_flags() & (HA_NO_TRANSACTIONS | HA_PERSISTENT_TABLE)) + == 0); + } + /* + True if the underlaying table doesn't support transactions + */ + bool has_transaction_manager() + { + return ((ha_table_flags() & HA_NO_TRANSACTIONS) == 0); + } /** This method is used to analyse the error to see whether the error @@ -3484,7 +3531,7 @@ public: cached */ - virtual my_bool register_query_cache_table(THD *thd, char *table_key, + virtual my_bool register_query_cache_table(THD *thd, const char *table_key, uint key_length, qc_engine_callback *engine_callback, @@ -3913,7 +3960,7 @@ public: return 0; } - LEX_STRING *engine_name() { return hton_name(ht); } + virtual LEX_CSTRING *engine_name(); TABLE* get_table() { return table; } TABLE_SHARE* get_table_share() { return table_share; } @@ -4000,11 +4047,17 @@ private: message will contain garbage. */ virtual int update_row(const uchar *old_data __attribute__((unused)), - uchar *new_data __attribute__((unused))) + const uchar *new_data __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; } + /* + Optimized function for updating the first row. Only used by sequence + tables + */ + virtual int update_first_row(uchar *new_data); + virtual int delete_row(const uchar *buf __attribute__((unused))) { return HA_ERR_WRONG_COMMAND; @@ -4067,6 +4120,7 @@ protected: enum ha_rkey_function find_flag) { return HA_ERR_WRONG_COMMAND; } friend class ha_partition; + friend class ha_sequence; public: /** This method is similar to update_row, however the handler doesn't need @@ -4081,7 +4135,7 @@ public: @retval 0 Bulk delete used by handler @retval 1 Bulk delete not used, normal operation used */ - virtual int bulk_update_row(const uchar *old_data, uchar *new_data, + virtual int bulk_update_row(const uchar *old_data, const uchar *new_data, uint *dup_key_found) { DBUG_ASSERT(FALSE); @@ -4167,11 +4221,27 @@ public: virtual handlerton *partition_ht() const { return ht; } inline int ha_write_tmp_row(uchar *buf); + inline int ha_delete_tmp_row(uchar *buf); inline int ha_update_tmp_row(const uchar * old_data, uchar * new_data); virtual void set_lock_type(enum thr_lock_type lock); friend enum icp_result handler_index_cond_check(void* h_arg); + + /** + Find unique record by index or unique constrain + + @param record record to find (also will be fillded with + actual record fields) + @param unique_ref index or unique constraiun number (depends + on what used in the engine + + @retval -1 Error + @retval 1 Not found + @retval 0 Found + */ + virtual int find_unique_row(uchar *record, uint unique_ref) + { return -1; /*unsupported */} protected: Handler_share *get_ha_share_ptr(); void set_ha_share_ptr(Handler_share *arg_ha_share); @@ -4194,7 +4264,7 @@ extern const char *myisam_stats_method_names[]; extern ulong total_ha, total_ha_2pc; /* lookups */ -plugin_ref ha_resolve_by_name(THD *thd, const LEX_STRING *name, bool tmp_table); +plugin_ref ha_resolve_by_name(THD *thd, const LEX_CSTRING *name, bool tmp_table); plugin_ref ha_lock_engine(THD *thd, const handlerton *hton); handlerton *ha_resolve_by_legacy_type(THD *thd, enum legacy_db_type db_type); handler *get_new_handler(TABLE_SHARE *share, MEM_ROOT *alloc, @@ -4262,11 +4332,11 @@ class Discovered_table_list: public handlerton::discovered_list const char *wild, *wend; bool with_temps; // whether to include temp tables in the result public: - Dynamic_array<LEX_STRING*> *tables; + Dynamic_array<LEX_CSTRING*> *tables; - Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_STRING*> *tables_arg, - const LEX_STRING *wild_arg); - Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_STRING*> *tables_arg) + Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_CSTRING*> *tables_arg, + const LEX_CSTRING *wild_arg); + Discovered_table_list(THD *thd_arg, Dynamic_array<LEX_CSTRING*> *tables_arg) : thd(thd_arg), wild(NULL), with_temps(true), tables(tables_arg) {} ~Discovered_table_list() {} @@ -4278,10 +4348,10 @@ public: }; int ha_discover_table(THD *thd, TABLE_SHARE *share); -int ha_discover_table_names(THD *thd, LEX_STRING *db, MY_DIR *dirp, +int ha_discover_table_names(THD *thd, LEX_CSTRING *db, MY_DIR *dirp, Discovered_table_list *result, bool reusable); bool ha_table_exists(THD *thd, const char *db, const char *table_name, - handlerton **hton= 0); + handlerton **hton= 0, bool *is_sequence= 0); #endif /* key cache */ @@ -4336,6 +4406,11 @@ inline const char *table_case_name(HA_CREATE_INFO *info, const char *name) return ((lower_case_table_names == 2 && info->alias) ? info->alias : name); } +typedef bool Log_func(THD*, TABLE*, bool, const uchar*, const uchar*); +int binlog_log_row(TABLE* table, + const uchar *before_record, + const uchar *after_record, + Log_func *log_func); #define TABLE_IO_WAIT(TRACKER, PSI, OP, INDEX, FLAGS, PAYLOAD) \ { \ @@ -4353,5 +4428,5 @@ void print_keydup_error(TABLE *table, KEY *key, const char *msg, myf errflag); void print_keydup_error(TABLE *table, KEY *key, myf errflag); int del_global_index_stat(THD *thd, TABLE* table, KEY* key_info); -int del_global_table_stat(THD *thd, LEX_STRING *db, LEX_STRING *table); +int del_global_table_stat(THD *thd, LEX_CSTRING *db, LEX_CSTRING *table); #endif /* HANDLER_INCLUDED */ diff --git a/sql/item.cc b/sql/item.cc index 910fdd06303..f609bfdf082 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -45,6 +45,15 @@ const String my_null_string("NULL", 4, default_charset_info); const String my_default_string("DEFAULT", 7, default_charset_info); +/* + item_empty_name is used when calling Item::set_name with NULL + pointer, to make it easier to use the name in printf. + item_used_name is used when calling Item::set_name with a 0 length + string. +*/ +const char *item_empty_name=""; +const char *item_used_name= "\0"; + static int save_field_in_field(Field *, bool *, Field *, bool); @@ -101,36 +110,6 @@ void Item::push_note_converted_to_positive_complement(THD *thd) /** - @todo - Make this functions class dependent -*/ - -bool Item::val_bool() -{ - switch(result_type()) { - case INT_RESULT: - return val_int() != 0; - case DECIMAL_RESULT: - { - my_decimal decimal_value; - my_decimal *val= val_decimal(&decimal_value); - if (val) - return !my_decimal_is_zero(val); - return 0; - } - case REAL_RESULT: - case STRING_RESULT: - return val_real() != 0.0; - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); - return 0; // Wrong (but safe) - } - return 0; // Wrong (but safe) -} - - -/** Get date/time/datetime. Optionally extend TIME result to DATETIME. */ @@ -302,6 +281,56 @@ bool Item::is_null_from_temporal() } +longlong Item::val_int_from_str(int *error) +{ + char buff[MAX_FIELD_WIDTH]; + String tmp(buff,sizeof(buff), &my_charset_bin), *res; + + /* + For a string result, we must first get the string and then convert it + to a longlong + */ + if (!(res= val_str(&tmp))) + { + *error= 0; + return 0; + } + Converter_strtoll10_with_warn cnv(NULL, Warn_filter_all(), + res->charset(), res->ptr(), res->length()); + *error= cnv.error(); + return cnv.result(); +} + + +longlong Item::val_int_signed_typecast_from_str() +{ + int error; + longlong value= val_int_from_str(&error); + if (!null_value && value < 0 && error == 0) + push_note_converted_to_negative_complement(current_thd); + return value; +} + + +longlong Item::val_int_unsigned_typecast_from_str() +{ + int error; + longlong value= val_int_from_str(&error); + if (!null_value && error < 0) + push_note_converted_to_positive_complement(current_thd); + return value; +} + + +longlong Item::val_int_unsigned_typecast_from_int() +{ + longlong value= val_int(); + if (!null_value && unsigned_flag == 0 && value < 0) + push_note_converted_to_positive_complement(current_thd); + return value; +} + + String *Item::val_string_from_date(String *str) { MYSQL_TIME ltime; @@ -419,21 +448,33 @@ longlong Item::val_int_from_decimal() return result; } -int Item::save_time_in_field(Field *field) + +longlong Item::val_int_unsigned_typecast_from_decimal() +{ + longlong result; + my_decimal tmp, *dec= val_decimal(&tmp); + if (null_value) + return 0; + my_decimal2int(E_DEC_FATAL_ERROR, dec, 1, &result); + return result; +} + + +int Item::save_time_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; if (get_time(<ime)) - return set_field_to_null_with_conversions(field, 0); + return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); return field->store_time_dec(<ime, decimals); } -int Item::save_date_in_field(Field *field) +int Item::save_date_in_field(Field *field, bool no_conversions) { MYSQL_TIME ltime; if (get_date(<ime, sql_mode_for_dates(field->table->in_use))) - return set_field_to_null_with_conversions(field, 0); + return set_field_to_null_with_conversions(field, no_conversions); field->set_notnull(); return field->store_time_dec(<ime, decimals); } @@ -471,7 +512,7 @@ int Item::save_str_value_in_field(Field *field, String *result) Item::Item(THD *thd): - is_expensive_cache(-1), rsize(0), name(0), orig_name(0), name_length(0), + is_expensive_cache(-1), rsize(0), name(null_clex_str), orig_name(0), fixed(0), is_autogenerated_name(TRUE) { DBUG_ASSERT(thd); @@ -508,14 +549,13 @@ Item::Item(THD *thd): tables. */ Item::Item(THD *thd, Item *item): - Type_std_attributes(item), + Type_all_attributes(item), join_tab_idx(item->join_tab_idx), is_expensive_cache(-1), rsize(0), str_value(item->str_value), name(item->name), orig_name(item->orig_name), - name_length(item->name_length), marker(item->marker), maybe_null(item->maybe_null), in_rollup(item->in_rollup), @@ -532,49 +572,6 @@ Item::Item(THD *thd, Item *item): } -uint Item::decimal_precision() const -{ - Item_result restype= result_type(); - - if ((restype == DECIMAL_RESULT) || (restype == INT_RESULT)) - { - uint prec= - my_decimal_length_to_precision(max_char_length(), decimals, - unsigned_flag); - return MY_MIN(prec, DECIMAL_MAX_PRECISION); - } - uint res= max_char_length(); - /* - Return at least one decimal digit, even if Item::max_char_length() - returned 0. This is important to avoid attempts to create fields of types - INT(0) or DECIMAL(0,0) when converting NULL or empty strings to INT/DECIMAL: - CREATE TABLE t1 AS SELECT CONVERT(NULL,SIGNED) AS a; - */ - return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1; -} - - -uint Item::temporal_precision(enum_field_types type_arg) -{ - if (const_item() && result_type() == STRING_RESULT && - !is_temporal_type(field_type())) - { - MYSQL_TIME ltime; - String buf, *tmp; - MYSQL_TIME_STATUS status; - DBUG_ASSERT(fixed); - if ((tmp= val_str(&buf)) && - !(type_arg == MYSQL_TYPE_TIME ? - str_to_time(tmp->charset(), tmp->ptr(), tmp->length(), - <ime, TIME_TIME_ONLY, &status) : - str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(), - <ime, TIME_FUZZY_DATES, &status))) - return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS); - } - return MY_MIN(decimals, TIME_SECOND_PART_DIGITS); -} - - void Item::print_parenthesised(String *str, enum_query_type query_type, enum precedence parent_prec) { @@ -597,11 +594,12 @@ void Item::print_item_w_name(String *str, enum_query_type query_type) { print(str, query_type); - if (name) + if (name.str) { + DBUG_ASSERT(name.length == strlen(name.str)); THD *thd= current_thd; str->append(STRING_WITH_LEN(" AS ")); - append_identifier(thd, str, name, (uint) strlen(name)); + append_identifier(thd, str, name.str, name.length); } } @@ -640,7 +638,10 @@ void Item::cleanup() marker= 0; join_tab_idx= MAX_TABLES; if (orig_name) - name= orig_name; + { + name.str= orig_name; + name.length= strlen(orig_name); + } DBUG_VOID_RETURN; } @@ -660,24 +661,6 @@ bool Item::cleanup_processor(void *arg) /** - rename item (used for views, cleanup() return original name). - - @param new_name new name of item; -*/ - -void Item::rename(char *new_name) -{ - /* - we can compare pointers to names here, because if name was not changed, - pointer will be same - */ - if (!orig_name && new_name != name) - orig_name= name; - name= new_name; -} - - -/** Traverse item tree possibly transforming it (replacing items). This function is designed to ease transformation of Item trees. @@ -746,31 +729,31 @@ Item* Item::set_expr_cache(THD *thd) Item_ident::Item_ident(THD *thd, Name_resolution_context *context_arg, const char *db_name_arg,const char *table_name_arg, - const char *field_name_arg) + const LEX_CSTRING *field_name_arg) :Item_result_field(thd), orig_db_name(db_name_arg), orig_table_name(table_name_arg), - orig_field_name(field_name_arg), context(context_arg), + orig_field_name(*field_name_arg), context(context_arg), db_name(db_name_arg), table_name(table_name_arg), - field_name(field_name_arg), + field_name(*field_name_arg), alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX), cached_table(0), depended_from(0), can_be_depended(TRUE) { - name = (char*) field_name_arg; - name_length= name ? strlen(name) : 0; + name= *field_name_arg; } -Item_ident::Item_ident(THD *thd, TABLE_LIST *view_arg, const char *field_name_arg) +Item_ident::Item_ident(THD *thd, TABLE_LIST *view_arg, + const LEX_CSTRING *field_name_arg) :Item_result_field(thd), orig_db_name(NullS), orig_table_name(view_arg->table_name), - orig_field_name(field_name_arg), context(&view_arg->view->select_lex.context), + orig_field_name(*field_name_arg), + context(&view_arg->view->select_lex.context), db_name(NullS), table_name(view_arg->alias), - field_name(field_name_arg), + field_name(*field_name_arg), alias_name_used(FALSE), cached_field_index(NO_CACHED_FIELD_INDEX), cached_table(NULL), depended_from(NULL), can_be_depended(TRUE) { - name = (char*) field_name_arg; - name_length= name ? strlen(name) : 0; + name= *field_name_arg; } @@ -861,7 +844,8 @@ bool Item_ident::collect_outer_ref_processor(void *param) bool Item_field::collect_item_field_processor(void *arg) { DBUG_ENTER("Item_field::collect_item_field_processor"); - DBUG_PRINT("info", ("%s", field->field_name ? field->field_name : "noname")); + DBUG_PRINT("info", ("%s", field->field_name.str ? + field->field_name.str : "noname")); List<Item_field> *item_list= (List<Item_field>*) arg; List_iterator<Item_field> item_list_it(*item_list); Item_field *curr_item; @@ -878,7 +862,8 @@ bool Item_field::collect_item_field_processor(void *arg) bool Item_field::add_field_to_set_processor(void *arg) { DBUG_ENTER("Item_field::add_field_to_set_processor"); - DBUG_PRINT("info", ("%s", field->field_name ? field->field_name : "noname")); + DBUG_PRINT("info", ("%s", field->field_name.str ? field->field_name.str : + "noname")); TABLE *table= (TABLE *) arg; if (field->table == table) bitmap_set_bit(&table->tmp_set, field->field_index); @@ -1006,7 +991,7 @@ bool Item_field::check_field_expression_processor(void *arg) { my_error(ER_EXPRESSION_REFERS_TO_UNINIT_FIELD, MYF(0), - org_field->field_name, field->field_name); + org_field->field_name.str, field->field_name.str); return 1; } } @@ -1037,13 +1022,141 @@ bool Item::check_cols(uint c) } +bool Item::check_type_or_binary(const char *opname, + const Type_handler *expect) const +{ + const Type_handler *handler= type_handler(); + if (handler == expect || + (handler->is_general_purpose_string_type() && + collation.collation == &my_charset_bin)) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_general_purpose_string(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->is_general_purpose_string_type()) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_traditional_scalar(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->is_traditional_type() && handler->is_scalar_type()) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_can_return_int(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->can_return_int()) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_can_return_decimal(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->can_return_decimal()) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_can_return_real(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->can_return_real()) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_can_return_date(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->can_return_date()) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_can_return_time(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->can_return_time()) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_can_return_str(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->can_return_str()) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_can_return_text(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->can_return_text()) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + handler->name().ptr(), opname); + return true; +} + + +bool Item::check_type_scalar(const char *opname) const +{ + const Type_handler *handler= type_handler(); + if (handler->is_scalar_type()) + return false; + my_error(ER_OPERAND_COLUMNS, MYF(0), 1); + return true; +} + + void Item::set_name(THD *thd, const char *str, uint length, CHARSET_INFO *cs) { if (!length) { - /* Empty string, used by AS or internal function like last_insert_id() */ - name= (char*) str; - name_length= 0; + /* + Null string are replaced by item_empty_name. This is used by AS or + internal function like last_insert_id() to detect if we need to + change the name later. + Used by sql_yacc.yy in select_alias handling + */ + name.str= str ? item_used_name : item_empty_name; + name.length= 0; return; } @@ -1085,13 +1198,13 @@ void Item::set_name(THD *thd, const char *str, uint length, CHARSET_INFO *cs) if (!my_charset_same(cs, system_charset_info)) { size_t res_length; - name= sql_strmake_with_convert(thd, str, length, cs, - MAX_ALIAS_NAME, system_charset_info, - &res_length); - name_length= res_length; + name.str= sql_strmake_with_convert(thd, str, length, cs, + MAX_ALIAS_NAME, system_charset_info, + &res_length); + name.length= res_length; } else - name= thd->strmake(str, (name_length= MY_MIN(length,MAX_ALIAS_NAME))); + name.str= thd->strmake(str, (name.length= MY_MIN(length,MAX_ALIAS_NAME))); } @@ -1101,28 +1214,13 @@ void Item::set_name_no_truncate(THD *thd, const char *str, uint length, if (!my_charset_same(cs, system_charset_info)) { size_t res_length; - name= sql_strmake_with_convert(thd, str, length, cs, - UINT_MAX, system_charset_info, - &res_length); - name_length= res_length; + name.str= sql_strmake_with_convert(thd, str, length, cs, + UINT_MAX, system_charset_info, + &res_length); + name.length= res_length; } else - name= thd->strmake(str, (name_length= length)); -} - - -void Item::set_name_for_rollback(THD *thd, const char *str, uint length, - CHARSET_INFO *cs) -{ - char *old_name, *new_name; - old_name= name; - set_name(thd, str, length, cs); - new_name= name; - if (old_name != new_name) - { - name= old_name; - thd->change_item_tree((Item **) &name, (Item *) new_name); - } + name.str= thd->strmake(str, (name.length= length)); } @@ -1140,8 +1238,9 @@ bool Item::eq(const Item *item, bool binary_cmp) const for all basic constants we have special checks, and Item_param's type() can be only among basic constant types. */ - return type() == item->type() && name && item->name && - !my_strcasecmp(system_charset_info,name,item->name); + return type() == item->type() && name.str && item->name.str && + name.length == item->name.length && + !my_strcasecmp(system_charset_info, name.str, item->name.str); } @@ -1407,6 +1506,25 @@ bool Item::get_seconds(ulonglong *sec, ulong *sec_part) return my_decimal2seconds(dec, sec, sec_part); } +const MY_LOCALE *Item::locale_from_val_str() +{ + StringBuffer<MAX_FIELD_WIDTH> tmp; + String *locale_name= val_str_ascii(&tmp); + const MY_LOCALE *lc; + if (!locale_name || + !(lc= my_locale_by_name(locale_name->c_ptr_safe()))) + { + THD *thd= current_thd; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_UNKNOWN_LOCALE, + ER_THD(thd, ER_UNKNOWN_LOCALE), + locale_name ? locale_name->c_ptr_safe() : "NULL"); + lc= &my_locale_en_US; + } + return lc; +} + + CHARSET_INFO *Item::default_charset() { return current_thd->variables.collation_connection; @@ -1479,28 +1597,61 @@ bool mark_unsupported_function(const char *w1, const char *w2, return mark_unsupported_function(ptr, store, result); } + +Query_fragment::Query_fragment(THD *thd, sp_head *sphead, + const char *start, const char *end) +{ + DBUG_ASSERT(start <= end); + if (sphead) + { + if (sphead->m_tmp_query) + { + // Normal SP statement + DBUG_ASSERT(sphead->m_tmp_query <= start); + set(start - sphead->m_tmp_query, end - start); + } + else + { + /* + We're in the "if" expression of a compound query: + if (expr) + do_something; + end if; + sphead->m_tmp_query is not set yet at this point, because + the "if" part of such statements is never put into the binary log. + Values of Rewritable_query_parameter::pos_in_query and + Rewritable_query_parameter:len_in_query will not be important, + so setting both to 0 should be fine. + */ + set(0, 0); + } + } + else + { + // Non-SP statement + DBUG_ASSERT(thd->query() <= start); + DBUG_ASSERT(end <= thd->query_end()); + set(start - thd->query(), end - start); + } +} + + /***************************************************************************** Item_sp_variable methods *****************************************************************************/ -Item_sp_variable::Item_sp_variable(THD *thd, char *sp_var_name_str, - uint sp_var_name_length): - Item(thd), m_thd(0) +Item_sp_variable::Item_sp_variable(THD *thd, const LEX_CSTRING *sp_var_name) + :Item(thd), m_thd(0), m_name(*sp_var_name) #ifndef DBUG_OFF , m_sp(0) #endif { - m_name.str= sp_var_name_str; - m_name.length= sp_var_name_length; } -bool Item_sp_variable::fix_fields(THD *thd, Item **) +bool Item_sp_variable::fix_fields_from_item(THD *thd, Item **, const Item *it) { - Item *it; - m_thd= thd; /* NOTE: this must be set before any this_xxx() */ - it= this_item(); DBUG_ASSERT(it->fixed); @@ -1585,16 +1736,26 @@ bool Item_sp_variable::is_null() return this_item()->is_null(); } +void Item_sp_variable::make_field(THD *thd, Send_field *field) +{ + Item *it= this_item(); + + it->make_field(thd, field); + if (name.str) + field->col_name= name; + else + field->col_name= m_name; +} /***************************************************************************** Item_splocal methods *****************************************************************************/ -Item_splocal::Item_splocal(THD *thd, const LEX_STRING &sp_var_name, +Item_splocal::Item_splocal(THD *thd, const LEX_CSTRING *sp_var_name, uint sp_var_idx, enum_field_types sp_var_type, uint pos_in_q, uint len_in_q): - Item_sp_variable(thd, sp_var_name.str, sp_var_name.length), + Item_sp_variable(thd, sp_var_name), Rewritable_query_parameter(pos_in_q, len_in_q), m_var_idx(sp_var_idx) { @@ -1606,11 +1767,19 @@ Item_splocal::Item_splocal(THD *thd, const LEX_STRING &sp_var_name, } +bool Item_splocal::fix_fields(THD *thd, Item **ref) +{ + Item *item= thd->spcont->get_item(m_var_idx); + set_handler(item->type_handler()); + return fix_fields_from_item(thd, ref, item); +} + + Item * Item_splocal::this_item() { DBUG_ASSERT(m_sp == m_thd->spcont->sp); - + DBUG_ASSERT(fixed); return m_thd->spcont->get_item(m_var_idx); } @@ -1618,7 +1787,7 @@ const Item * Item_splocal::this_item() const { DBUG_ASSERT(m_sp == m_thd->spcont->sp); - + DBUG_ASSERT(fixed); return m_thd->spcont->get_item(m_var_idx); } @@ -1627,7 +1796,7 @@ Item ** Item_splocal::this_item_addr(THD *thd, Item **) { DBUG_ASSERT(m_sp == thd->spcont->sp); - + DBUG_ASSERT(fixed); return thd->spcont->get_item_addr(m_var_idx); } @@ -1647,17 +1816,195 @@ bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it) } +/** + These two declarations are different: + x INT; + ROW(x INT); + A ROW with one elements should not be comparable to scalar value. + + TODO: Currently we don't support one argument with the function ROW(), so + this query returns a syntax error, meaning that more arguments are expected: + SELECT ROW(1); + + Therefore, all around the code we assume that cols()==1 means a scalar value + and cols()>1 means a ROW value. With adding ROW SP variables this + assumption is not true any more. ROW variables with one element are + now possible. + + To implement Item::check_cols() correctly, we now should extend it to + know if a ROW or a scalar value is being tested. For example, + these new prototypes should work: + virtual bool check_cols(Item_result result, uint c); + or + virtual bool check_cols(const Type_handler *type, uint c); + + The current implementation of Item_splocal::check_cols() is a compromise + that should be more or less fine until we extend check_cols(). + It disallows ROW variables to appear in a scalar context. + The "|| n == 1" part of the conditon is responsible for this. + For example, it disallows ROW variables to appear in SELECT list: + +DELIMITER $$; +CREATE PROCEDURE p1() +AS + a ROW (a INT); +BEGIN + SELECT a; +END; +$$ +DELIMITER ;$$ +--error ER_OPERAND_COLUMNS +CALL p1(); + + But is produces false negatives with ROW variables consisting of one element. + For example, this script fails: + +SET sql_mode=ORACLE; +DROP PROCEDURE IF EXISTS p1; +DELIMITER $$ +CREATE PROCEDURE p1 +AS + a ROW(a INT); + b ROW(a INT); +BEGIN + SELECT a=b; +END; +$$ +DELIMITER ; +CALL p1(); + + and returns "ERROR 1241 (21000): Operand should contain 1 column(s)". + This will be fixed that we change check_cols(). +*/ + +bool Item_splocal::check_cols(uint n) +{ + DBUG_ASSERT(m_thd->spcont); + if (Type_handler_hybrid_field_type::cmp_type() != ROW_RESULT) + return Item::check_cols(n); + + if (n != this_item()->cols() || n == 1) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), n); + return true; + } + return false; +} + + +bool Item_splocal_row_field::fix_fields(THD *thd, Item **ref) +{ + Item *item= thd->spcont->get_item(m_var_idx)->element_index(m_field_idx); + return fix_fields_from_item(thd, ref, item); +} + + +Item * +Item_splocal_row_field::this_item() +{ + DBUG_ASSERT(m_sp == m_thd->spcont->sp); + DBUG_ASSERT(fixed); + return m_thd->spcont->get_item(m_var_idx)->element_index(m_field_idx); +} + + +const Item * +Item_splocal_row_field::this_item() const +{ + DBUG_ASSERT(m_sp == m_thd->spcont->sp); + DBUG_ASSERT(fixed); + return m_thd->spcont->get_item(m_var_idx)->element_index(m_field_idx); +} + + +Item ** +Item_splocal_row_field::this_item_addr(THD *thd, Item **) +{ + DBUG_ASSERT(m_sp == thd->spcont->sp); + DBUG_ASSERT(fixed); + return thd->spcont->get_item(m_var_idx)->addr(m_field_idx); +} + + +void Item_splocal_row_field::print(String *str, enum_query_type) +{ + str->reserve(m_name.length + m_field_name.length + 8); + str->append(m_name.str, m_name.length); + str->append('.'); + str->append(m_field_name.str, m_field_name.length); + str->append('@'); + str->qs_append(m_var_idx); + str->append('['); + str->qs_append(m_field_idx); + str->append(']'); +} + + +bool Item_splocal_row_field::set_value(THD *thd, sp_rcontext *ctx, Item **it) +{ + return ctx->set_variable_row_field(thd, m_var_idx, m_field_idx, it); +} + + +bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it) +{ + m_thd= thd; + Item *item, *row= m_thd->spcont->get_item(m_var_idx); + if (row->element_index_by_name(&m_field_idx, m_field_name)) + { + my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0), + m_name.str, m_field_name.str); + return true; + } + item= row->element_index(m_field_idx); + set_handler(item->type_handler()); + return fix_fields_from_item(thd, it, item); +} + + +void Item_splocal_row_field_by_name::print(String *str, enum_query_type) +{ + // +16 should be enough for .NNN@[""] + if (str->reserve(m_name.length + 2 * m_field_name.length + 16)) + return; + str->qs_append(m_name.str, m_name.length); + str->qs_append('.'); + str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append('@'); + str->qs_append(m_var_idx); + str->qs_append("[\"", 2); + str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append("\"]", 2); +} + + +bool Item_splocal_row_field_by_name::set_value(THD *thd, sp_rcontext *ctx, Item **it) +{ + DBUG_ASSERT(fixed); // Make sure m_field_idx is already set + return Item_splocal_row_field::set_value(thd, ctx, it); +} + + /***************************************************************************** Item_case_expr methods *****************************************************************************/ +LEX_CSTRING str_case_expr= { STRING_WITH_LEN("case_expr") }; + Item_case_expr::Item_case_expr(THD *thd, uint case_expr_id): - Item_sp_variable(thd, C_STRING_WITH_LEN("case_expr")), + Item_sp_variable(thd, &str_case_expr), m_case_expr_id(case_expr_id) { } +bool Item_case_expr::fix_fields(THD *thd, Item **ref) +{ + Item *item= thd->spcont->get_case_expr(m_case_expr_id); + return fix_fields_from_item(thd, ref, item); +} + + Item * Item_case_expr::this_item() { @@ -1825,7 +2172,7 @@ bool Item_name_const::fix_fields(THD *thd, Item **ref) } if (is_autogenerated_name) { - set_name(thd, item_name->ptr(), (uint) item_name->length(), + set_name(thd, item_name->c_ptr(), (uint) item_name->length(), system_charset_info); } if (value_item->collation.derivation == DERIVATION_NUMERIC) @@ -1859,7 +2206,7 @@ class Item_aggregate_ref : public Item_ref public: Item_aggregate_ref(THD *thd, Name_resolution_context *context_arg, Item **item, const char *table_name_arg, - const char *field_name_arg): + const LEX_CSTRING *field_name_arg): Item_ref(thd, context_arg, item, table_name_arg, field_name_arg) {} virtual inline void print (String *str, enum_query_type query_type) @@ -1976,14 +2323,14 @@ void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array, already a reference. */ Item *real_itm= real_item(); - ref_pointer_array[el]= real_itm; if (type() == WINDOW_FUNC_ITEM) { if (!(item_ref= (new (thd->mem_root) Item_direct_ref(thd, - &thd->lex->current_select->context, - &ref_pointer_array[el], 0, name)))) + &thd->lex->current_select->context, + &ref_pointer_array[el], 0, + &name)))) return; // fatal_error is set } else @@ -1991,7 +2338,8 @@ void Item::split_sum_func2(THD *thd, Ref_ptr_array ref_pointer_array, if (!(item_ref= (new (thd->mem_root) Item_aggregate_ref(thd, &thd->lex->current_select->context, - &ref_pointer_array[el], 0, name)))) + &ref_pointer_array[el], 0, + &name)))) return; // fatal_error is set } if (type() == SUM_FUNC_ITEM) @@ -2196,9 +2544,9 @@ void my_coll_agg_error(Item** args, uint count, const char *fname, } -bool Item_func_or_sum::agg_item_collations(DTCollation &c, const char *fname, - Item **av, uint count, - uint flags, int item_sep) +bool Type_std_attributes::agg_item_collations(DTCollation &c, const char *fname, + Item **av, uint count, + uint flags, int item_sep) { uint i; Item **arg; @@ -2243,10 +2591,10 @@ bool Item_func_or_sum::agg_item_collations(DTCollation &c, const char *fname, } -bool Item_func_or_sum::agg_item_set_converter(const DTCollation &coll, - const char *fname, - Item **args, uint nargs, - uint flags, int item_sep) +bool Type_std_attributes::agg_item_set_converter(const DTCollation &coll, + const char *fname, + Item **args, uint nargs, + uint flags, int item_sep) { Item **arg, *safe_args[2]= {NULL, NULL}; @@ -2405,7 +2753,6 @@ void Item_ident_for_show::make_field(THD *thd, Send_field *tmp_field) tmp_field->table_name= tmp_field->org_table_name= table_name; tmp_field->db_name= db_name; tmp_field->col_name= tmp_field->org_col_name= field->field_name; - tmp_field->charsetnr= field->charset()->number; tmp_field->length=field->field_length; tmp_field->type=field->type(); tmp_field->flags= field->table->maybe_null ? @@ -2416,7 +2763,7 @@ void Item_ident_for_show::make_field(THD *thd, Send_field *tmp_field) /**********************************************/ Item_field::Item_field(THD *thd, Field *f) - :Item_ident(thd, 0, NullS, *f->table_name, f->field_name), + :Item_ident(thd, 0, NullS, *f->table_name, &f->field_name), item_equal(0), have_privileges(0), any_privileges(0) { @@ -2425,7 +2772,8 @@ Item_field::Item_field(THD *thd, Field *f) field_name and table_name should not point to garbage if this item is to be reused */ - orig_table_name= orig_field_name= ""; + orig_table_name= ""; + orig_field_name= null_clex_str; with_field= 1; } @@ -2439,7 +2787,8 @@ Item_field::Item_field(THD *thd, Field *f) Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, Field *f) - :Item_ident(thd, context_arg, f->table->s->db.str, *f->table_name, f->field_name), + :Item_ident(thd, context_arg, f->table->s->db.str, *f->table_name, + &f->field_name), item_equal(0), have_privileges(0), any_privileges(0) { @@ -2465,14 +2814,15 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, orig_db_name= thd->strdup(db_name); if (table_name) orig_table_name= thd->strdup(table_name); - if (field_name) - orig_field_name= thd->strdup(field_name); + if (field_name.str) + thd->make_lex_string(&orig_field_name, field_name.str, + field_name.length); /* We don't restore 'name' in cleanup because it's not changed during execution. Still we need it to point to persistent memory if this item is to be reused. */ - name= (char*) orig_field_name; + name= orig_field_name; } set_field(f); with_field= 1; @@ -2481,7 +2831,7 @@ Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, Item_field::Item_field(THD *thd, Name_resolution_context *context_arg, const char *db_arg,const char *table_name_arg, - const char *field_name_arg) + const LEX_CSTRING *field_name_arg) :Item_ident(thd, context_arg, db_arg, table_name_arg, field_name_arg), field(0), item_equal(0), have_privileges(0), any_privileges(0) @@ -2567,15 +2917,11 @@ void Item_field::set_field(Field *field_par) { field=result_field=field_par; // for easy coding with fields maybe_null=field->maybe_null(); - decimals= field->decimals(); + Type_std_attributes::set(field_par); table_name= *field_par->table_name; field_name= field_par->field_name; db_name= field_par->table->s->db.str; alias_name_used= field_par->table->alias_name_used; - unsigned_flag= MY_TEST(field_par->flags & UNSIGNED_FLAG); - collation.set(field_par->charset(), field_par->derivation(), - field_par->repertoire()); - fix_char_length(field_par->char_length()); max_length= adjust_max_effective_column_length(field_par, max_length); @@ -2595,7 +2941,7 @@ void Item_field::reset_field(Field *f) { set_field(f); /* 'name' is pointing at field->field_name of old field */ - name= (char*) f->field_name; + name= f->field_name; } @@ -2634,15 +2980,15 @@ bool Item_field::switch_to_nullable_fields_processor(void *arg) const char *Item_ident::full_name() const { char *tmp; - if (!table_name || !field_name) - return field_name ? field_name : name ? name : "tmp_field"; + if (!table_name || !field_name.str) + return field_name.str ? field_name.str : name.str ? name.str : "tmp_field"; if (db_name && db_name[0]) { THD *thd= current_thd; tmp=(char*) thd->alloc((uint) strlen(db_name)+(uint) strlen(table_name)+ - (uint) strlen(field_name)+3); - strxmov(tmp,db_name,".",table_name,".",field_name,NullS); + (uint) field_name.length+3); + strxmov(tmp,db_name,".",table_name,".",field_name.str,NullS); } else { @@ -2650,11 +2996,11 @@ const char *Item_ident::full_name() const { THD *thd= current_thd; tmp= (char*) thd->alloc((uint) strlen(table_name) + - (uint) strlen(field_name) + 2); - strxmov(tmp, table_name, ".", field_name, NullS); + field_name.length + 2); + strxmov(tmp, table_name, ".", field_name.str, NullS); } else - tmp= (char*) field_name; + return field_name.str; } return tmp; } @@ -2691,7 +3037,7 @@ void Item_ident::print(String *str, enum_query_type query_type) use_db_name= use_table_name= false; } - if (!field_name || !field_name[0]) + if (!field_name.str || !field_name.str[0]) { append_identifier(thd, str, STRING_WITH_LEN("tmp_field")); return; @@ -2725,7 +3071,7 @@ void Item_ident::print(String *str, enum_query_type query_type) append_identifier(thd, str, t_name, (uint) strlen(t_name)); str->append('.'); } - append_identifier(thd, str, field_name, (uint) strlen(field_name)); + append_identifier(thd, str, field_name.str, field_name.length); } /* ARGSUSED */ @@ -2856,8 +3202,8 @@ bool Item_field::eq(const Item *item, bool binary_cmp) const (In cases where we would choose wrong we would have to generate a ER_NON_UNIQ_ERROR). */ - return (!my_strcasecmp(system_charset_info, item_field->name, - field_name) && + return (!my_strcasecmp(system_charset_info, item_field->name.str, + field_name.str) && (!item_field->table_name || !table_name || (!my_strcasecmp(table_alias_charset, item_field->table_name, table_name) && @@ -2984,7 +3330,12 @@ Item_int::Item_int(THD *thd, const char *str_arg, uint length): int error; value= my_strtoll10(str_arg, &end_ptr, &error); max_length= (uint) (end_ptr - str_arg); - name= (char*) str_arg; + name.str= str_arg; + /* + We can't trust max_length as in show_routine_code we are using "Pos" as + the field name. + */ + name.length= !str_arg[max_length] ? max_length : strlen(str_arg); fixed= 1; } @@ -3014,7 +3365,7 @@ void Item_int::print(String *str, enum_query_type query_type) Item *Item_bool::neg_transformer(THD *thd) { value= !value; - name= 0; + name= null_clex_str; return this; } @@ -3055,7 +3406,8 @@ Item_decimal::Item_decimal(THD *thd, const char *str_arg, uint length, Item_num(thd) { str2my_decimal(E_DEC_FATAL_ERROR, str_arg, length, charset, &decimal_value); - name= (char*) str_arg; + name.str= str_arg; + name.length= safe_strlen(str_arg); decimals= (uint8) decimal_value.frac; fixed= 1; max_length= my_decimal_precision_to_length_no_truncation(decimal_value.intg + @@ -3095,7 +3447,8 @@ Item_decimal::Item_decimal(THD *thd, const char *str, const my_decimal *val_arg, Item_num(thd) { my_decimal2decimal(val_arg, &decimal_value); - name= (char*) str; + name.str= str; + name.length= safe_strlen(str); decimals= (uint8) decimal_par; max_length= length; fixed= 1; @@ -3187,7 +3540,7 @@ void Item_decimal::set_decimal_value(my_decimal *value_par) Item *Item_decimal::clone_item(THD *thd) { - return new (thd->mem_root) Item_decimal(thd, name, &decimal_value, decimals, + return new (thd->mem_root) Item_decimal(thd, name.str, &decimal_value, decimals, max_length); } @@ -3212,7 +3565,7 @@ my_decimal *Item_float::val_decimal(my_decimal *decimal_value) Item *Item_float::clone_item(THD *thd) { - return new (thd->mem_root) Item_float(thd, name, value, decimals, + return new (thd->mem_root) Item_float(thd, name.str, value, decimals, max_length); } @@ -3323,7 +3676,7 @@ Item *Item_null::safe_charset_converter(THD *thd, CHARSET_INFO *tocs) Item *Item_null::clone_item(THD *thd) { - return new (thd->mem_root) Item_null(thd, name); + return new (thd->mem_root) Item_null(thd, name.str); } /*********************** Item_param related ******************************/ @@ -3342,10 +3695,20 @@ default_set_param_func(Item_param *param, } -Item_param::Item_param(THD *thd, uint pos_in_query_arg): +Item_param::Item_param(THD *thd, const LEX_CSTRING *name_arg, + uint pos_in_query_arg, uint len_in_query_arg): Item_basic_value(thd), - Rewritable_query_parameter(pos_in_query_arg, 1), - Type_handler_hybrid_field_type(MYSQL_TYPE_VARCHAR), + Rewritable_query_parameter(pos_in_query_arg, len_in_query_arg), + /* + Set handler to type_handler_null. Its data type test methods such as: + - is_scalar_type() + - can_return_int() + - can_return_real(), + - is_general_purpose_string_type() + all return "true". This is needed to avoid any "illegal parameter type" + errors in Item::check_type_xxx() at PS prepare time. + */ + Type_handler_hybrid_field_type(&type_handler_null), state(NO_VALUE), /* Don't pretend to be a literal unless value for this item is set. */ item_type(PARAM_ITEM), @@ -3361,8 +3724,8 @@ Item_param::Item_param(THD *thd, uint pos_in_query_arg): */ m_is_settable_routine_parameter(true) { - name= (char*) "?"; - /* + name= *name_arg; + /* Since we can't say whenever this item can be NULL or cannot be NULL before mysql_stmt_execute(), so we assuming that it can be NULL until value is set. @@ -3621,47 +3984,10 @@ bool Item_param::set_from_item(THD *thd, Item *item) } } struct st_value tmp; - if (!item->store(&tmp, 0)) + if (!item->save_in_value(&tmp)) { - unsigned_flag= item->unsigned_flag; - switch (item->cmp_type()) { - case REAL_RESULT: - set_double(tmp.value.m_double); - set_handler_by_field_type(MYSQL_TYPE_DOUBLE); - break; - case INT_RESULT: - set_int(tmp.value.m_longlong, MY_INT64_NUM_DECIMAL_DIGITS); - set_handler_by_field_type(MYSQL_TYPE_LONGLONG); - break; - case STRING_RESULT: - { - value.cs_info.set(thd, item->collation.collation); - /* - Exact value of max_length is not known unless data is converted to - charset of connection, so we have to set it later. - */ - set_handler_by_field_type(MYSQL_TYPE_VARCHAR); - - if (set_str(tmp.m_string.ptr(), tmp.m_string.length())) - DBUG_RETURN(1); - break; - } - case DECIMAL_RESULT: - { - set_decimal(&tmp.m_decimal, unsigned_flag); - set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL); - break; - } - case TIME_RESULT: - { - set_time(&tmp.value.m_time, item->max_length, item->decimals); - set_handler(item->type_handler()); - break; - } - case ROW_RESULT: - DBUG_ASSERT(0); - set_null(); - } + if (item->type_handler()->Item_param_set_from_value(thd, this, item, &tmp)) + DBUG_RETURN(true); } else set_null(); @@ -4053,19 +4379,19 @@ Item_param::clone_item(THD *thd) invalid_default_param(); // fall through case NULL_VALUE: - return new (mem_root) Item_null(thd, name); + return new (mem_root) Item_null(thd, name.str); case INT_VALUE: return (unsigned_flag ? - new (mem_root) Item_uint(thd, name, value.integer, max_length) : - new (mem_root) Item_int(thd, name, value.integer, max_length)); + new (mem_root) Item_uint(thd, name.str, value.integer, max_length) : + new (mem_root) Item_int(thd, name.str, value.integer, max_length)); case REAL_VALUE: - return new (mem_root) Item_float(thd, name, value.real, decimals, + return new (mem_root) Item_float(thd, name.str, value.real, decimals, max_length); case DECIMAL_VALUE: return 0; // Should create Item_decimal. See MDEV-11361. case STRING_VALUE: case LONG_DATA_VALUE: - return new (mem_root) Item_string(thd, name, str_value.c_ptr_quick(), + return new (mem_root) Item_string(thd, name.str, str_value.c_ptr_quick(), str_value.length(), str_value.charset(), collation.derivation, collation.repertoire); @@ -4341,7 +4667,6 @@ void Item_param::make_field(THD *thd, Send_field *field) field->org_col_name= m_out_param_info->org_col_name; field->length= m_out_param_info->length; - field->charsetnr= m_out_param_info->charsetnr; field->flags= m_out_param_info->flags; field->decimals= m_out_param_info->decimals; field->type= m_out_param_info->type; @@ -4699,7 +5024,7 @@ static bool mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current, ER_THD(thd,ER_WARN_FIELD_RESOLVED), db_name, (db_name[0] ? "." : ""), table_name, (table_name [0] ? "." : ""), - resolved_item->field_name, + resolved_item->field_name.str, current->select_number, last->select_number); } DBUG_RETURN(FALSE); @@ -4789,7 +5114,7 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) { const char *db_name; const char *table_name; - const char *field_name; + LEX_CSTRING *field_name; ORDER *found_group= NULL; int found_match_degree= 0; char name_buff[SAFE_NAME_LEN+1]; @@ -4799,7 +5124,7 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) { db_name= ((Item_ident*) find_item)->db_name; table_name= ((Item_ident*) find_item)->table_name; - field_name= ((Item_ident*) find_item)->field_name; + field_name= &((Item_ident*) find_item)->field_name; } else return NULL; @@ -4812,17 +5137,17 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) db_name= name_buff; } - DBUG_ASSERT(field_name != 0); + DBUG_ASSERT(field_name->str != 0); for (ORDER *cur_group= group_list ; cur_group ; cur_group= cur_group->next) { int cur_match_degree= 0; /* SELECT list element with explicit alias */ - if ((*(cur_group->item))->name && !table_name && + if ((*(cur_group->item))->name.str && !table_name && !(*(cur_group->item))->is_autogenerated_name && !my_strcasecmp(system_charset_info, - (*(cur_group->item))->name, field_name)) + (*(cur_group->item))->name.str, field_name->str)) { ++cur_match_degree; } @@ -4833,12 +5158,12 @@ static Item** find_field_in_group_list(Item *find_item, ORDER *group_list) Item_ident *cur_field= (Item_ident*) *cur_group->item; const char *l_db_name= cur_field->db_name; const char *l_table_name= cur_field->table_name; - const char *l_field_name= cur_field->field_name; + LEX_CSTRING *l_field_name= &cur_field->field_name; - DBUG_ASSERT(l_field_name != 0); + DBUG_ASSERT(l_field_name->str != 0); if (!my_strcasecmp(system_charset_info, - l_field_name, field_name)) + l_field_name->str, field_name->str)) ++cur_match_degree; else continue; @@ -4974,7 +5299,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) the strict mode is enabled. */ my_error(ER_NON_GROUPING_FIELD_USED, MYF(0), - ref->name, "HAVING"); + ref->name.str, "HAVING"); return NULL; } if (select_ref != not_found_item || group_by_ref) @@ -4985,7 +5310,7 @@ resolve_ref_in_select_and_group(THD *thd, Item_ident *ref, SELECT_LEX *select) if (!select->ref_pointer_array[counter]) { my_error(ER_ILLEGAL_REFERENCE, MYF(0), - ref->name, "forward reference in item list"); + ref->name.str, "forward reference in item list"); return NULL; } DBUG_ASSERT((*select_ref)->fixed); @@ -5318,15 +5643,15 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) *ref= NULL; // Don't call set_properties() rf= (place == IN_HAVING ? new (thd->mem_root) - Item_ref(thd, context, ref, (char*) table_name, - (char*) field_name, alias_name_used) : + Item_ref(thd, context, ref, table_name, + &field_name, alias_name_used) : (!select->group_list.elements ? new (thd->mem_root) - Item_direct_ref(thd, context, ref, (char*) table_name, - (char*) field_name, alias_name_used) : + Item_direct_ref(thd, context, ref, table_name, + &field_name, alias_name_used) : new (thd->mem_root) - Item_outer_ref(thd, context, ref, (char*) table_name, - (char*) field_name, alias_name_used))); + Item_outer_ref(thd, context, ref, table_name, + &field_name, alias_name_used))); *ref= save; if (!rf) return -1; @@ -5370,9 +5695,10 @@ Item_field::fix_outer_field(THD *thd, Field **from_field, Item **reference) if (last_checked_context->select_lex->having_fix_field) { Item_ref *rf; - rf= new (thd->mem_root) Item_ref(thd, context, (*from_field)->table->s->db.str, - (*from_field)->table->alias.c_ptr(), - (char*) field_name); + rf= new (thd->mem_root) Item_ref(thd, context, + (*from_field)->table->s->db.str, + (*from_field)->table->alias.c_ptr(), + &field_name); if (!rf) return -1; thd->change_item_tree(reference, rf); @@ -5490,7 +5816,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) if (new_field == NULL) { /* The column to which we link isn't valid. */ - my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name, + my_error(ER_BAD_FIELD_ERROR, MYF(0), (*res)->name.str, thd->where); return(1); } @@ -5511,8 +5837,7 @@ bool Item_field::fix_fields(THD *thd, Item **reference) Item_field created by the parser with the new Item_ref. */ Item_ref *rf= new (thd->mem_root) - Item_ref(thd, context, db_name, table_name, - field_name); + Item_ref(thd, context, db_name, table_name, &field_name); if (!rf) return 1; bool err= rf->fix_fields(thd, (Item **) &rf) || rf->check_cols(1); @@ -5610,16 +5935,16 @@ bool Item_field::fix_fields(THD *thd, Item **reference) #ifndef NO_EMBEDDED_ACCESS_CHECKS if (any_privileges) { - char *db, *tab; + const char *db, *tab; db= field->table->s->db.str; tab= field->table->s->table_name.str; if (!(have_privileges= (get_column_grant(thd, &field->table->grant, - db, tab, field_name) & + db, tab, field_name.str) & VIEW_ANY_ACL))) { my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "ANY", thd->security_ctx->priv_user, - thd->security_ctx->host_or_ip, field_name, tab); + thd->security_ctx->host_or_ip, field_name.str, tab); goto error; } } @@ -5681,6 +6006,7 @@ error: return TRUE; } + /* @brief Mark virtual columns as used in a partitioning expression @@ -5871,7 +6197,8 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg) comparison context, and it's safe to replace it to the constant from item_equal. */ - DBUG_ASSERT(cmp_type() == item_equal->compare_type()); + DBUG_ASSERT(type_handler()->type_handler_for_comparison()->cmp_type() == + item_equal->compare_type_handler()->cmp_type()); return const_item2; } Item_field *subst= @@ -5888,13 +6215,11 @@ Item *Item_field::replace_equal_field(THD *thd, uchar *arg) void Item::init_make_field(Send_field *tmp_field, enum enum_field_types field_type_arg) { - char *empty_name= (char*) ""; - tmp_field->db_name= empty_name; - tmp_field->org_table_name= empty_name; - tmp_field->org_col_name= empty_name; - tmp_field->table_name= empty_name; - tmp_field->col_name= name; - tmp_field->charsetnr= collation.collation->number; + tmp_field->db_name= ""; + tmp_field->org_table_name= ""; + tmp_field->org_col_name= empty_clex_str; + tmp_field->table_name= ""; + tmp_field->col_name= name; tmp_field->flags= (maybe_null ? 0 : NOT_NULL_FLAG) | (my_binary_compare(charset_for_protocol()) ? BINARY_FLAG : 0); @@ -5913,7 +6238,7 @@ void Item::make_field(THD *thd, Send_field *tmp_field) void Item_empty_string::make_field(THD *thd, Send_field *tmp_field) { - init_make_field(tmp_field, string_field_type()); + init_make_field(tmp_field, string_type_handler()->field_type()); } @@ -6047,180 +6372,16 @@ bool Item::eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs) } -/** - Create a field to hold a string value from an item. - - If too_big_for_varchar() create a blob @n - If max_length > 0 create a varchar @n - If max_length == 0 create a CHAR(0) - - @param table Table for which the field is created -*/ - -Field *Item::make_string_field(TABLE *table) -{ - Field *field; - MEM_ROOT *mem_root= table->in_use->mem_root; - - DBUG_ASSERT(collation.collation); - /* - Note: the following check is repeated in - subquery_types_allow_materialization(): - */ - if (too_big_for_varchar()) - field= new (mem_root) - Field_blob(max_length, maybe_null, name, - collation.collation, TRUE); - /* Item_type_holder holds the exact type, do not change it */ - else if (max_length > 0 && - (type() != Item::TYPE_HOLDER || field_type() != MYSQL_TYPE_STRING)) - field= new (mem_root) - Field_varstring(max_length, maybe_null, name, table->s, - collation.collation); - else - field= new (mem_root) - Field_string(max_length, maybe_null, name, collation.collation); - if (field) - field->init(table); - return field; -} - - -/** - Create a field based on field_type of argument. - - For now, this is only used to create a field for - IFNULL(x,something) and time functions - - @retval - NULL error - @retval - \# Created field -*/ - -Field *Item::tmp_table_field_from_field_type(TABLE *table, - bool fixed_length, - bool set_blob_packlength) -{ - /* - The field functions defines a field to be not null if null_ptr is not 0 - */ - uchar *null_ptr= maybe_null ? (uchar*) "" : 0; - Field *field; - MEM_ROOT *mem_root= table->in_use->mem_root; - - switch (field_type()) { - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_NEWDECIMAL: - field= Field_new_decimal::create_from_item(mem_root, this); - break; - case MYSQL_TYPE_TINY: - field= new (mem_root) - Field_tiny((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); - break; - case MYSQL_TYPE_SHORT: - field= new (mem_root) - Field_short((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); - break; - case MYSQL_TYPE_LONG: - field= new (mem_root) - Field_long((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); - break; -#ifdef HAVE_LONG_LONG - case MYSQL_TYPE_LONGLONG: - field= new (mem_root) - Field_longlong((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); - break; -#endif - case MYSQL_TYPE_FLOAT: - field= new (mem_root) - Field_float((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, decimals, 0, unsigned_flag); - break; - case MYSQL_TYPE_DOUBLE: - field= new (mem_root) - Field_double((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, decimals, 0, unsigned_flag); - break; - case MYSQL_TYPE_INT24: - field= new (mem_root) - Field_medium((uchar*) 0, max_length, null_ptr, 0, Field::NONE, - name, 0, unsigned_flag); - break; - case MYSQL_TYPE_NEWDATE: - case MYSQL_TYPE_DATE: - field= new (mem_root) - Field_newdate(0, null_ptr, 0, Field::NONE, name); - break; - case MYSQL_TYPE_TIME: - field= new_Field_time(mem_root, 0, null_ptr, 0, Field::NONE, name, - decimals); - break; - case MYSQL_TYPE_TIMESTAMP: - field= new_Field_timestamp(mem_root, 0, null_ptr, 0, - Field::NONE, name, 0, decimals); - break; - case MYSQL_TYPE_DATETIME: - field= new_Field_datetime(mem_root, 0, null_ptr, 0, Field::NONE, name, - decimals); - break; - case MYSQL_TYPE_YEAR: - field= new (mem_root) - Field_year((uchar*) 0, max_length, null_ptr, 0, Field::NONE, name); - break; - case MYSQL_TYPE_BIT: - field= new (mem_root) - Field_bit_as_char(NULL, max_length, null_ptr, 0, Field::NONE, name); - break; - default: - /* This case should never be chosen */ - DBUG_ASSERT(0); - /* If something goes awfully wrong, it's better to get a string than die */ - case MYSQL_TYPE_NULL: - case MYSQL_TYPE_STRING: - if (fixed_length && !too_big_for_varchar()) - { - field= new (mem_root) - Field_string(max_length, maybe_null, name, collation.collation); - break; - } - /* Fall through */ - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_VARCHAR: - return make_string_field(table); - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - field= new (mem_root) - Field_blob(max_length, maybe_null, name, - collation.collation, set_blob_packlength); - break; // Blob handled outside of case -#ifdef HAVE_SPATIAL - case MYSQL_TYPE_GEOMETRY: - field= new (mem_root) - Field_geom(max_length, maybe_null, name, table->s, get_geometry_type()); -#endif /* HAVE_SPATIAL */ - } - if (field) - field->init(table); - return field; -} - - /* ARGSUSED */ void Item_field::make_field(THD *thd, Send_field *tmp_field) { field->make_field(tmp_field); DBUG_ASSERT(tmp_field->table_name != 0); - if (name) - tmp_field->col_name=name; // Use user supplied name + if (name.str) + { + DBUG_ASSERT(name.length == strlen(name.str)); + tmp_field->col_name= name; // Use user supplied name + } if (table_name) tmp_field->table_name= table_name; if (db_name) @@ -6368,54 +6529,62 @@ int Item_null::save_safe_in_field(Field *field) Note: all Item_XXX::val_str(str) methods must NOT assume that str != str_value. For example, see fix for bug #44743. */ +int Item::save_str_in_field(Field *field, bool no_conversions) +{ + String *result; + CHARSET_INFO *cs= collation.collation; + char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns + str_value.set_quick(buff, sizeof(buff), cs); + result=val_str(&str_value); + if (null_value) + { + str_value.set_quick(0, 0, cs); + return set_field_to_null_with_conversions(field, no_conversions); + } -int Item::save_in_field(Field *field, bool no_conversions) + /* NOTE: If null_value == FALSE, "result" must be not NULL. */ + + field->set_notnull(); + int error= field->store(result->ptr(),result->length(),cs); + str_value.set_quick(0, 0, cs); + return error; +} + + +int Item::save_real_in_field(Field *field, bool no_conversions) { - int error; - if (result_type() == STRING_RESULT) - { - String *result; - CHARSET_INFO *cs= collation.collation; - char buff[MAX_FIELD_WIDTH]; // Alloc buffer for small columns - str_value.set_quick(buff, sizeof(buff), cs); - result=val_str(&str_value); - if (null_value) - { - str_value.set_quick(0, 0, cs); - return set_field_to_null_with_conversions(field, no_conversions); - } + double nr= val_real(); + if (null_value) + return set_field_to_null_with_conversions(field, no_conversions); + field->set_notnull(); + return field->store(nr); +} - /* NOTE: If null_value == FALSE, "result" must be not NULL. */ - field->set_notnull(); - error=field->store(result->ptr(),result->length(),cs); - str_value.set_quick(0, 0, cs); - } - else if (result_type() == REAL_RESULT) - { - double nr= val_real(); - if (null_value) - return set_field_to_null_with_conversions(field, no_conversions); - field->set_notnull(); - error=field->store(nr); - } - else if (result_type() == DECIMAL_RESULT) - { - my_decimal decimal_value; - my_decimal *value= val_decimal(&decimal_value); - if (null_value) - return set_field_to_null_with_conversions(field, no_conversions); - field->set_notnull(); - error=field->store_decimal(value); - } - else - { - longlong nr=val_int(); - if (null_value) - return set_field_to_null_with_conversions(field, no_conversions); - field->set_notnull(); - error=field->store(nr, unsigned_flag); - } +int Item::save_decimal_in_field(Field *field, bool no_conversions) +{ + my_decimal decimal_value; + my_decimal *value= val_decimal(&decimal_value); + if (null_value) + return set_field_to_null_with_conversions(field, no_conversions); + field->set_notnull(); + return field->store_decimal(value); +} + + +int Item::save_int_in_field(Field *field, bool no_conversions) +{ + longlong nr= val_int(); + if (null_value) + return set_field_to_null_with_conversions(field, no_conversions); + field->set_notnull(); + return field->store(nr, unsigned_flag); +} + + +int Item::save_in_field(Field *field, bool no_conversions) +{ + int error= type_handler()->Item_save_in_field(this, field, no_conversions); return error ? error : (field->table->in_use->is_error() ? 1 : 0); } @@ -6437,7 +6606,7 @@ int Item_string::save_in_field(Field *field, bool no_conversions) Item *Item_string::clone_item(THD *thd) { return new (thd->mem_root) - Item_string(thd, name, str_value.ptr(), + Item_string(thd, name.str, str_value.ptr(), str_value.length(), collation.collation); } @@ -6460,7 +6629,7 @@ int Item_int::save_in_field(Field *field, bool no_conversions) Item *Item_int::clone_item(THD *thd) { - return new (thd->mem_root) Item_int(thd, name, value, max_length); + return new (thd->mem_root) Item_int(thd, name.str, value, max_length); } @@ -6496,9 +6665,9 @@ Item *Item_int_with_ref::clone_item(THD *thd) */ return (ref->unsigned_flag ? new (thd->mem_root) - Item_uint(thd, ref->name, ref->val_int(), ref->max_length) : + Item_uint(thd, ref->name.str, ref->val_int(), ref->max_length) : new (thd->mem_root) - Item_int(thd, ref->name, ref->val_int(), ref->max_length)); + Item_int(thd, ref->name.str, ref->val_int(), ref->max_length)); } @@ -6526,7 +6695,7 @@ Item *Item_int::neg(THD *thd) else if (value < 0 && max_length) max_length--; value= -value; - name= 0; + name= null_clex_str; return this; } @@ -6534,7 +6703,7 @@ Item *Item_decimal::neg(THD *thd) { my_decimal_neg(&decimal_value); unsigned_flag= 0; - name= 0; + name= null_clex_str; max_length= my_decimal_precision_to_length_no_truncation( decimal_value.intg + decimals, decimals, unsigned_flag); return this; @@ -6547,7 +6716,8 @@ Item *Item_float::neg(THD *thd) else if (value < 0 && max_length) max_length--; value= -value; - name= presentation= 0 ; + presentation= 0; + name= null_clex_str; return this; } @@ -6566,7 +6736,7 @@ Item *Item_uint::neg(THD *thd) Item *Item_uint::clone_item(THD *thd) { - return new (thd->mem_root) Item_uint(thd, name, value, max_length); + return new (thd->mem_root) Item_uint(thd, name.str, value, max_length); } static uint nr_of_decimals(const char *str, const char *end) @@ -6636,7 +6806,8 @@ Item_float::Item_float(THD *thd, const char *str_arg, uint length): my_snprintf(tmp, sizeof(tmp), "%.*s", length, str_arg); my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "double", tmp); } - presentation= name=(char*) str_arg; + presentation= name.str= str_arg; + name.length= strlen(str_arg); decimals=(uint8) nr_of_decimals(str_arg, str_arg+length); max_length=length; fixed= 1; @@ -6710,6 +6881,22 @@ void Item_hex_hybrid::print(String *str, enum_query_type query_type) } +uint Item_hex_hybrid::decimal_precision() const +{ + switch (max_length) {// HEX DEC + case 0: // ---- --- + case 1: return 3; // 0xFF 255 + case 2: return 5; // 0xFFFF 65535 + case 3: return 8; // 0xFFFFFF 16777215 + case 4: return 10; // 0xFFFFFFFF 4294967295 + case 5: return 13; // 0xFFFFFFFFFF 1099511627775 + case 6: return 15; // 0xFFFFFFFFFFFF 281474976710655 + case 7: return 17; // 0xFFFFFFFFFFFFFF 72057594037927935 + } + return 20; // 0xFFFFFFFFFFFFFFFF 18446744073709551615 +} + + void Item_hex_string::print(String *str, enum_query_type query_type) { str->append("X'"); @@ -6857,127 +7044,11 @@ bool Item_time_literal::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) Pack data in buffer for sending. */ -bool Item_null::send(Protocol *protocol, String *packet) +bool Item_null::send(Protocol *protocol, st_value *buffer) { return protocol->store_null(); } -/** - This is only called from items that is not of type item_field. -*/ - -bool Item::send(Protocol *protocol, String *buffer) -{ - bool UNINIT_VAR(result); // Will be set if null_value == 0 - enum_field_types f_type; - - switch ((f_type=field_type())) { - default: - case MYSQL_TYPE_NULL: - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_GEOMETRY: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_BIT: - case MYSQL_TYPE_NEWDECIMAL: - { - String *res; - if ((res=val_str(buffer))) - { - DBUG_ASSERT(!null_value); - result= protocol->store(res->ptr(),res->length(),res->charset()); - } - else - { - DBUG_ASSERT(null_value); - } - break; - } - case MYSQL_TYPE_TINY: - { - longlong nr; - nr= val_int(); - if (!null_value) - result= protocol->store_tiny(nr); - break; - } - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_YEAR: - { - longlong nr; - nr= val_int(); - if (!null_value) - result= protocol->store_short(nr); - break; - } - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_LONG: - { - longlong nr; - nr= val_int(); - if (!null_value) - result= protocol->store_long(nr); - break; - } - case MYSQL_TYPE_LONGLONG: - { - longlong nr; - nr= val_int(); - if (!null_value) - result= protocol->store_longlong(nr, unsigned_flag); - break; - } - case MYSQL_TYPE_FLOAT: - { - float nr; - nr= (float) val_real(); - if (!null_value) - result= protocol->store(nr, decimals, buffer); - break; - } - case MYSQL_TYPE_DOUBLE: - { - double nr= val_real(); - if (!null_value) - result= protocol->store(nr, decimals, buffer); - break; - } - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_TIMESTAMP: - { - MYSQL_TIME tm; - get_date(&tm, sql_mode_for_dates(current_thd)); - if (!null_value) - { - if (f_type == MYSQL_TYPE_DATE) - return protocol->store_date(&tm); - else - result= protocol->store(&tm, decimals); - } - break; - } - case MYSQL_TYPE_TIME: - { - MYSQL_TIME tm; - get_time(&tm); - if (!null_value) - result= protocol->store_time(&tm, decimals); - break; - } - } - if (null_value) - result= protocol->store_null(); - return result; -} - /** Check if an item is a constant one and can be cached. @@ -7031,7 +7102,7 @@ Item* Item::cache_const_expr_transformer(THD *thd, uchar *arg) if (*(bool*)arg) { *((bool*)arg)= FALSE; - Item_cache *cache= Item_cache::get_cache(thd, this); + Item_cache *cache= get_cache(thd); if (!cache) return NULL; cache->setup(thd, this); @@ -7049,7 +7120,7 @@ bool Item::find_item_processor(void *arg) return (this == ((Item *) arg)); } -bool Item_field::send(Protocol *protocol, String *buffer) +bool Item_field::send(Protocol *protocol, st_value *buffer) { return protocol->store(result_field); } @@ -7122,7 +7193,7 @@ Item *Item_field::update_value_transformer(THD *thd, uchar *select_arg) all_fields->push_front((Item*)this, thd->mem_root); ref= new (thd->mem_root) Item_ref(thd, &select->context, &ref_pointer_array[el], - table_name, field_name); + table_name, &field_name); return ref; } return this; @@ -7149,7 +7220,7 @@ Item *get_field_item_for_having(THD *thd, Item *item, st_select_lex *sel) if (equal_item->used_tables() == map) { field_item= (Item_field *)(equal_item->real_item()); - break; + break; } } } @@ -7157,7 +7228,7 @@ Item *get_field_item_for_having(THD *thd, Item *item, st_select_lex *sel) { Item_ref *ref= new (thd->mem_root) Item_ref(thd, &sel->context, NullS, NullS, - field_item->field_name); + &field_item->field_name); return ref; } DBUG_ASSERT(0); @@ -7316,6 +7387,26 @@ void Item_field::print(String *str, enum_query_type query_type) } +bool Item_field_row::element_index_by_name(uint *idx, + const LEX_CSTRING &name) const +{ + Field *field; + for (uint i= 0; (field= get_row_field(i)); i++) + { + // Use the same comparison style with sp_context::find_variable() + if (!my_strnncoll(system_charset_info, + (const uchar *) field->field_name.str, + field->field_name.length, + (const uchar *) name.str, name.length)) + { + *idx= i; + return false; + } + } + return true; +} + + void Item_temptable_field::print(String *str, enum_query_type query_type) { /* @@ -7328,7 +7419,7 @@ void Item_temptable_field::print(String *str, enum_query_type query_type) Item_ref::Item_ref(THD *thd, Name_resolution_context *context_arg, Item **item, const char *table_name_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, bool alias_name_used_arg): Item_ident(thd, context_arg, NullS, table_name_arg, field_name_arg), ref(item), reference_trough_name(0) @@ -7373,7 +7464,8 @@ public: }; Item_ref::Item_ref(THD *thd, TABLE_LIST *view_arg, Item **item, - const char *field_name_arg, bool alias_name_used_arg): + const LEX_CSTRING *field_name_arg, + bool alias_name_used_arg): Item_ident(thd, view_arg, field_name_arg), ref(item), reference_trough_name(0) { @@ -7655,13 +7747,13 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) */ if (!((*ref)->type() == REF_ITEM && ((Item_ref *)(*ref))->ref_type() == OUTER_REF) && - (((*ref)->with_sum_func && name && + (((*ref)->with_sum_func && name.str && !(current_sel->linkage != GLOBAL_OPTIONS_TYPE && current_sel->having_fix_field)) || !(*ref)->fixed)) { my_error(ER_ILLEGAL_REFERENCE, MYF(0), - name, ((*ref)->with_sum_func? + name.str, ((*ref)->with_sum_func? "reference to group function": "forward reference in item list")); goto error; @@ -7803,11 +7895,11 @@ void Item_ref::print(String *str, enum_query_type query_type) if (ref) { if ((*ref)->type() != Item::CACHE_ITEM && ref_type() != VIEW_REF && - !table_name && name && alias_name_used) + !table_name && name.str && alias_name_used) { THD *thd= current_thd; - append_identifier(thd, str, (*ref)->real_item()->name, - strlen((*ref)->real_item()->name)); + append_identifier(thd, str, (*ref)->real_item()->name.str, + (*ref)->real_item()->name.length); } else (*ref)->print(str, query_type); @@ -7817,11 +7909,11 @@ void Item_ref::print(String *str, enum_query_type query_type) } -bool Item_ref::send(Protocol *prot, String *tmp) +bool Item_ref::send(Protocol *prot, st_value *buffer) { if (result_field) return prot->store(result_field); - return (*ref)->send(prot, tmp); + return (*ref)->send(prot, buffer); } @@ -8004,13 +8096,13 @@ void Item_ref::make_field(THD *thd, Send_field *field) { (*ref)->make_field(thd, field); /* Non-zero in case of a view */ - if (name) + if (name.str) field->col_name= name; if (table_name) field->table_name= table_name; if (db_name) field->db_name= db_name; - if (orig_field_name) + if (orig_field_name.str) field->org_col_name= orig_field_name; if (orig_table_name) field->org_table_name= orig_table_name; @@ -8116,10 +8208,9 @@ Item_cache_wrapper::Item_cache_wrapper(THD *thd, Item *item_arg): with_sum_func= orig_item->with_sum_func; with_field= orig_item->with_field; name= item_arg->name; - name_length= item_arg->name_length; with_subselect= orig_item->with_subselect; - if ((expr_value= Item_cache::get_cache(thd, orig_item))) + if ((expr_value= orig_item->get_cache(thd))) expr_value->setup(thd, orig_item); fixed= 1; @@ -8177,7 +8268,7 @@ bool Item_cache_wrapper::fix_fields(THD *thd __attribute__((unused)), return FALSE; } -bool Item_cache_wrapper::send(Protocol *protocol, String *buffer) +bool Item_cache_wrapper::send(Protocol *protocol, st_value *buffer) { if (result_field) return protocol->store(result_field); @@ -8508,7 +8599,7 @@ Item* Item_cache_wrapper::get_tmp_table_item(THD *thd) } -bool Item_direct_view_ref::send(Protocol *protocol, String *buffer) +bool Item_direct_view_ref::send(Protocol *protocol, st_value *buffer) { if (check_null_ref()) return protocol->store_null(); @@ -8823,14 +8914,15 @@ bool Item_default_value::fix_fields(THD *thd, Item **items) real_arg= arg->real_item(); if (real_arg->type() != FIELD_ITEM) { - my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), arg->name); + my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), arg->name.str); goto error; } field_arg= (Item_field *)real_arg; if ((field_arg->field->flags & NO_DEFAULT_VALUE_FLAG)) { - my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), field_arg->field->field_name); + my_error(ER_NO_DEFAULT_FOR_FIELD, MYF(0), + field_arg->field->field_name.str); goto error; } if (!(def_field= (Field*) thd->alloc(field_arg->field->size_of()))) @@ -8910,7 +9002,7 @@ bool Item_default_value::get_date(MYSQL_TIME *ltime,ulonglong fuzzydate) return Item_field::get_date(ltime, fuzzydate); } -bool Item_default_value::send(Protocol *protocol, String *buffer) +bool Item_default_value::send(Protocol *protocol, st_value *buffer) { calculate(); return Item_field::send(protocol, buffer); @@ -9017,7 +9109,7 @@ bool Item_ignore_value::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) return TRUE; } -bool Item_ignore_value::send(Protocol *protocol, String *buffer) +bool Item_ignore_value::send(Protocol *protocol, st_value *buffer) { DBUG_ASSERT(0); // never should be called return TRUE; @@ -9071,7 +9163,7 @@ bool Item_insert_value::fix_fields(THD *thd, Item **items) { Field *tmp_field= field_arg->field; /* charset doesn't matter here, it's to avoid sigsegv only */ - tmp_field= new Field_null(0, 0, Field::NONE, field_arg->field->field_name, + tmp_field= new Field_null(0, 0, Field::NONE, &field_arg->field->field_name, &my_charset_bin); if (tmp_field) { @@ -9128,7 +9220,7 @@ void Item_trigger_field::setup_field(THD *thd, TABLE *table, Try to find field by its name and if it will be found set field_idx properly. */ - (void)find_field_in_table(thd, table, field_name, (uint) strlen(field_name), + (void)find_field_in_table(thd, table, field_name.str, field_name.length, 0, &field_idx); thd->mark_used_columns= save_mark_used_columns; triggers= table->triggers; @@ -9140,8 +9232,8 @@ bool Item_trigger_field::eq(const Item *item, bool binary_cmp) const { return item->type() == TRIGGER_FIELD_ITEM && row_version == ((Item_trigger_field *)item)->row_version && - !my_strcasecmp(system_charset_info, field_name, - ((Item_trigger_field *)item)->field_name); + !my_strcasecmp(system_charset_info, field_name.str, + ((Item_trigger_field *)item)->field_name.str); } @@ -9207,9 +9299,11 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items) { table_grants->want_privilege= want_privilege; - if (check_grant_column(thd, table_grants, triggers->trigger_table->s->db.str, - triggers->trigger_table->s->table_name.str, field_name, - strlen(field_name), thd->security_ctx)) + if (check_grant_column(thd, table_grants, + triggers->trigger_table->s->db.str, + triggers->trigger_table->s->table_name.str, + field_name.str, field_name.length, + thd->security_ctx)) return TRUE; } #endif // NO_EMBEDDED_ACCESS_CHECKS @@ -9221,7 +9315,7 @@ bool Item_trigger_field::fix_fields(THD *thd, Item **items) return FALSE; } - my_error(ER_BAD_FIELD_ERROR, MYF(0), field_name, + my_error(ER_BAD_FIELD_ERROR, MYF(0), field_name.str, (row_version == NEW_ROW) ? "NEW" : "OLD"); return TRUE; } @@ -9231,14 +9325,14 @@ void Item_trigger_field::print(String *str, enum_query_type query_type) { str->append((row_version == NEW_ROW) ? "NEW" : "OLD", 3); str->append('.'); - str->append(field_name); + str->append(&field_name); } bool Item_trigger_field::check_vcol_func_processor(void *arg) { const char *ver= row_version == NEW_ROW ? "NEW." : "OLD."; - return mark_unsupported_function(ver, field_name, arg, VCOL_IMPOSSIBLE); + return mark_unsupported_function(ver, field_name.str, arg, VCOL_IMPOSSIBLE); } @@ -9275,101 +9369,14 @@ void resolve_const_item(THD *thd, Item **ref, Item *comp_item) Item *item= *ref; if (item->basic_const_item()) return; // Can't be better - - Item *new_item= NULL; - Item_result res_type= item_cmp_type(comp_item, item); - char *name= item->name; // Alloced on THD::mem_root - MEM_ROOT *mem_root= thd->mem_root; - - switch (res_type) { - case TIME_RESULT: - { - bool is_null; - Item **ref_copy= ref; - /* the following call creates a constant and puts it in new_item */ - enum_field_types type= item->field_type_for_temporal_comparison(comp_item); - get_datetime_value(thd, &ref_copy, &new_item, type, &is_null); - if (is_null) - new_item= new (mem_root) Item_null(thd, name); - break; - } - case STRING_RESULT: + Type_handler_hybrid_field_type cmp(comp_item->type_handler_for_comparison()); + if (!cmp.aggregate_for_comparison(item->type_handler_for_comparison())) { - char buff[MAX_FIELD_WIDTH]; - String tmp(buff,sizeof(buff),&my_charset_bin),*result; - result=item->val_str(&tmp); - if (item->null_value) - new_item= new (mem_root) Item_null(thd, name); - else - { - uint length= result->length(); - char *tmp_str= thd->strmake(result->ptr(), length); - new_item= new (mem_root) Item_string(thd, name, tmp_str, length, result->charset()); - } - break; - } - case INT_RESULT: - { - longlong result=item->val_int(); - uint length=item->max_length; - bool null_value=item->null_value; - new_item= (null_value ? (Item*) new (mem_root) Item_null(thd, name) : - (Item*) new (mem_root) Item_int(thd, name, result, length)); - break; - } - case ROW_RESULT: - if (item->type() == Item::ROW_ITEM && comp_item->type() == Item::ROW_ITEM) - { - /* - Substitute constants only in Item_row's. Don't affect other Items - with ROW_RESULT (eg Item_singlerow_subselect). - - For such Items more optimal is to detect if it is constant and replace - it with Item_row. This would optimize queries like this: - SELECT * FROM t1 WHERE (a,b) = (SELECT a,b FROM t2 LIMIT 1); - */ - Item_row *item_row= (Item_row*) item; - Item_row *comp_item_row= (Item_row*) comp_item; - uint col; - new_item= 0; - /* - If item and comp_item are both Item_row's and have same number of cols - then process items in Item_row one by one. - We can't ignore NULL values here as this item may be used with <=>, in - which case NULL's are significant. - */ - DBUG_ASSERT(item->result_type() == comp_item->result_type()); - DBUG_ASSERT(item_row->cols() == comp_item_row->cols()); - col= item_row->cols(); - while (col-- > 0) - resolve_const_item(thd, item_row->addr(col), - comp_item_row->element_index(col)); - break; - } - /* Fallthrough */ - case REAL_RESULT: - { // It must REAL_RESULT - double result= item->val_real(); - uint length=item->max_length,decimals=item->decimals; - bool null_value=item->null_value; - new_item= (null_value ? (Item*) new (mem_root) Item_null(thd, name) : (Item*) - new (mem_root) Item_float(thd, name, result, decimals, length)); - break; - } - case DECIMAL_RESULT: - { - my_decimal decimal_value; - my_decimal *result= item->val_decimal(&decimal_value); - uint length= item->max_length, decimals= item->decimals; - bool null_value= item->null_value; - new_item= (null_value ? - (Item*) new (mem_root) Item_null(thd, name) : - (Item*) new (mem_root) Item_decimal(thd, name, result, length, decimals)); - break; - } + Item *new_item= cmp.type_handler()-> + make_const_item_for_comparison(thd, item, comp_item); + if (new_item) + thd->change_item_tree(ref, new_item); } - if (new_item) - thd->change_item_tree(ref, new_item); } /** @@ -9466,41 +9473,6 @@ int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) return 0; } -Item_cache* Item_cache::get_cache(THD *thd, const Item *item) -{ - return get_cache(thd, item, item->cmp_type()); -} - - -/** - Get a cache item of given type. - - @param item value to be cached - @param type required type of cache - - @return cache item -*/ - -Item_cache* Item_cache::get_cache(THD *thd, const Item *item, - const Item_result type) -{ - MEM_ROOT *mem_root= thd->mem_root; - switch (type) { - case INT_RESULT: - return new (mem_root) Item_cache_int(thd, item->field_type()); - case REAL_RESULT: - return new (mem_root) Item_cache_real(thd); - case DECIMAL_RESULT: - return new (mem_root) Item_cache_decimal(thd); - case STRING_RESULT: - return new (mem_root) Item_cache_str(thd, item); - case ROW_RESULT: - return new (mem_root) Item_cache_row(thd); - case TIME_RESULT: - return new (mem_root) Item_cache_temporal(thd, item->field_type()); - } - return 0; // Impossible -} void Item_cache::store(Item *item) { @@ -9618,13 +9590,11 @@ Item *Item_cache_int::convert_to_basic_const_item(THD *thd) } -Item_cache_temporal::Item_cache_temporal(THD *thd, - enum_field_types field_type_arg): - Item_cache_int(thd, field_type_arg) +Item_cache_temporal::Item_cache_temporal(THD *thd, const Type_handler *handler) + :Item_cache_int(thd, handler) { - if (mysql_type_to_time_type(Item_cache_temporal::field_type()) == - MYSQL_TIMESTAMP_ERROR) - set_handler_by_field_type(MYSQL_TYPE_DATETIME); + if (mysql_timestamp_type() == MYSQL_TIMESTAMP_ERROR) + set_handler(&type_handler_datetime2); } @@ -9732,7 +9702,7 @@ bool Item_cache_temporal::get_date(MYSQL_TIME *ltime, ulonglong fuzzydate) } unpack_time(value, ltime); - ltime->time_type= mysql_type_to_time_type(field_type()); + ltime->time_type= mysql_timestamp_type(); if (ltime->time_type == MYSQL_TIMESTAMP_TIME) { ltime->hour+= (ltime->month*32+ltime->day)*24; @@ -9767,7 +9737,7 @@ void Item_cache_temporal::store_packed(longlong val_arg, Item *example_arg) Item *Item_cache_temporal::clone_item(THD *thd) { Item_cache_temporal *item= new (thd->mem_root) - Item_cache_temporal(thd, Item_cache_temporal::field_type()); + Item_cache_temporal(thd, Item_cache_temporal::type_handler()); item->store_packed(value, example); return item; } @@ -10025,7 +9995,7 @@ bool Item_cache_row::setup(THD *thd, Item *item) { Item *el= item->element_index(i); Item_cache *tmp; - if (!(tmp= values[i]= Item_cache::get_cache(thd, el))) + if (!(tmp= values[i]= el->get_cache(thd))) return 1; tmp->setup(thd, el); } @@ -10128,388 +10098,6 @@ void Item_cache_row::set_null() }; -Item_type_holder::Item_type_holder(THD *thd, Item *item) - :Item(thd, item), - Type_handler_hybrid_real_field_type(get_real_type(item)), - enum_set_typelib(0), - geometry_type(Field::GEOM_GEOMETRY) -{ - DBUG_ASSERT(item->fixed); - maybe_null= item->maybe_null; - collation.set(item->collation); - get_full_info(item); - /** - Field::result_merge_type(real_field_type()) should be equal to - result_type(), with one exception when "this" is a Item_field for - a BIT field: - - Field_bit::result_type() returns INT_RESULT, so does its Item_field. - - Field::result_merge_type(MYSQL_TYPE_BIT) returns STRING_RESULT. - Perhaps we need a new method in Type_handler to cover these type - merging rules for UNION. - */ - DBUG_ASSERT(real_field_type() == MYSQL_TYPE_BIT || - Item_type_holder::result_type() == - Field::result_merge_type(Item_type_holder::real_field_type())); - /* fix variable decimals which always is NOT_FIXED_DEC */ - if (Field::result_merge_type(real_field_type()) == INT_RESULT) - decimals= 0; - prev_decimal_int_part= item->decimal_int_part(); -#ifdef HAVE_SPATIAL - if (item->field_type() == MYSQL_TYPE_GEOMETRY) - geometry_type= item->get_geometry_type(); -#endif /* HAVE_SPATIAL */ -} - - -/** - Find real field type of item. - - @return - type of field which should be created to store item value -*/ - -enum_field_types Item_type_holder::get_real_type(Item *item) -{ - if (item->type() == REF_ITEM) - item= item->real_item(); - switch(item->type()) - { - case FIELD_ITEM: - { - /* - Item_field::field_type ask Field_type() but sometimes field return - a different type, like for enum/set, so we need to ask real type. - */ - Field *field= ((Item_field *) item)->field; - enum_field_types type= field->real_type(); - if (field->is_created_from_null_item) - return MYSQL_TYPE_NULL; - /* work around about varchar type field detection */ - if (type == MYSQL_TYPE_STRING && field->type() == MYSQL_TYPE_VAR_STRING) - return MYSQL_TYPE_VAR_STRING; - return type; - } - case SUM_FUNC_ITEM: - { - /* - Argument of aggregate function sometimes should be asked about field - type - */ - Item_sum *item_sum= (Item_sum *) item; - if (item_sum->keep_field_type()) - return get_real_type(item_sum->get_arg(0)); - break; - } - case FUNC_ITEM: - if (((Item_func *) item)->functype() == Item_func::GUSERVAR_FUNC) - { - /* - There are work around of problem with changing variable type on the - fly and variable always report "string" as field type to get - acceptable information for client in send_field, so we make field - type from expression type. - */ - switch (item->result_type()) { - case STRING_RESULT: - return MYSQL_TYPE_VARCHAR; - case INT_RESULT: - return MYSQL_TYPE_LONGLONG; - case REAL_RESULT: - return MYSQL_TYPE_DOUBLE; - case DECIMAL_RESULT: - return MYSQL_TYPE_NEWDECIMAL; - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); - return MYSQL_TYPE_VARCHAR; - } - } - break; - case TYPE_HOLDER: - /* - Item_type_holder and Item_blob should not appear in this context. - In case they for some reasons do, returning field_type() is wrong anyway. - They must return Item_type_holder::real_field_type() instead, to make - the code in sql_type.cc and sql_type.h happy, as it expectes - Field::real_type()-compatible rather than Field::field_type()-compatible - valies in some places, and may in the future add some asserts preventing - use of field_type() instead of real_type() and the other way around. - */ - DBUG_ASSERT(0); - default: - break; - } - return item->field_type(); -} - -/** - Find field type which can carry current Item_type_holder type and - type of given Item. - - @param thd thread handler - @param item given item to join its parameters with this item ones - - @retval - TRUE error - types are incompatible - @retval - FALSE OK -*/ - -bool Item_type_holder::join_types(THD *thd, Item *item) -{ - uint max_length_orig= max_length; - uint decimals_orig= decimals; - DBUG_ENTER("Item_type_holder::join_types"); - DBUG_PRINT("info:", ("was type %d len %d, dec %d name %s", - real_field_type(), max_length, decimals, - (name ? name : "<NULL>"))); - DBUG_PRINT("info:", ("in type %d len %d, dec %d", - get_real_type(item), - item->max_length, item->decimals)); - set_handler_by_real_type(Field::field_type_merge(real_field_type(), - get_real_type(item))); - { - uint item_decimals= item->decimals; - /* fix variable decimals which always is NOT_FIXED_DEC */ - if (Field::result_merge_type(real_field_type()) == INT_RESULT) - item_decimals= 0; - decimals= MY_MAX(decimals, item_decimals); - } - - if (Item_type_holder::field_type() == FIELD_TYPE_GEOMETRY) - geometry_type= - Field_geom::geometry_type_merge(geometry_type, item->get_geometry_type()); - - if (Field::result_merge_type(real_field_type()) == DECIMAL_RESULT) - { - decimals= MY_MIN(MY_MAX(decimals, item->decimals), DECIMAL_MAX_SCALE); - int item_int_part= item->decimal_int_part(); - int item_prec = MY_MAX(prev_decimal_int_part, item_int_part) + decimals; - int precision= MY_MIN(item_prec, DECIMAL_MAX_PRECISION); - unsigned_flag&= item->unsigned_flag; - max_length= my_decimal_precision_to_length_no_truncation(precision, - decimals, - unsigned_flag); - } - - switch (Field::result_merge_type(real_field_type())) - { - case STRING_RESULT: - { - const char *old_cs, *old_derivation; - uint32 old_max_chars= max_length / collation.collation->mbmaxlen; - old_cs= collation.collation->name; - old_derivation= collation.derivation_name(); - if (collation.aggregate(item->collation, MY_COLL_ALLOW_CONV)) - { - my_error(ER_CANT_AGGREGATE_2COLLATIONS, MYF(0), - old_cs, old_derivation, - item->collation.collation->name, - item->collation.derivation_name(), - "UNION"); - DBUG_RETURN(TRUE); - } - /* - To figure out max_length, we have to take into account possible - expansion of the size of the values because of character set - conversions. - */ - if (collation.collation != &my_charset_bin) - { - max_length= MY_MAX(old_max_chars * collation.collation->mbmaxlen, - display_length(item) / - item->collation.collation->mbmaxlen * - collation.collation->mbmaxlen); - } - else - set_if_bigger(max_length, display_length(item)); - break; - } - case REAL_RESULT: - { - if (decimals != NOT_FIXED_DEC) - { - /* - For FLOAT(M,D)/DOUBLE(M,D) do not change precision - if both fields have the same M and D - */ - if (item->max_length != max_length_orig || - item->decimals != decimals_orig) - { - int delta1= max_length_orig - decimals_orig; - int delta2= item->max_length - item->decimals; - max_length= MY_MAX(delta1, delta2) + decimals; - if (Item_type_holder::real_field_type() == MYSQL_TYPE_FLOAT && - max_length > FLT_DIG + 2) - { - max_length= MAX_FLOAT_STR_LENGTH; - decimals= NOT_FIXED_DEC; - } - else if (Item_type_holder::real_field_type() == MYSQL_TYPE_DOUBLE && - max_length > DBL_DIG + 2) - { - max_length= MAX_DOUBLE_STR_LENGTH; - decimals= NOT_FIXED_DEC; - } - } - } - else - max_length= (Item_type_holder::field_type() == MYSQL_TYPE_FLOAT) ? - FLT_DIG+6 : DBL_DIG+7; - break; - } - default: - max_length= MY_MAX(max_length, display_length(item)); - }; - maybe_null|= item->maybe_null; - get_full_info(item); - - /* Remember decimal integer part to be used in DECIMAL_RESULT handleng */ - prev_decimal_int_part= decimal_int_part(); - DBUG_PRINT("info", ("become type: %d len: %u dec: %u", - (int) real_field_type(), max_length, (uint) decimals)); - DBUG_RETURN(FALSE); -} - -/** - Calculate lenth for merging result for given Item type. - - @param item Item for length detection - - @return - length -*/ - -uint32 Item_type_holder::display_length(Item *item) -{ - if (item->type() == Item::FIELD_ITEM) - return ((Item_field *)item)->max_disp_length(); - - switch (item->field_type()) - { - case MYSQL_TYPE_DECIMAL: - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_DATE: - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_NEWDATE: - case MYSQL_TYPE_VARCHAR: - case MYSQL_TYPE_BIT: - case MYSQL_TYPE_NEWDECIMAL: - case MYSQL_TYPE_ENUM: - case MYSQL_TYPE_SET: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_LONG_BLOB: - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_VAR_STRING: - case MYSQL_TYPE_STRING: - case MYSQL_TYPE_GEOMETRY: - return item->max_length; - case MYSQL_TYPE_TINY: - return 4; - case MYSQL_TYPE_SHORT: - return 6; - case MYSQL_TYPE_LONG: - return MY_INT32_NUM_DECIMAL_DIGITS; - case MYSQL_TYPE_FLOAT: - return 25; - case MYSQL_TYPE_DOUBLE: - return 53; - case MYSQL_TYPE_NULL: - return 0; - case MYSQL_TYPE_LONGLONG: - return 20; - case MYSQL_TYPE_INT24: - return 8; - default: - DBUG_ASSERT(0); // we should never go there - return 0; - } -} - - -/** - Make temporary table field according collected information about type - of UNION result. - - @param table temporary table for which we create fields - - @return - created field -*/ - -Field *Item_type_holder::make_field_by_type(TABLE *table) -{ - /* - The field functions defines a field to be not null if null_ptr is not 0 - */ - uchar *null_ptr= maybe_null ? (uchar*) "" : 0; - Field *field; - - switch (Item_type_holder::real_field_type()) { - case MYSQL_TYPE_ENUM: - DBUG_ASSERT(enum_set_typelib); - field= new Field_enum((uchar *) 0, max_length, null_ptr, 0, - Field::NONE, name, - get_enum_pack_length(enum_set_typelib->count), - enum_set_typelib, collation.collation); - if (field) - field->init(table); - return field; - case MYSQL_TYPE_SET: - DBUG_ASSERT(enum_set_typelib); - field= new Field_set((uchar *) 0, max_length, null_ptr, 0, - Field::NONE, name, - get_set_pack_length(enum_set_typelib->count), - enum_set_typelib, collation.collation); - if (field) - field->init(table); - return field; - case MYSQL_TYPE_NULL: - return make_string_field(table); - default: - break; - } - return tmp_table_field_from_field_type(table, false, true); -} - - -/** - Get full information from Item about enum/set fields to be able to create - them later. - - @param item Item for information collection -*/ -void Item_type_holder::get_full_info(Item *item) -{ - if (Item_type_holder::real_field_type() == MYSQL_TYPE_ENUM || - Item_type_holder::real_field_type() == MYSQL_TYPE_SET) - { - if (item->type() == Item::SUM_FUNC_ITEM && - (((Item_sum*)item)->sum_func() == Item_sum::MAX_FUNC || - ((Item_sum*)item)->sum_func() == Item_sum::MIN_FUNC)) - item = ((Item_sum*)item)->get_arg(0); - /* - We can have enum/set type after merging only if we have one enum|set - field (or MIN|MAX(enum|set field)) and number of NULL fields - */ - DBUG_ASSERT((enum_set_typelib && - get_real_type(item) == MYSQL_TYPE_NULL) || - (!enum_set_typelib && - item->real_item()->type() == Item::FIELD_ITEM && - (get_real_type(item->real_item()) == MYSQL_TYPE_ENUM || - get_real_type(item->real_item()) == MYSQL_TYPE_SET) && - ((Field_enum*)((Item_field *) item->real_item())->field)->typelib)); - if (!enum_set_typelib) - { - enum_set_typelib= ((Field_enum*)((Item_field *) item->real_item())->field)->typelib; - } - } -} - - double Item_type_holder::val_real() { DBUG_ASSERT(0); // should never be called diff --git a/sql/item.h b/sql/item.h index a834e3fef94..bb341959103 100644 --- a/sql/item.h +++ b/sql/item.h @@ -55,8 +55,41 @@ struct st_value C_MODE_END + +class Value: public st_value +{ +public: + bool is_null() const { return m_type == DYN_COL_NULL; } + bool is_longlong() const + { + return m_type == DYN_COL_UINT || m_type == DYN_COL_INT; + } + bool is_double() const { return m_type == DYN_COL_DOUBLE; } + bool is_temporal() const { return m_type == DYN_COL_DATETIME; } + bool is_string() const { return m_type == DYN_COL_STRING; } + bool is_decimal() const { return m_type == DYN_COL_DECIMAL; } +}; + + +template<size_t buffer_size> +class ValueBuffer: public Value +{ + char buffer[buffer_size]; + void reset_buffer() + { + m_string.set(buffer, buffer_size, &my_charset_bin); + } +public: + ValueBuffer() + { + reset_buffer(); + } +}; + + const char *dbug_print_item(Item *item); +class sp_head; class Protocol; struct TABLE_LIST; void item_init(void); /* Init item functions */ @@ -96,13 +129,6 @@ enum precedence { typedef Bounds_checked_array<Item*> Ref_ptr_array; -static inline uint32 -char_to_byte_length_safe(size_t char_length_arg, uint32 mbmaxlen_arg) -{ - ulonglong tmp= ((ulonglong) char_length_arg) * mbmaxlen_arg; - return tmp > UINT_MAX32 ? UINT_MAX32 : static_cast<uint32>(tmp); -} - bool mark_unsupported_function(const char *where, void *store, uint result); /* convenience helper for mark_unsupported_function() above */ @@ -113,124 +139,12 @@ bool mark_unsupported_function(const char *w1, const char *w2, #define SPLIT_SUM_SKIP_REGISTERED 1 /* Skip registered funcs */ #define SPLIT_SUM_SELECT 2 /* SELECT item; Split all parts */ -/* - "Declared Type Collation" - A combination of collation and its derivation. - - Flags for collation aggregation modes: - MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset - MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value - (i.e. constant). - MY_COLL_ALLOW_CONV - allow any kind of conversion - (combination of the above two) - MY_COLL_ALLOW_NUMERIC_CONV - if all items were numbers, convert to - @@character_set_connection - MY_COLL_DISALLOW_NONE - don't allow return DERIVATION_NONE - (e.g. when aggregating for comparison) - MY_COLL_CMP_CONV - combination of MY_COLL_ALLOW_CONV - and MY_COLL_DISALLOW_NONE -*/ - -#define MY_COLL_ALLOW_SUPERSET_CONV 1 -#define MY_COLL_ALLOW_COERCIBLE_CONV 2 -#define MY_COLL_DISALLOW_NONE 4 -#define MY_COLL_ALLOW_NUMERIC_CONV 8 - -#define MY_COLL_ALLOW_CONV (MY_COLL_ALLOW_SUPERSET_CONV | MY_COLL_ALLOW_COERCIBLE_CONV) -#define MY_COLL_CMP_CONV (MY_COLL_ALLOW_CONV | MY_COLL_DISALLOW_NONE) #define NO_EXTRACTION_FL (1 << 6) #define FULL_EXTRACTION_FL (1 << 7) #define EXTRACTION_MASK (NO_EXTRACTION_FL | FULL_EXTRACTION_FL) -class DTCollation { -public: - CHARSET_INFO *collation; - enum Derivation derivation; - uint repertoire; - - void set_repertoire_from_charset(CHARSET_INFO *cs) - { - repertoire= cs->state & MY_CS_PUREASCII ? - MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; - } - DTCollation() - { - collation= &my_charset_bin; - derivation= DERIVATION_NONE; - repertoire= MY_REPERTOIRE_UNICODE30; - } - DTCollation(CHARSET_INFO *collation_arg, Derivation derivation_arg) - { - collation= collation_arg; - derivation= derivation_arg; - set_repertoire_from_charset(collation_arg); - } - DTCollation(CHARSET_INFO *collation_arg, - Derivation derivation_arg, - uint repertoire_arg) - :collation(collation_arg), - derivation(derivation_arg), - repertoire(repertoire_arg) - { } - void set(const DTCollation &dt) - { - collation= dt.collation; - derivation= dt.derivation; - repertoire= dt.repertoire; - } - void set(CHARSET_INFO *collation_arg, Derivation derivation_arg) - { - collation= collation_arg; - derivation= derivation_arg; - set_repertoire_from_charset(collation_arg); - } - void set(CHARSET_INFO *collation_arg, - Derivation derivation_arg, - uint repertoire_arg) - { - collation= collation_arg; - derivation= derivation_arg; - repertoire= repertoire_arg; - } - void set_numeric() - { - collation= &my_charset_numeric; - derivation= DERIVATION_NUMERIC; - repertoire= MY_REPERTOIRE_NUMERIC; - } - void set(CHARSET_INFO *collation_arg) - { - collation= collation_arg; - set_repertoire_from_charset(collation_arg); - } - void set(Derivation derivation_arg) - { derivation= derivation_arg; } - bool aggregate(const DTCollation &dt, uint flags= 0); - bool set(DTCollation &dt1, DTCollation &dt2, uint flags= 0) - { set(dt1); return aggregate(dt2, flags); } - const char *derivation_name() const - { - switch(derivation) - { - case DERIVATION_NUMERIC: return "NUMERIC"; - case DERIVATION_IGNORABLE: return "IGNORABLE"; - case DERIVATION_COERCIBLE: return "COERCIBLE"; - case DERIVATION_IMPLICIT: return "IMPLICIT"; - case DERIVATION_SYSCONST: return "SYSCONST"; - case DERIVATION_EXPLICIT: return "EXPLICIT"; - case DERIVATION_NONE: return "NONE"; - default: return "UNKNOWN"; - } - } - int sortcmp(const String *s, const String *t) const - { - return collation->coll->strnncollsp(collation, - (uchar *) s->ptr(), s->length(), - (uchar *) t->ptr(), t->length()); - } -}; - +extern const char *item_empty_name; void dummy_error_processor(THD *thd, void *data); @@ -478,6 +392,31 @@ public: { return NULL; } }; + +/* + A helper class to calculate offset and length of a query fragment + - outside of SP + - inside an SP + - inside a compound block +*/ +class Query_fragment +{ + uint m_pos; + uint m_length; + void set(size_t pos, size_t length) + { + DBUG_ASSERT(pos < UINT_MAX32); + DBUG_ASSERT(length < UINT_MAX32); + m_pos= (uint) pos; + m_length= (uint) length; + } +public: + Query_fragment(THD *thd, sp_head *sphead, const char *start, const char *end); + uint pos() const { return m_pos; } + uint length() const { return m_length; } +}; + + /** This is used for items in the query that needs to be rewritten before binlogging @@ -496,7 +435,7 @@ class Rewritable_query_parameter /* Byte length of parameter name in the statement. This is not - Item::name_length because name_length contains byte length of UTF8-encoded + Item::name.length because name.length contains byte length of UTF8-encoded name, but the query string is in the client charset. */ uint len_in_query; @@ -578,6 +517,8 @@ struct find_selective_predicates_list_processor_data List<st_cond_statistic> list; }; +class MY_LOCALE; + class Item_equal; class COND_EQUAL; @@ -600,49 +541,8 @@ public: }; -/** - A class to store type attributes for the standard data types. - Does not include attributes for the extended data types - such as ENUM, SET, GEOMETRY. -*/ -class Type_std_attributes -{ -public: - DTCollation collation; - uint decimals; - /* - The maximum value length in characters multiplied by collation->mbmaxlen. - Almost always it's the maximum value length in bytes. - */ - uint32 max_length; - bool unsigned_flag; - Type_std_attributes() - :collation(&my_charset_bin, DERIVATION_COERCIBLE), - decimals(0), max_length(0), unsigned_flag(false) - { } - Type_std_attributes(const Type_std_attributes *other) - :collation(other->collation), - decimals(other->decimals), - max_length(other->max_length), - unsigned_flag(other->unsigned_flag) - { } - void set(const Type_std_attributes *other) - { - *this= *other; - } - void set(const Field *field) - { - decimals= field->decimals(); - max_length= field->field_length; - collation.set(field->charset()); - unsigned_flag= MY_TEST(field->flags & UNSIGNED_FLAG); - } -}; - - class Item: public Value_source, - public Type_std_attributes, - public Type_handler + public Type_all_attributes { void operator=(Item &); /** @@ -695,14 +595,76 @@ protected: SEL_TREE *get_mm_tree_for_const(RANGE_OPT_PARAM *param); - virtual Field *make_string_field(TABLE *table); - Field *tmp_table_field_from_field_type(TABLE *table, - bool fixed_length, - bool set_blob_packlength); - Field *create_tmp_field(bool group, TABLE *table, uint convert_int_length); + /** + Create a field based on the exact data type handler. + */ + Field *create_table_field_from_handler(TABLE *table) + { + const Type_handler *h= type_handler(); + return h->make_and_init_table_field(&name, Record_addr(maybe_null), + *this, table); + } + /** + Create a field based on field_type of argument. + This is used to create a field for + - IFNULL(x,something) + - time functions + - prepared statement placeholders + - SP variables with data type references: DECLARE a TYPE OF t1.a; + @retval NULL error + @retval !NULL on success + */ + Field *tmp_table_field_from_field_type(TABLE *table) + { + const Type_handler *h= type_handler()->type_handler_for_tmp_table(this); + return h->make_and_init_table_field(&name, Record_addr(maybe_null), + *this, table); + } + Field *create_tmp_field_int(TABLE *table, uint convert_int_length); void push_note_converted_to_negative_complement(THD *thd); void push_note_converted_to_positive_complement(THD *thd); + + /* Helper methods, to get an Item value from another Item */ + double val_real_from_item(Item *item) + { + DBUG_ASSERT(fixed == 1); + double value= item->val_real(); + null_value= item->null_value; + return value; + } + longlong val_int_from_item(Item *item) + { + DBUG_ASSERT(fixed == 1); + longlong value= item->val_int(); + null_value= item->null_value; + return value; + } + String *val_str_from_item(Item *item, String *str) + { + DBUG_ASSERT(fixed == 1); + String *res= item->val_str(str); + if (res) + res->set_charset(collation.collation); + if ((null_value= item->null_value)) + res= NULL; + return res; + } + my_decimal *val_decimal_from_item(Item *item, my_decimal *decimal_value) + { + DBUG_ASSERT(fixed == 1); + my_decimal *value= item->val_decimal(decimal_value); + if ((null_value= item->null_value)) + value= NULL; + return value; + } + bool get_date_with_conversion_from_item(Item *item, + MYSQL_TIME *ltime, uint fuzzydate) + { + DBUG_ASSERT(fixed == 1); + return (null_value= item->get_date_with_conversion(ltime, fuzzydate)); + } + public: /* Cache val_str() into the own buffer, e.g. to evaluate constant @@ -710,9 +672,11 @@ public: */ String *val_str() { return val_str(&str_value); } - char * name; /* Name from select */ + const MY_LOCALE *locale_from_val_str(); + + LEX_CSTRING name; /* Name of item */ /* Original item name (if it was renamed)*/ - char * orig_name; + const char *orig_name; /** Intrusive list pointer for free list. If not null, points to the next Item on some Query_arena's free list. For instance, stored procedures @@ -721,12 +685,6 @@ public: @see Query_arena::free_list */ Item *next; - /* - TODO: convert name and name_length fields into LEX_STRING to keep them in - sync (see bug #11829681/60295 etc). Then also remove some strlen(name) - calls. - */ - uint name_length; /* Length of name */ int marker; bool maybe_null; /* If item may be null */ bool in_rollup; /* If used in GROUP BY list @@ -758,15 +716,13 @@ public: virtual ~Item() { #ifdef EXTRA_DEBUG - name=0; + name.str= 0; + name.length= 0; #endif } /*lint -e1509 */ void set_name(THD *thd, const char *str, uint length, CHARSET_INFO *cs); void set_name_no_truncate(THD *thd, const char *str, uint length, CHARSET_INFO *cs); - void set_name_for_rollback(THD *thd, const char *str, uint length, - CHARSET_INFO *cs); - void rename(char *new_name); void init_make_field(Send_field *tmp_field,enum enum_field_types type); virtual void cleanup(); virtual void make_field(THD *thd, Send_field *field); @@ -790,54 +746,9 @@ public: */ virtual inline void quick_fix_field() { fixed= 1; } - bool store(struct st_value *value, ulonglong fuzzydate) + bool save_in_value(struct st_value *value) { - switch (cmp_type()) { - case INT_RESULT: - { - value->m_type= unsigned_flag ? DYN_COL_UINT : DYN_COL_INT; - value->value.m_longlong= val_int(); - break; - } - case REAL_RESULT: - { - value->m_type= DYN_COL_DOUBLE; - value->value.m_double= val_real(); - break; - } - case DECIMAL_RESULT: - { - value->m_type= DYN_COL_DECIMAL; - my_decimal *dec= val_decimal(&value->m_decimal); - if (dec != &value->m_decimal && !null_value) - my_decimal2decimal(dec, &value->m_decimal); - break; - } - case STRING_RESULT: - { - value->m_type= DYN_COL_STRING; - String *str= val_str(&value->m_string); - if (str != &value->m_string && !null_value) - value->m_string.set(str->ptr(), str->length(), str->charset()); - break; - } - case TIME_RESULT: - { - value->m_type= DYN_COL_DATETIME; - get_date(&value->value.m_time, fuzzydate); - break; - } - case ROW_RESULT: - DBUG_ASSERT(false); - null_value= true; - break; - } - if (null_value) - { - value->m_type= DYN_COL_NULL; - return true; - } - return false; + return type_handler()->Item_save_in_value(this, value); } /* Function returns 1 on overflow and -1 on fatal errors */ @@ -852,42 +763,80 @@ public: { return NULL; } virtual int save_safe_in_field(Field *field) { return save_in_field(field, 1); } - virtual bool send(Protocol *protocol, String *str); + virtual bool send(Protocol *protocol, st_value *buffer) + { + return type_handler()->Item_send(this, protocol, buffer); + } virtual bool eq(const Item *, bool binary_cmp) const; - const Type_handler *type_handler() const + enum_field_types field_type() const { - return get_handler_by_field_type(field_type()); + return type_handler()->field_type(); } - Field *make_num_distinct_aggregator_field(MEM_ROOT *mem_root, - const Item *item) const + virtual const Type_handler *type_handler() const= 0; + const Type_handler *type_handler_for_comparison() const { - return type_handler()->make_num_distinct_aggregator_field(mem_root, this); + return type_handler()->type_handler_for_comparison(); } - Field *make_conversion_table_field(TABLE *table, - uint metadata, const Field *target) const + virtual const Type_handler *real_type_handler() const { - DBUG_ASSERT(0); // Should not be called in Item context - return NULL; + return type_handler(); + } + virtual const Type_handler *cast_to_int_type_handler() const + { + return type_handler(); } /* result_type() of an item specifies how the value should be returned */ - Item_result result_type() const { return type_handler()->result_type(); } + Item_result result_type() const + { + return type_handler()->result_type(); + } /* ... while cmp_type() specifies how it should be compared */ - Item_result cmp_type() const { return type_handler()->cmp_type(); } - void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, - Sort_param *param) const + Item_result cmp_type() const + { + return type_handler()->cmp_type(); + } + const Type_handler *string_type_handler() const + { + return Type_handler::string_type_handler(max_length); + } + /* + Calculate the maximum length of an expression. + This method is used in data type aggregation for UNION, e.g.: + SELECT 'b' UNION SELECT COALESCE(double_10_3_field) FROM t1; + + The result is usually equal to max_length, except for some numeric types. + In case of the INT, FLOAT, DOUBLE data types Item::max_length and + Item::decimals are ignored, so the returned value depends only on the + data type itself. E.g. for an expression of the DOUBLE(10,3) data type, + the result is always 53 (length 10 and precision 3 do not matter). + + max_length is ignored for these numeric data types because the length limit + means only "expected maximum length", it is not a hard limit, so it does + not impose any data truncation. E.g. a column of the type INT(4) can + normally store big values up to 2147483647 without truncation. When we're + aggregating such column for UNION it's important to create a long enough + result column, not to lose any data. + + For detailed behaviour of various data types see implementations of + the corresponding Type_handler_xxx::max_display_length(). + + Note, Item_field::max_display_length() overrides this to get + max_display_length() from the underlying field. + */ + virtual uint32 max_display_length() const { - type_handler()->make_sort_key(to, item, sort_field, param); + return type_handler()->max_display_length(this); } - void sortlength(THD *thd, - const Type_std_attributes *item, - SORT_FIELD_ATTR *attr) const + TYPELIB *get_typelib() const { return NULL; } + void set_maybe_null(bool maybe_null_arg) { maybe_null= maybe_null_arg; } + void set_typelib(TYPELIB *typelib) { - type_handler()->sortlength(thd, item, attr); + // Non-field Items (e.g. hybrid functions) never have ENUM/SET types yet. + DBUG_ASSERT(0); } - virtual Item_result cast_to_int_type() const { return cmp_type(); } - enum_field_types string_field_type() const + Item_cache* get_cache(THD *thd) const { - return Type_handler::string_type_handler(max_length)->field_type(); + return type_handler()->Item_get_cache(thd, this); } virtual enum Type type() const =0; /* @@ -971,20 +920,45 @@ public: to negative complements. Values of non-integer data types are adjusted to the SIGNED range. */ - virtual longlong val_int_signed_typecast(); + virtual longlong val_int_signed_typecast() + { + return cast_to_int_type_handler()->Item_val_int_signed_typecast(this); + } + longlong val_int_signed_typecast_from_str(); /** Get a value for CAST(x AS UNSIGNED). Negative signed integer values are converted to positive complements. Values of non-integer data types are adjusted to the UNSIGNED range. */ - virtual longlong val_int_unsigned_typecast(); + virtual longlong val_int_unsigned_typecast() + { + return cast_to_int_type_handler()->Item_val_int_unsigned_typecast(this); + } + longlong val_int_unsigned_typecast_from_decimal(); + longlong val_int_unsigned_typecast_from_int(); + longlong val_int_unsigned_typecast_from_str(); /* This is just a shortcut to avoid the cast. You should still use unsigned_flag to check the sign of the item. */ inline ulonglong val_uint() { return (ulonglong) val_int(); } /* + Adjust the result of val_int() to an unsigned number: + - NULL value is converted to 0. The caller can check "null_value" + to distinguish between 0 and NULL when necessary. + - Negative numbers are converted to 0. + - Positive numbers bigger than upper_bound are converted to upper_bound. + - Other numbers are returned as is. + */ + ulonglong val_uint_from_val_int(ulonglong upper_bound) + { + longlong nr= val_int(); + return (null_value || (nr < 0 && !unsigned_flag)) ? 0 : + (ulonglong) nr > upper_bound ? upper_bound : (ulonglong) nr; + } + + /* Return string representation of this item object. SYNOPSIS @@ -1117,7 +1091,10 @@ public: FALSE value is false or NULL TRUE value is true (not equal to 0) */ - virtual bool val_bool(); + virtual bool val_bool() + { + return type_handler()->Item_val_bool(this); + } virtual String *val_nodeset(String*) { return 0; } /* @@ -1156,16 +1133,21 @@ public: // Check NULL value for a TIME, DATE or DATETIME expression bool is_null_from_temporal(); - int save_time_in_field(Field *field); - int save_date_in_field(Field *field); + int save_time_in_field(Field *field, bool no_conversions); + int save_date_in_field(Field *field, bool no_conversions); + int save_str_in_field(Field *field, bool no_conversions); + int save_real_in_field(Field *field, bool no_conversions); + int save_int_in_field(Field *field, bool no_conversions); + int save_decimal_in_field(Field *field, bool no_conversions); + int save_str_value_in_field(Field *field, String *result); virtual Field *get_tmp_table_field() { return 0; } virtual Field *create_field_for_create_select(TABLE *table); virtual Field *create_field_for_schema(THD *thd, TABLE *table); - virtual const char *full_name() const { return name ? name : "???"; } + virtual const char *full_name() const { return name.str ? name.str : "???"; } const char *field_name_or_null() - { return real_item()->type() == Item::FIELD_ITEM ? name : NULL; } + { return real_item()->type() == Item::FIELD_ITEM ? name.str : NULL; } /* *result* family of methods is analog of *val* family (see above) but @@ -1216,7 +1198,10 @@ public: inline uint float_length(uint decimals_par) const { return decimals < FLOATING_POINT_DECIMALS ? (DBL_DIG+2+decimals_par) : DBL_DIG+8;} /* Returns total number of decimal digits */ - virtual uint decimal_precision() const; + virtual uint decimal_precision() const + { + return type_handler()->Item_decimal_precision(this); + } /* Returns the number of integer part digits only */ inline int decimal_int_part() const { return my_decimal_int_part(decimal_precision(), decimals); } @@ -1227,10 +1212,7 @@ public: */ uint decimal_scale() const { - return decimals < NOT_FIXED_DEC ? decimals : - is_temporal_type_with_time(field_type()) ? - TIME_SECOND_PART_DIGITS : - MY_MIN(max_length, DECIMAL_MAX_SCALE); + return type_handler()->Item_decimal_scale(this); } /* Returns how many digits a divisor adds into a division result. @@ -1251,15 +1233,25 @@ public: */ uint divisor_precision_increment() const { - return decimals < NOT_FIXED_DEC ? decimals : - is_temporal_type_with_time(field_type()) ? - TIME_SECOND_PART_DIGITS : - decimals; + return type_handler()->Item_divisor_precision_increment(this); } /** TIME or DATETIME precision of the item: 0..6 */ - uint temporal_precision(enum_field_types type); + uint time_precision() + { + return const_item() ? type_handler()->Item_time_precision(this) : + MY_MIN(decimals, TIME_SECOND_PART_DIGITS); + } + uint datetime_precision() + { + return const_item() ? type_handler()->Item_datetime_precision(this) : + MY_MIN(decimals, TIME_SECOND_PART_DIGITS); + } + virtual longlong val_int_min() const + { + return LONGLONG_MIN; + } /* Returns true if this is constant (during query execution, i.e. its value will not change until next fix_fields) and its value is known. @@ -1409,28 +1401,6 @@ public: return f_type == MYSQL_TYPE_TIME ? val_time_packed() : val_datetime_packed(); } - enum_field_types field_type_for_temporal_comparison(const Item *other) const - { - if (cmp_type() == TIME_RESULT) - { - if (other->cmp_type() == TIME_RESULT) - return Field::field_type_merge(field_type(), other->field_type()); - else - return field_type(); - } - else - { - if (other->cmp_type() == TIME_RESULT) - return other->field_type(); - DBUG_ASSERT(0); // Two non-temporal data types, we should not get to here - return MYSQL_TYPE_DATETIME; - } - } - // Get a temporal value to compare to another Item - longlong val_temporal_packed(const Item *other) - { - return val_temporal_packed(field_type_for_temporal_comparison(other)); - } bool get_seconds(ulonglong *sec, ulong *sec_part); virtual bool get_date_result(MYSQL_TIME *ltime, ulonglong fuzzydate) { return get_date(ltime,fuzzydate); } @@ -1513,14 +1483,9 @@ public: static CHARSET_INFO *default_charset(); - /* - For backward compatibility, to make numeric - data types return "binary" charset in client-side metadata. - */ - virtual CHARSET_INFO *charset_for_protocol(void) const + CHARSET_INFO *charset_for_protocol(void) const { - return cmp_type() == STRING_RESULT ? collation.collation : - &my_charset_bin; + return type_handler()->charset_for_protocol(this); }; virtual bool walk(Item_processor processor, bool walk_subquery, void *arg) @@ -1687,6 +1652,7 @@ public: */ virtual bool check_valid_arguments_processor(void *arg) { return 0; } virtual bool update_vcol_processor(void *arg) { return 0; } + virtual bool set_fields_as_dependent_processor(void *arg) { return 0; } /*============== End of Item processor list ======================*/ virtual Item *get_copy(THD *thd, MEM_ROOT *mem_root)=0; @@ -1731,22 +1697,38 @@ public: virtual Item **this_item_addr(THD *thd, Item **addr_arg) { return addr_arg; } // Row emulation - virtual uint cols() { return 1; } + virtual uint cols() const { return 1; } virtual Item* element_index(uint i) { return this; } + virtual bool element_index_by_name(uint *idx, const LEX_CSTRING &name) const + { + return true; // Error + } virtual Item** addr(uint i) { return 0; } virtual bool check_cols(uint c); + bool check_type_traditional_scalar(const char *opname) const; + bool check_type_scalar(const char *opname) const; + bool check_type_or_binary(const char *opname, const Type_handler *handler) const; + bool check_type_general_purpose_string(const char *opname) const; + bool check_type_can_return_int(const char *opname) const; + bool check_type_can_return_decimal(const char *opname) const; + bool check_type_can_return_real(const char *opname) const; + bool check_type_can_return_str(const char *opname) const; + bool check_type_can_return_text(const char *opname) const; + bool check_type_can_return_date(const char *opname) const; + bool check_type_can_return_time(const char *opname) const; // It is not row => null inside is impossible virtual bool null_inside() { return 0; } // used in row subselects to get value of elements virtual void bring_value() {} + const Type_handler *type_handler_long_or_longlong() const + { + return Type_handler::type_handler_long_or_longlong(max_char_length()); + } + virtual Field *create_tmp_field(bool group, TABLE *table) { - /* - Values with MY_INT32_NUM_DECIMAL_DIGITS digits may or may not fit into - Field_long : make them Field_longlong. - */ - return create_tmp_field(false, table, MY_INT32_NUM_DECIMAL_DIGITS - 2); + return tmp_table_field_from_field_type(table); } virtual Item_field *field_for_view_update() { return 0; } @@ -1849,10 +1831,14 @@ public: } virtual Field::geometry_type get_geometry_type() const { return Field::GEOM_GEOMETRY; }; + uint uint_geometry_type() const + { return get_geometry_type(); } + void set_geometry_type(uint type) + { + DBUG_ASSERT(0); + } String *check_well_formed_result(String *str, bool send_error= 0); bool eq_by_collation(Item *item, bool binary_cmp, CHARSET_INFO *cs); - uint32 max_char_length() const - { return max_length / collation.collation->mbmaxlen; } bool too_big_for_varchar() const { return max_char_length() > CONVERT_IF_BIGGER_TO_BLOB; } void fix_length_and_charset(uint32 max_char_length_arg, CHARSET_INFO *cs) @@ -1949,6 +1935,56 @@ inline Item* get_item_copy (THD *thd, MEM_ROOT *mem_root, T* item) } +class Type_geometry_attributes +{ + uint m_geometry_type; + static const uint m_geometry_type_unknown= Field::GEOM_GEOMETRYCOLLECTION + 1; + void copy(const Type_handler *handler, const Type_all_attributes *gattr) + { + // Ignore implicit NULLs + m_geometry_type= handler == &type_handler_geometry ? + gattr->uint_geometry_type() : + m_geometry_type_unknown; + } +public: + Type_geometry_attributes() + :m_geometry_type(m_geometry_type_unknown) + { } + Type_geometry_attributes(const Type_handler *handler, + const Type_all_attributes *gattr) + :m_geometry_type(m_geometry_type_unknown) + { + copy(handler, gattr); + } + void join(const Item *item) + { + // Ignore implicit NULLs + if (m_geometry_type == m_geometry_type_unknown) + copy(item->type_handler(), item); + else if (item->type_handler() == &type_handler_geometry) + { + m_geometry_type= + Field_geom::geometry_type_merge((Field_geom::geometry_type) + m_geometry_type, + (Field_geom::geometry_type) + item->uint_geometry_type()); + } + } + Field::geometry_type get_geometry_type() const + { + return m_geometry_type == m_geometry_type_unknown ? + Field::GEOM_GEOMETRY : + (Field::geometry_type) m_geometry_type; + } + void set_geometry_type(uint type) + { + DBUG_ASSERT(type <= m_geometry_type_unknown); + m_geometry_type= type; + } +}; + + + /** Compare two Items for List<Item>::add_unique() */ @@ -1956,6 +1992,120 @@ inline Item* get_item_copy (THD *thd, MEM_ROOT *mem_root, T* item) bool cmp_items(Item *a, Item *b); +/** + Array of items, e.g. function or aggerate function arguments. +*/ +class Item_args +{ +protected: + Item **args, *tmp_arg[2]; + uint arg_count; + void set_arguments(THD *thd, List<Item> &list); + bool walk_args(Item_processor processor, bool walk_subquery, void *arg) + { + for (uint i= 0; i < arg_count; i++) + { + if (args[i]->walk(processor, walk_subquery, arg)) + return true; + } + return false; + } + bool transform_args(THD *thd, Item_transformer transformer, uchar *arg); + void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *); + bool excl_dep_on_table(table_map tab_map) + { + for (uint i= 0; i < arg_count; i++) + { + if (args[i]->const_item()) + continue; + if (!args[i]->excl_dep_on_table(tab_map)) + return false; + } + return true; + } + bool excl_dep_on_grouping_fields(st_select_lex *sel) + { + for (uint i= 0; i < arg_count; i++) + { + if (args[i]->const_item()) + continue; + if (!args[i]->excl_dep_on_grouping_fields(sel)) + return false; + } + return true; + } +public: + Item_args(void) + :args(NULL), arg_count(0) + { } + Item_args(Item *a) + :args(tmp_arg), arg_count(1) + { + args[0]= a; + } + Item_args(Item *a, Item *b) + :args(tmp_arg), arg_count(2) + { + args[0]= a; args[1]= b; + } + Item_args(THD *thd, Item *a, Item *b, Item *c) + { + arg_count= 0; + if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 3))) + { + arg_count= 3; + args[0]= a; args[1]= b; args[2]= c; + } + } + Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d) + { + arg_count= 0; + if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 4))) + { + arg_count= 4; + args[0]= a; args[1]= b; args[2]= c; args[3]= d; + } + } + Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e) + { + arg_count= 5; + if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 5))) + { + arg_count= 5; + args[0]= a; args[1]= b; args[2]= c; args[3]= d; args[4]= e; + } + } + Item_args(THD *thd, List<Item> &list) + { + set_arguments(thd, list); + } + Item_args(THD *thd, const Item_args *other); + bool alloc_arguments(THD *thd, uint count); + void add_argument(Item *item) + { + args[arg_count++]= item; + } + inline Item **arguments() const { return args; } + inline uint argument_count() const { return arg_count; } + inline void remove_arguments() { arg_count=0; } +}; + + +class Item_spvar_args: public Item_args +{ + TABLE *m_table; +public: + Item_spvar_args():Item_args(), m_table(NULL) { } + ~Item_spvar_args(); + bool row_create_items(THD *thd, List<Spvar_definition> *list); + Field *get_row_field(uint i) const + { + DBUG_ASSERT(m_table); + return m_table->field[i]; + } +}; + + /* Class to be used to enumerate all field references in an item tree. This includes references to outside but not fields of the tables within a @@ -1981,7 +2131,6 @@ public: Field_enumerator() {} /* Remove gcc warning */ }; -class sp_head; class Item_string; @@ -2090,7 +2239,10 @@ public: done again between subsequent executions of a prepared statement. */ if (orig_name) - name= orig_name; + { + name.str= orig_name; + name.length= strlen(orig_name); + } } }; @@ -2111,8 +2263,9 @@ protected: */ THD *m_thd; + bool fix_fields_from_item(THD *thd, Item **, const Item *); public: - LEX_STRING m_name; + LEX_CSTRING m_name; public: #ifndef DBUG_OFF @@ -2124,10 +2277,10 @@ public: #endif public: - Item_sp_variable(THD *thd, char *sp_var_name_str, uint sp_var_name_length); + Item_sp_variable(THD *thd, const LEX_CSTRING *sp_var_name); public: - bool fix_fields(THD *thd, Item **); + bool fix_fields(THD *thd, Item **)= 0; double val_real(); longlong val_int(); @@ -2136,12 +2289,12 @@ public: bool is_null(); public: - inline void make_field(THD *thd, Send_field *field); + void make_field(THD *thd, Send_field *field); inline bool const_item() const; inline int save_in_field(Field *field, bool no_conversions); - inline bool send(Protocol *protocol, String *str); + inline bool send(Protocol *protocol, st_value *buffer); bool check_vcol_func_processor(void *arg) { return mark_unsupported_function(m_name.str, arg, VCOL_IMPOSSIBLE); @@ -2152,17 +2305,6 @@ public: Item_sp_variable inline implementation. *****************************************************************************/ -inline void Item_sp_variable::make_field(THD *thd, Send_field *field) -{ - Item *it= this_item(); - - if (name) - it->set_name(thd, name, (uint) strlen(name), system_charset_info); - else - it->set_name(thd, m_name.str, (uint) m_name.length, system_charset_info); - it->make_field(thd, field); -} - inline bool Item_sp_variable::const_item() const { return TRUE; @@ -2173,9 +2315,9 @@ inline int Item_sp_variable::save_in_field(Field *field, bool no_conversions) return this_item()->save_in_field(field, no_conversions); } -inline bool Item_sp_variable::send(Protocol *protocol, String *str) +inline bool Item_sp_variable::send(Protocol *protocol, st_value *buffer) { - return this_item()->send(protocol, str); + return this_item()->send(protocol, buffer); } @@ -2189,14 +2331,18 @@ class Item_splocal :public Item_sp_variable, public Rewritable_query_parameter, public Type_handler_hybrid_field_type { +protected: uint m_var_idx; Type m_type; + + bool append_value_for_log(THD *thd, String *str); public: - Item_splocal(THD *thd, const LEX_STRING &sp_var_name, uint sp_var_idx, + Item_splocal(THD *thd, const LEX_CSTRING *sp_var_name, uint sp_var_idx, enum_field_types sp_var_type, uint pos_in_q= 0, uint len_in_q= 0); + bool fix_fields(THD *, Item **); Item *this_item(); const Item *this_item() const; Item **this_item_addr(THD *thd, Item **); @@ -2204,17 +2350,17 @@ public: virtual void print(String *str, enum_query_type query_type); public: - inline const LEX_STRING *my_name() const; + inline const LEX_CSTRING *my_name() const; inline uint get_var_idx() const; inline enum Type type() const; - enum_field_types field_type() const - { return Type_handler_hybrid_field_type::field_type(); } - enum Item_result result_type () const - { return Type_handler_hybrid_field_type::result_type(); } - enum Item_result cmp_type () const - { return Type_handler_hybrid_field_type::cmp_type(); } + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } + uint cols() const { return this_item()->cols(); } + Item* element_index(uint i) { return this_item()->element_index(i); } + Item** addr(uint i) { return this_item()->addr(i); } + bool check_cols(uint c); private: bool set_value(THD *thd, sp_rcontext *ctx, Item **it); @@ -2231,13 +2377,108 @@ public: bool append_for_log(THD *thd, String *str); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } + + /* + Override the inherited create_field_for_create_select(), + because we want to preserve the exact data type for: + DECLARE a1 INT; + DECLARE a2 TYPE OF t1.a2; + CREATE TABLE t1 AS SELECT a1, a2; + The inherited implementation would create a column + based on result_type(), which is less exact. + */ + Field *create_field_for_create_select(TABLE *table) + { return tmp_table_field_from_field_type(table); } +}; + + +class Item_splocal_row: public Item_splocal +{ +public: + Item_splocal_row(THD *thd, const LEX_CSTRING *sp_var_name, + uint sp_var_idx, uint pos_in_q, uint len_in_q) + :Item_splocal(thd, sp_var_name, sp_var_idx, MYSQL_TYPE_NULL, + pos_in_q, len_in_q) + { + set_handler(&type_handler_row); + } + enum Type type() const { return ROW_ITEM; } +}; + + +/** + An Item_splocal variant whose data type becomes known only at + sp_rcontext creation time, e.g. "DECLARE var1 t1.col1%TYPE". +*/ +class Item_splocal_with_delayed_data_type: public Item_splocal +{ +public: + Item_splocal_with_delayed_data_type(THD *thd, + const LEX_CSTRING *sp_var_name, + uint sp_var_idx, + uint pos_in_q, uint len_in_q) + :Item_splocal(thd, sp_var_name, sp_var_idx, MYSQL_TYPE_NULL, + pos_in_q, len_in_q) + { } +}; + + +/** + SP variables that are fields of a ROW. + DELCARE r ROW(a INT,b INT); + SELECT r.a; -- This is handled by Item_splocal_row_field +*/ +class Item_splocal_row_field :public Item_splocal +{ +protected: + LEX_CSTRING m_field_name; + uint m_field_idx; + bool set_value(THD *thd, sp_rcontext *ctx, Item **it); +public: + Item_splocal_row_field(THD *thd, + const LEX_CSTRING *sp_var_name, + const LEX_CSTRING *sp_field_name, + uint sp_var_idx, uint sp_field_idx, + enum_field_types sp_var_type, + uint pos_in_q= 0, uint len_in_q= 0) + :Item_splocal(thd, sp_var_name, sp_var_idx, sp_var_type, + pos_in_q, len_in_q), + m_field_name(*sp_field_name), + m_field_idx(sp_field_idx) + { } + bool fix_fields(THD *thd, Item **); + Item *this_item(); + const Item *this_item() const; + Item **this_item_addr(THD *thd, Item **); + bool append_for_log(THD *thd, String *str); + void print(String *str, enum_query_type query_type); }; + +class Item_splocal_row_field_by_name :public Item_splocal_row_field +{ + bool set_value(THD *thd, sp_rcontext *ctx, Item **it); +public: + Item_splocal_row_field_by_name(THD *thd, + const LEX_CSTRING *sp_var_name, + const LEX_CSTRING *sp_field_name, + uint sp_var_idx, + enum_field_types sp_var_type, + uint pos_in_q= 0, uint len_in_q= 0) + :Item_splocal_row_field(thd, sp_var_name, sp_field_name, + sp_var_idx, 0 /* field index will be set later */, + sp_var_type, pos_in_q, len_in_q) + { } + bool fix_fields(THD *thd, Item **it); + void print(String *str, enum_query_type query_type); +}; + + /***************************************************************************** Item_splocal inline implementation. *****************************************************************************/ -inline const LEX_STRING *Item_splocal::my_name() const +inline const LEX_CSTRING *Item_splocal::my_name() const { return &m_name; } @@ -2262,13 +2503,13 @@ public: Item_case_expr(THD *thd, uint case_expr_id); public: + bool fix_fields(THD *thd, Item **); Item *this_item(); const Item *this_item() const; Item **this_item_addr(THD *thd, Item **); inline enum Type type() const; - inline Item_result result_type() const; - enum_field_types field_type() const { return this_item()->field_type(); } + const Type_handler *type_handler() const { return this_item()->type_handler(); } public: /* @@ -2292,12 +2533,6 @@ inline enum Item::Type Item_case_expr::type() const return this_item()->type(); } -inline Item_result Item_case_expr::result_type() const -{ - return this_item()->result_type(); -} - - /* NAME_CONST(given_name, const_value). This 'function' has all properties of the supplied const_value (which is @@ -2330,14 +2565,9 @@ public: bool is_null(); virtual void print(String *str, enum_query_type query_type); - enum_field_types field_type() const - { - return value_item->field_type(); - } - - Item_result result_type() const + const Type_handler *type_handler() const { - return value_item->result_type(); + return value_item->type_handler(); } bool const_item() const @@ -2350,9 +2580,9 @@ public: return value_item->save_in_field(field, no_conversions); } - bool send(Protocol *protocol, String *str) + bool send(Protocol *protocol, st_value *buffer) { - return value_item->send(protocol, str); + return value_item->send(protocol, buffer); } bool check_vcol_func_processor(void *arg) { @@ -2414,13 +2644,13 @@ protected: */ const char *orig_db_name; const char *orig_table_name; - const char *orig_field_name; + LEX_CSTRING orig_field_name; public: Name_resolution_context *context; const char *db_name; const char *table_name; - const char *field_name; + LEX_CSTRING field_name; bool alias_name_used; /* true if item was resolved against alias */ /* Cached value of index for this field in table->field array, used by prep. @@ -2450,9 +2680,9 @@ public: bool can_be_depended; Item_ident(THD *thd, Name_resolution_context *context_arg, const char *db_name_arg, const char *table_name_arg, - const char *field_name_arg); + const LEX_CSTRING *field_name_arg); Item_ident(THD *thd, Item_ident *item); - Item_ident(THD *thd, TABLE_LIST *view_arg, const char *field_name_arg); + Item_ident(THD *thd, TABLE_LIST *view_arg, const LEX_CSTRING *field_name_arg); const char *full_name() const; void cleanup(); st_select_lex *get_depended_from() const; @@ -2481,17 +2711,20 @@ public: Item_ident_for_show(THD *thd, Field *par_field, const char *db_arg, const char *table_name_arg): Item(thd), field(par_field), db_name(db_arg), table_name(table_name_arg) - {} - + { + Type_std_attributes::set(par_field); + } enum Type type() const { return FIELD_ITEM; } double val_real() { return field->val_real(); } longlong val_int() { return field->val_int(); } String *val_str(String *str) { return field->val_str(str); } my_decimal *val_decimal(my_decimal *dec) { return field->val_decimal(dec); } void make_field(THD *thd, Send_field *tmp_field); - CHARSET_INFO *charset_for_protocol(void) const - { return field->charset_for_protocol(); } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + const Type_handler *type_handler() const + { + const Type_handler *handler= field->type_handler(); + return handler->type_handler_for_item_field(); + } Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_ident_for_show>(thd, mem_root, this); } }; @@ -2513,7 +2746,7 @@ public: bool any_privileges; Item_field(THD *thd, Name_resolution_context *context_arg, const char *db_arg,const char *table_name_arg, - const char *field_name_arg); + const LEX_CSTRING *field_name_arg); /* Constructor needed to process subselect with temporary tables (see Item) */ @@ -2543,7 +2776,7 @@ public: my_decimal *val_decimal_result(my_decimal *); bool val_bool_result(); bool is_null_result(); - bool send(Protocol *protocol, String *str_arg); + bool send(Protocol *protocol, st_value *buffer); void reset_field(Field *f); bool fix_fields(THD *, Item **); void fix_after_pullout(st_select_lex *new_parent, Item **ref); @@ -2553,18 +2786,22 @@ public: fast_field_copier setup_fast_field_copier(Field *field); table_map used_tables() const; table_map all_used_tables() const; - enum Item_result result_type () const + const Type_handler *type_handler() const { - return field->result_type(); + const Type_handler *handler= field->type_handler(); + return handler->type_handler_for_item_field(); } - Item_result cast_to_int_type() const + const Type_handler *cast_to_int_type_handler() const { - return field->cmp_type(); + return field->type_handler()->cast_to_int_type_handler(); } - enum_field_types field_type() const + const Type_handler *real_type_handler() const { - return field->type(); + if (field->is_created_from_null_item) + return &type_handler_null; + return field->type_handler(); } + TYPELIB *get_typelib() const { return field->get_typelib(); } enum_monotonicity_info get_monotonicity_info() const { return MONOTONIC_STRICT_INCREASING; @@ -2639,9 +2876,18 @@ public: if (field && (field->unireg_check == Field::NEXT_NUMBER)) { // Auto increment fields are unsupported - return mark_unsupported_function(field_name, arg, VCOL_FIELD_REF | VCOL_AUTO_INC); + return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF | VCOL_AUTO_INC); + } + return mark_unsupported_function(field_name.str, arg, VCOL_FIELD_REF); + } + bool set_fields_as_dependent_processor(void *arg) + { + if (!(used_tables() & OUTER_REF_TABLE_BIT)) + { + depended_from= (st_select_lex *) arg; + item_equal= NULL; } - return mark_unsupported_function(field_name, arg, VCOL_FIELD_REF); + return 0; } void cleanup(); Item_equal *get_item_equal() { return item_equal; } @@ -2649,7 +2895,7 @@ public: Item_equal *find_item_equal(COND_EQUAL *cond_equal); Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *); Item *replace_equal_field(THD *thd, uchar *arg); - inline uint32 max_disp_length() { return field->max_display_length(); } + uint32 max_display_length() const { return field->max_display_length(); } Item_field *field_for_view_update() { return this; } int fix_outer_field(THD *thd, Field **field, Item **reference); virtual Item *update_value_transformer(THD *thd, uchar *select_arg); @@ -2676,14 +2922,44 @@ public: DBUG_ASSERT(field_type() == MYSQL_TYPE_GEOMETRY); return field->get_geometry_type(); } - CHARSET_INFO *charset_for_protocol(void) const - { return field->charset_for_protocol(); } friend class Item_default_value; friend class Item_insert_value; friend class st_select_lex_unit; }; +/** + Item_field for the ROW data type +*/ +class Item_field_row: public Item_field, + public Item_spvar_args +{ +public: + Item_field_row(THD *thd, Field *field) + :Item_field(thd, field), + Item_spvar_args() + { } + + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_field_row>(thd, mem_root, this); } + + const Type_handler *type_handler() const { return &type_handler_row; } + uint cols() const { return arg_count; } + bool element_index_by_name(uint *idx, const LEX_CSTRING &name) const; + Item* element_index(uint i) { return arg_count ? args[i] : this; } + Item** addr(uint i) { return arg_count ? args + i : NULL; } + bool check_cols(uint c) + { + if (cols() != c) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), c); + return true; + } + return false; + } +}; + + /* @brief Item_temptable_field is the same as Item_field, except that print() @@ -2727,12 +3003,13 @@ public: class Item_null :public Item_basic_constant { public: - Item_null(THD *thd, char *name_par=0, CHARSET_INFO *cs= &my_charset_bin): + Item_null(THD *thd, const char *name_par=0, CHARSET_INFO *cs= &my_charset_bin): Item_basic_constant(thd) { maybe_null= null_value= TRUE; max_length= 0; - name= name_par ? name_par : (char*) "NULL"; + name.str= name_par ? name_par : "NULL"; + name.length= strlen(name.str); fixed= 1; collation.set(cs, DERIVATION_IGNORABLE, MY_REPERTOIRE_ASCII); } @@ -2744,9 +3021,8 @@ public: my_decimal *val_decimal(my_decimal *); int save_in_field(Field *field, bool no_conversions); int save_safe_in_field(Field *field); - bool send(Protocol *protocol, String *str); - enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_NULL; } + bool send(Protocol *protocol, st_value *buffer); + const Type_handler *type_handler() const { return &type_handler_null; } bool basic_const_item() const { return 1; } Item *clone_item(THD *thd); bool is_null() { return 1; } @@ -2794,7 +3070,8 @@ public: class Item_param :public Item_basic_value, private Settable_routine_parameter, public Rewritable_query_parameter, - public Type_handler_hybrid_field_type + public Type_handler_hybrid_field_type, + public Type_geometry_attributes { /* NO_VALUE is a special value meaning that the parameter has not been @@ -2921,14 +3198,17 @@ public: MYSQL_TIME time; } value; - enum_field_types field_type() const - { return Type_handler_hybrid_field_type::field_type(); } - enum Item_result result_type () const - { return Type_handler_hybrid_field_type::result_type(); } - enum Item_result cmp_type () const - { return Type_handler_hybrid_field_type::cmp_type(); } + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } - Item_param(THD *thd, uint pos_in_query_arg); + Field::geometry_type get_geometry_type() const + { return Type_geometry_attributes::get_geometry_type(); }; + + void set_geometry_type(uint type) + { Type_geometry_attributes::set_geometry_type(type); } + + Item_param(THD *thd, const LEX_CSTRING *name_arg, + uint pos_in_query_arg, uint len_in_query_arg); enum Type type() const { @@ -3053,12 +3333,21 @@ public: { max_length=length; fixed= 1; unsigned_flag= 1; } Item_int(THD *thd, const char *str_arg,longlong i,uint length): Item_num(thd), value(i) - { max_length=length; name=(char*) str_arg; fixed= 1; } + { + max_length=length; + name.str= str_arg; name.length= safe_strlen(name.str); + fixed= 1; + } Item_int(THD *thd, const char *str_arg, uint length=64); enum Type type() const { return INT_ITEM; } - enum Item_result result_type () const { return INT_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const + { return type_handler_long_or_longlong(); } + Field *create_tmp_field(bool group, TABLE *table) + { return tmp_table_field_from_field_type(table); } + Field *create_field_for_create_select(TABLE *table) + { return tmp_table_field_from_field_type(table); } longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } + longlong val_int_min() const { DBUG_ASSERT(fixed == 1); return value; } double val_real() { DBUG_ASSERT(fixed == 1); return (double) value; } my_decimal *val_decimal(my_decimal *); String *val_str(String*); @@ -3138,8 +3427,7 @@ public: Item_decimal(THD *thd, const uchar *bin, int precision, int scale); enum Type type() const { return DECIMAL_ITEM; } - enum Item_result result_type () const { return DECIMAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; } + const Type_handler *type_handler() const { return &type_handler_newdecimal; } longlong val_int(); double val_real(); String *val_str(String*); @@ -3159,16 +3447,17 @@ public: class Item_float :public Item_num { - char *presentation; + const char *presentation; public: double value; Item_float(THD *thd, const char *str_arg, uint length); Item_float(THD *thd, const char *str, double val_arg, uint decimal_par, uint length): Item_num(thd), value(val_arg) { - presentation= name=(char*) str; + presentation= name.str= str; + name.length= safe_strlen(str); decimals=(uint8) decimal_par; - max_length=length; + max_length= length; fixed= 1; } Item_float(THD *thd, double value_par, uint decimal_par): @@ -3179,7 +3468,7 @@ public: } int save_in_field(Field *field, bool no_conversions); enum Type type() const { return REAL_ITEM; } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + const Type_handler *type_handler() const { return &type_handler_double; } double val_real() { DBUG_ASSERT(fixed == 1); return value; } longlong val_int() { @@ -3294,7 +3583,7 @@ public: { str_value.set_or_copy_aligned(str, length, cs); fix_from_value(dv, Metadata(&str_value)); - set_name(thd, name_par, 0, system_charset_info); + set_name(thd, name_par, (uint) safe_strlen(name_par), system_charset_info); } Item_string(THD *thd, const char *name_par, const char *str, uint length, CHARSET_INFO *cs, Derivation dv, uint repertoire): @@ -3302,7 +3591,7 @@ public: { str_value.set_or_copy_aligned(str, length, cs); fix_from_value(dv, Metadata(&str_value, repertoire)); - set_name(thd, name_par, 0, system_charset_info); + set_name(thd, name_par, (uint) safe_strlen(name_par), system_charset_info); } void print_value(String *to) const { @@ -3318,8 +3607,7 @@ public: } my_decimal *val_decimal(my_decimal *); int save_in_field(Field *field, bool no_conversions); - enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } + const Type_handler *type_handler() const { return &type_handler_varchar; } bool basic_const_item() const { return 1; } bool eq(const Item *item, bool binary_cmp) const { @@ -3330,7 +3618,7 @@ public: { return const_charset_converter(thd, tocs, true); } - inline void append(char *str, uint length) + inline void append(const char *str, uint length) { str_value.append(str, length); max_length= str_value.numchars() * collation.collation->mbmaxlen; @@ -3365,7 +3653,7 @@ public: String *check_well_formed_result(bool send_error) { return Item::check_well_formed_result(&str_value, send_error); } - enum_field_types odbc_temporal_literal_type(const LEX_STRING *type_str) const + enum_field_types odbc_temporal_literal_type(const LEX_CSTRING *type_str) const { /* If string is a reasonably short pure ASCII string literal, @@ -3438,7 +3726,7 @@ public: DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII) { } Item_string_ascii(THD *thd, const char *str): - Item_string(thd, str, (uint)strlen(str), &my_charset_latin1, + Item_string(thd, str, (uint) strlen(str), &my_charset_latin1, DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII) { } }; @@ -3504,7 +3792,10 @@ public: Item_partition_func_safe_string(thd, name_arg, length_arg, &my_charset_bin), date_time_field_type(field_type_arg) { decimals= 0; } - enum_field_types field_type() const { return date_time_field_type; } + const Type_handler *type_handler() const + { + return Type_handler::get_handler_by_field_type(date_time_field_type); + } }; @@ -3512,12 +3803,22 @@ class Item_blob :public Item_partition_func_safe_string { public: Item_blob(THD *thd, const char *name_arg, uint length): - Item_partition_func_safe_string(thd, name_arg, (uint) strlen(name_arg), &my_charset_bin) + Item_partition_func_safe_string(thd, name_arg, (uint) safe_strlen(name_arg), + &my_charset_bin) { max_length= length; } enum Type type() const { return TYPE_HOLDER; } - enum_field_types field_type() const { return MYSQL_TYPE_BLOB; } + const Type_handler *type_handler() const + { + return Type_handler::blob_type_handler(max_length); + } + const Type_handler *real_type_handler() const + { + // Should not be called, Item_blob is used for SHOW purposes only. + DBUG_ASSERT(0); + return &type_handler_varchar; + } Field *create_field_for_schema(THD *thd, TABLE *table) - { return tmp_table_field_from_field_type(table, false, true); } + { return tmp_table_field_from_field_type(table); } }; @@ -3534,7 +3835,11 @@ public: CHARSET_INFO *cs= NULL): Item_partition_func_safe_string(thd, "", 0, cs ? cs : &my_charset_utf8_general_ci) - { name=(char*) header; max_length= length * collation.collation->mbmaxlen; } + { + name.str= header; + name.length= strlen(name.str); + max_length= length * collation.collation->mbmaxlen; + } void make_field(THD *thd, Send_field *field); }; @@ -3549,7 +3854,10 @@ public: { unsigned_flag=1; } - enum_field_types field_type() const { return int_field_type; } + const Type_handler *type_handler() const + { + return Type_handler::get_handler_by_field_type(int_field_type); + } }; @@ -3571,8 +3879,7 @@ public: hex_string_init(thd, str, str_length); } enum Type type() const { return VARBIN_ITEM; } - enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } + const Type_handler *type_handler() const { return &type_handler_varchar; } virtual Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs) { return const_charset_converter(thd, tocs, true); @@ -3582,7 +3889,7 @@ public: bool eq(const Item *item, bool binary_cmp) const { return item->basic_const_item() && item->type() == type() && - item->cast_to_int_type() == cast_to_int_type() && + item->cast_to_int_type_handler() == cast_to_int_type_handler() && str_value.bin_eq(&((Item_hex_constant*)item)->str_value); } String *val_str(String*) { DBUG_ASSERT(fixed == 1); return &str_value; } @@ -3600,6 +3907,7 @@ public: Item_hex_hybrid(THD *thd): Item_hex_constant(thd) {} Item_hex_hybrid(THD *thd, const char *str, uint str_length): Item_hex_constant(thd, str, str_length) {} + uint decimal_precision() const; double val_real() { DBUG_ASSERT(fixed == 1); @@ -3624,7 +3932,10 @@ public: field->set_notnull(); return field->store_hex_hybrid(str_value.ptr(), str_value.length()); } - enum Item_result cast_to_int_type() const { return INT_RESULT; } + const Type_handler *cast_to_int_type_handler() const + { + return &type_handler_longlong; + } void print(String *str, enum_query_type query_type); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_hex_hybrid>(thd, mem_root, this); } @@ -3666,7 +3977,6 @@ public: return field->store(str_value.ptr(), str_value.length(), collation.collation); } - enum Item_result cast_to_int_type() const { return STRING_RESULT; } void print(String *str, enum_query_type query_type); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_hex_string>(thd, mem_root, this); } @@ -3706,8 +4016,6 @@ public: bool const_item() const { return true; } enum Type type() const { return DATE_ITEM; } bool eq(const Item *item, bool binary_cmp) const; - enum Item_result result_type () const { return STRING_RESULT; } - Item_result cmp_type() const { return TIME_RESULT; } bool check_partition_func_processor(void *int_arg) {return FALSE;} @@ -3723,7 +4031,7 @@ public: my_decimal *val_decimal(my_decimal *decimal_value) { return val_decimal_from_date(decimal_value); } int save_in_field(Field *field, bool no_conversions) - { return save_date_in_field(field); } + { return save_date_in_field(field, no_conversions); } }; @@ -3747,7 +4055,7 @@ public: */ maybe_null= !ltime->month || !ltime->day; } - enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + const Type_handler *type_handler() const { return &type_handler_newdate; } void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); @@ -3768,7 +4076,7 @@ public: max_length= MIN_TIME_WIDTH + (decimals ? decimals + 1 : 0); fixed= 1; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + const Type_handler *type_handler() const { return &type_handler_time2; } void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); @@ -3791,7 +4099,7 @@ public: // See the comment on maybe_null in Item_date_literal maybe_null= !ltime->month || !ltime->day; } - enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } + const Type_handler *type_handler() const { return &type_handler_datetime2; } void print(String *str, enum_query_type query_type); Item *clone_item(THD *thd); bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); @@ -3857,100 +4165,6 @@ public: }; -/** - Array of items, e.g. function or aggerate function arguments. -*/ -class Item_args -{ -protected: - Item **args, *tmp_arg[2]; - uint arg_count; - void set_arguments(THD *thd, List<Item> &list); - bool walk_args(Item_processor processor, bool walk_subquery, void *arg) - { - for (uint i= 0; i < arg_count; i++) - { - if (args[i]->walk(processor, walk_subquery, arg)) - return true; - } - return false; - } - bool transform_args(THD *thd, Item_transformer transformer, uchar *arg); - void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *); - bool excl_dep_on_table(table_map tab_map) - { - for (uint i= 0; i < arg_count; i++) - { - if (args[i]->const_item()) - continue; - if (!args[i]->excl_dep_on_table(tab_map)) - return false; - } - return true; - } - bool excl_dep_on_grouping_fields(st_select_lex *sel) - { - for (uint i= 0; i < arg_count; i++) - { - if (args[i]->const_item()) - continue; - if (!args[i]->excl_dep_on_grouping_fields(sel)) - return false; - } - return true; - } -public: - Item_args(void) - :args(NULL), arg_count(0) - { } - Item_args(Item *a) - :args(tmp_arg), arg_count(1) - { - args[0]= a; - } - Item_args(Item *a, Item *b) - :args(tmp_arg), arg_count(2) - { - args[0]= a; args[1]= b; - } - Item_args(THD *thd, Item *a, Item *b, Item *c) - { - arg_count= 0; - if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 3))) - { - arg_count= 3; - args[0]= a; args[1]= b; args[2]= c; - } - } - Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d) - { - arg_count= 0; - if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 4))) - { - arg_count= 4; - args[0]= a; args[1]= b; args[2]= c; args[3]= d; - } - } - Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e) - { - arg_count= 5; - if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 5))) - { - arg_count= 5; - args[0]= a; args[1]= b; args[2]= c; args[3]= d; args[4]= e; - } - } - Item_args(THD *thd, List<Item> &list) - { - set_arguments(thd, list); - } - Item_args(THD *thd, const Item_args *other); - inline Item **arguments() const { return args; } - inline uint argument_count() const { return arg_count; } - inline void remove_arguments() { arg_count=0; } -}; - - class Used_tables_and_const_cache { public: @@ -4020,80 +4234,31 @@ class Item_func_or_sum: public Item_result_field, public Item_args, public Used_tables_and_const_cache { - bool agg_item_collations(DTCollation &c, const char *name, - Item **items, uint nitems, - uint flags, int item_sep); - bool agg_item_set_converter(const DTCollation &coll, const char *fname, - Item **args, uint nargs, - uint flags, int item_sep); protected: - /* - Collect arguments' character sets together. - We allow to apply automatic character set conversion in some cases. - The conditions when conversion is possible are: - - arguments A and B have different charsets - - A wins according to coercibility rules - (i.e. a column is stronger than a string constant, - an explicit COLLATE clause is stronger than a column) - - character set of A is either superset for character set of B, - or B is a string constant which can be converted into the - character set of A without data loss. - - If all of the above is true, then it's possible to convert - B into the character set of A, and then compare according - to the collation of A. - - For functions with more than two arguments: - - collect(A,B,C) ::= collect(collect(A,B),C) - - Since this function calls THD::change_item_tree() on the passed Item ** - pointers, it is necessary to pass the original Item **'s, not copies. - Otherwise their values will not be properly restored (see BUG#20769). - If the items are not consecutive (eg. args[2] and args[5]), use the - item_sep argument, ie. - - agg_item_charsets(coll, fname, &args[2], 2, flags, 3) - */ bool agg_arg_charsets(DTCollation &c, Item **items, uint nitems, uint flags, int item_sep) { - if (agg_item_collations(c, func_name(), items, nitems, flags, item_sep)) - return true; - - return agg_item_set_converter(c, func_name(), items, nitems, - flags, item_sep); + return Type_std_attributes::agg_arg_charsets(c, func_name(), + items, nitems, + flags, item_sep); } - /* - Aggregate arguments for string result, e.g: CONCAT(a,b) - - convert to @@character_set_connection if all arguments are numbers - - allow DERIVATION_NONE - */ bool agg_arg_charsets_for_string_result(DTCollation &c, Item **items, uint nitems, int item_sep= 1) { - uint flags= MY_COLL_ALLOW_SUPERSET_CONV | - MY_COLL_ALLOW_COERCIBLE_CONV | - MY_COLL_ALLOW_NUMERIC_CONV; - return agg_arg_charsets(c, items, nitems, flags, item_sep); + return Type_std_attributes:: + agg_arg_charsets_for_string_result(c, func_name(), + items, nitems, item_sep); } - /* - Aggregate arguments for string result, when some comparison - is involved internally, e.g: REPLACE(a,b,c) - - convert to @@character_set_connection if all arguments are numbers - - disallow DERIVATION_NONE - */ bool agg_arg_charsets_for_string_result_with_comparison(DTCollation &c, Item **items, uint nitems, int item_sep= 1) { - uint flags= MY_COLL_ALLOW_SUPERSET_CONV | - MY_COLL_ALLOW_COERCIBLE_CONV | - MY_COLL_ALLOW_NUMERIC_CONV | - MY_COLL_DISALLOW_NONE; - return agg_arg_charsets(c, items, nitems, flags, item_sep); + return Type_std_attributes:: + agg_arg_charsets_for_string_result_with_comparison(c, func_name(), + items, nitems, + item_sep); } /* @@ -4105,13 +4270,10 @@ protected: Item **items, uint nitems, int item_sep= 1) { - uint flags= MY_COLL_ALLOW_SUPERSET_CONV | - MY_COLL_ALLOW_COERCIBLE_CONV | - MY_COLL_DISALLOW_NONE; - return agg_arg_charsets(c, items, nitems, flags, item_sep); + return Type_std_attributes:: + agg_arg_charsets_for_comparison(c, func_name(), items, nitems, item_sep); } - public: // This method is used by Arg_comparator bool agg_arg_charsets_for_comparison(CHARSET_INFO **cs, Item **a, Item **b) @@ -4194,7 +4356,7 @@ public: bool reference_trough_name; Item_ref(THD *thd, Name_resolution_context *context_arg, const char *db_arg, const char *table_name_arg, - const char *field_name_arg): + const LEX_CSTRING *field_name_arg): Item_ident(thd, context_arg, db_arg, table_name_arg, field_name_arg), set_properties_only(0), ref(0), reference_trough_name(1) {} /* @@ -4212,10 +4374,10 @@ public: with Bar, and if we have a more broader set of problems like this. */ Item_ref(THD *thd, Name_resolution_context *context_arg, Item **item, - const char *table_name_arg, const char *field_name_arg, + const char *table_name_arg, const LEX_CSTRING *field_name_arg, bool alias_name_used_arg= FALSE); Item_ref(THD *thd, TABLE_LIST *view_arg, Item **item, - const char *field_name_arg, bool alias_name_used_arg= FALSE); + const LEX_CSTRING *field_name_arg, bool alias_name_used_arg= FALSE); /* Constructor need to process subselect with temporary tables (see Item) */ Item_ref(THD *thd, Item_ref *item) @@ -4243,7 +4405,7 @@ public: my_decimal *val_decimal_result(my_decimal *); bool val_bool_result(); bool is_null_result(); - bool send(Protocol *prot, String *tmp); + bool send(Protocol *prot, st_value *buffer); void make_field(THD *thd, Send_field *field); bool fix_fields(THD *, Item **); void fix_after_pullout(st_select_lex *new_parent, Item **ref); @@ -4251,8 +4413,9 @@ public: void save_org_in_field(Field *field, fast_field_copier optimizer_data); fast_field_copier setup_fast_field_copier(Field *field) { return (*ref)->setup_fast_field_copier(field); } - enum Item_result result_type () const { return (*ref)->result_type(); } - enum_field_types field_type() const { return (*ref)->field_type(); } + const Type_handler *type_handler() const { return (*ref)->type_handler(); } + const Type_handler *real_type_handler() const + { return (*ref)->real_type_handler(); } Field *get_tmp_table_field() { return result_field ? result_field : (*ref)->get_tmp_table_field(); } Item *get_tmp_table_item(THD *thd); @@ -4290,6 +4453,11 @@ public: { return ref ? (*ref)->real_item() : this; } + TYPELIB *get_typelib() const + { + return ref ? (*ref)->get_typelib() : NULL; + } + bool walk(Item_processor processor, bool walk_subquery, void *arg) { if (ref && *ref) @@ -4318,7 +4486,7 @@ public: virtual Ref_Type ref_type() { return REF; } // Row emulation: forwarding of ROW-related calls to ref - uint cols() + uint cols() const { return ref && result_type() == ROW_RESULT ? (*ref)->cols() : 1; } @@ -4404,7 +4572,7 @@ class Item_direct_ref :public Item_ref public: Item_direct_ref(THD *thd, Name_resolution_context *context_arg, Item **item, const char *table_name_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, bool alias_name_used_arg= FALSE): Item_ref(thd, context_arg, item, table_name_arg, field_name_arg, alias_name_used_arg) @@ -4412,7 +4580,7 @@ public: /* Constructor need to process subselect with temporary tables (see Item) */ Item_direct_ref(THD *thd, Item_direct_ref *item) : Item_ref(thd, item) {} Item_direct_ref(THD *thd, TABLE_LIST *view_arg, Item **item, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, bool alias_name_used_arg= FALSE): Item_ref(thd, view_arg, item, field_name_arg, alias_name_used_arg) @@ -4450,7 +4618,7 @@ class Item_direct_ref_to_ident :public Item_direct_ref public: Item_direct_ref_to_ident(THD *thd, Item_ident *item): Item_direct_ref(thd, item->context, (Item**)&item, item->table_name, - item->field_name, FALSE) + &item->field_name, FALSE) { ident= item; ref= (Item**)&ident; @@ -4523,7 +4691,7 @@ public: bool val_bool(); bool is_null(); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - bool send(Protocol *protocol, String *buffer); + bool send(Protocol *protocol, st_value *buffer); void save_org_in_field(Field *field, fast_field_copier data __attribute__ ((__unused__))) { @@ -4551,8 +4719,7 @@ public: orig_item->fix_after_pullout(new_parent, &orig_item); } int save_in_field(Field *to, bool no_conversions); - enum Item_result result_type () const { return orig_item->result_type(); } - enum_field_types field_type() const { return orig_item->field_type(); } + const Type_handler *type_handler() const { return orig_item->type_handler(); } table_map used_tables() const { return orig_item->used_tables(); } void update_used_tables() { @@ -4571,7 +4738,7 @@ public: { return orig_item->field_for_view_update(); } /* Row emulation: forwarding of ROW-related calls to orig_item */ - uint cols() + uint cols() const { return result_type() == ROW_RESULT ? orig_item->cols() : 1; } Item* element_index(uint i) { return result_type() == ROW_RESULT ? orig_item->element_index(i) : this; } @@ -4637,7 +4804,7 @@ public: Item_direct_view_ref(THD *thd, Name_resolution_context *context_arg, Item **item, const char *table_name_arg, - const char *field_name_arg, + LEX_CSTRING *field_name_arg, TABLE_LIST *view_arg): Item_direct_ref(thd, context_arg, item, table_name_arg, field_name_arg), item_equal(0), view(view_arg), @@ -4744,7 +4911,7 @@ public: } return Item_direct_ref::get_date(ltime, fuzzydate); } - bool send(Protocol *protocol, String *buffer); + bool send(Protocol *protocol, st_value *buffer); void save_org_in_field(Field *field, fast_field_copier data __attribute__ ((__unused__))) { @@ -4798,7 +4965,7 @@ public: Item_outer_ref(THD *thd, Name_resolution_context *context_arg, Item_field *outer_field_arg): Item_direct_ref(thd, context_arg, 0, outer_field_arg->table_name, - outer_field_arg->field_name), + &outer_field_arg->field_name), outer_ref(outer_field_arg), in_sum_func(0), found_in_select_list(0), found_in_group_by(0) { @@ -4807,7 +4974,7 @@ public: fixed= 0; /* reset flag set in set_properties() */ } Item_outer_ref(THD *thd, Name_resolution_context *context_arg, Item **item, - const char *table_name_arg, const char *field_name_arg, + const char *table_name_arg, LEX_CSTRING *field_name_arg, bool alias_name_used_arg): Item_direct_ref(thd, context_arg, item, table_name_arg, field_name_arg, alias_name_used_arg), @@ -4848,7 +5015,8 @@ protected: public: Item_ref_null_helper(THD *thd, Name_resolution_context *context_arg, Item_in_subselect* master, Item **item, - const char *table_name_arg, const char *field_name_arg): + const char *table_name_arg, + const LEX_CSTRING *field_name_arg): Item_ref(thd, context_arg, item, table_name_arg, field_name_arg), owner(master) {} void save_val(Field *to); @@ -4951,8 +5119,8 @@ protected: item= i; null_value=maybe_null=item->maybe_null; Type_std_attributes::set(item); - name=item->name; - set_handler_by_field_type(item->field_type()); + name= item->name; + set_handler(item->type_handler()); fixed= item->fixed; } @@ -4978,12 +5146,8 @@ public: /** All of the subclasses should have the same type tag */ enum Type type() const { return COPY_STR_ITEM; } - enum_field_types field_type() const - { return Type_handler_hybrid_field_type::field_type(); } - enum Item_result result_type () const - { return Type_handler_hybrid_field_type::result_type(); } - enum Item_result cmp_type () const - { return Type_handler_hybrid_field_type::cmp_type(); } + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } void make_field(THD *thd, Send_field *field) { item->make_field(thd, field); } table_map used_tables() const { return (table_map) 1L; } @@ -5238,15 +5402,15 @@ public: Item *arg; Item_default_value(THD *thd, Name_resolution_context *context_arg) :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL, - (const char *)NULL), + &null_clex_str), arg(NULL) {} Item_default_value(THD *thd, Name_resolution_context *context_arg, Item *a) :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL, - (const char *)NULL), + &null_clex_str), arg(a) {} Item_default_value(THD *thd, Name_resolution_context *context_arg, Field *a) :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL, - (const char *)NULL), + &null_clex_str), arg(NULL) {} enum Type type() const { return DEFAULT_VALUE_ITEM; } bool eq(const Item *item, bool binary_cmp) const; @@ -5257,7 +5421,7 @@ public: longlong val_int(); my_decimal *val_decimal(my_decimal *decimal_value); bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate); - bool send(Protocol *protocol, String *buffer); + bool send(Protocol *protocol, st_value *buffer); int save_in_field(Field *field_arg, bool no_conversions); bool save_in_param(THD *thd, Item_param *param) { @@ -5309,7 +5473,7 @@ public: longlong val_int(); my_decimal *val_decimal(my_decimal *decimal_value); bool get_date(MYSQL_TIME *ltime,ulonglong fuzzydate); - bool send(Protocol *protocol, String *buffer); + bool send(Protocol *protocol, st_value *buffer); }; @@ -5329,7 +5493,7 @@ public: Item *arg; Item_insert_value(THD *thd, Name_resolution_context *context_arg, Item *a) :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL, - (const char *)NULL), + &null_clex_str), arg(a) {} bool eq(const Item *item, bool binary_cmp) const; bool fix_fields(THD *, Item **); @@ -5390,7 +5554,7 @@ public: Item_trigger_field(THD *thd, Name_resolution_context *context_arg, row_version_type row_ver_arg, - const char *field_name_arg, + const LEX_CSTRING *field_name_arg, ulong priv, const bool ro) :Item_field(thd, context_arg, (const char *)NULL, (const char *)NULL, field_name_arg), @@ -5476,7 +5640,7 @@ protected: public: Item_cache(THD *thd): Item_basic_constant(thd), - Type_handler_hybrid_field_type(MYSQL_TYPE_STRING), + Type_handler_hybrid_field_type(&type_handler_string), example(0), cached_field(0), value_cached(0) { @@ -5485,9 +5649,9 @@ public: null_value= 1; } protected: - Item_cache(THD *thd, enum_field_types field_type_arg): + Item_cache(THD *thd, const Type_handler *handler): Item_basic_constant(thd), - Type_handler_hybrid_field_type(field_type_arg), + Type_handler_hybrid_field_type(handler), example(0), cached_field(0), value_cached(0) { @@ -5508,15 +5672,9 @@ public: }; enum Type type() const { return CACHE_ITEM; } - enum_field_types field_type() const - { return Type_handler_hybrid_field_type::field_type(); } - enum Item_result result_type () const - { return Type_handler_hybrid_field_type::result_type(); } - enum Item_result cmp_type () const - { return Type_handler_hybrid_field_type::cmp_type(); } - - static Item_cache* get_cache(THD *thd, const Item *item); - static Item_cache* get_cache(THD *thd, const Item* item, const Item_result type); + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } + virtual void keep_array() {} virtual void print(String *str, enum_query_type query_type); bool eq_def(const Field *field) @@ -5607,16 +5765,15 @@ class Item_cache_int: public Item_cache protected: longlong value; public: - Item_cache_int(THD *thd): Item_cache(thd, MYSQL_TYPE_LONGLONG), + Item_cache_int(THD *thd): Item_cache(thd, &type_handler_longlong), value(0) {} - Item_cache_int(THD *thd, enum_field_types field_type_arg): - Item_cache(thd, field_type_arg), value(0) {} + Item_cache_int(THD *thd, const Type_handler *handler): + Item_cache(thd, handler), value(0) {} double val_real(); longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); - enum Item_result result_type() const { return INT_RESULT; } bool cache_value(); int save_in_field(Field *field, bool no_conversions); Item *convert_to_basic_const_item(THD *thd); @@ -5628,7 +5785,7 @@ public: class Item_cache_temporal: public Item_cache_int { public: - Item_cache_temporal(THD *thd, enum_field_types field_type_arg); + Item_cache_temporal(THD *thd, const Type_handler *handler); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); longlong val_int(); @@ -5638,7 +5795,6 @@ public: bool cache_value(); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); int save_in_field(Field *field, bool no_conversions); - Item_result cmp_type() const { return TIME_RESULT; } void store_packed(longlong val_arg, Item *example); /* Having a clone_item method tells optimizer that this object @@ -5656,14 +5812,13 @@ class Item_cache_real: public Item_cache { double value; public: - Item_cache_real(THD *thd): Item_cache(thd, MYSQL_TYPE_DOUBLE), + Item_cache_real(THD *thd): Item_cache(thd, &type_handler_double), value(0) {} double val_real(); longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); - enum Item_result result_type() const { return REAL_RESULT; } bool cache_value(); Item *convert_to_basic_const_item(THD *thd); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -5676,13 +5831,12 @@ class Item_cache_decimal: public Item_cache protected: my_decimal decimal_value; public: - Item_cache_decimal(THD *thd): Item_cache(thd, MYSQL_TYPE_NEWDECIMAL) {} + Item_cache_decimal(THD *thd): Item_cache(thd, &type_handler_newdecimal) {} double val_real(); longlong val_int(); String* val_str(String *str); my_decimal *val_decimal(my_decimal *); - enum Item_result result_type() const { return DECIMAL_RESULT; } bool cache_value(); Item *convert_to_basic_const_item(THD *thd); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -5698,7 +5852,7 @@ class Item_cache_str: public Item_cache public: Item_cache_str(THD *thd, const Item *item): - Item_cache(thd, item->field_type()), value(0), + Item_cache(thd, item->type_handler()), value(0), is_varbinary(item->type() == FIELD_ITEM && Item_cache_str::field_type() == MYSQL_TYPE_VARCHAR && !((const Item_field *) item)->field->has_charset()) @@ -5709,7 +5863,6 @@ public: longlong val_int(); String* val_str(String *); my_decimal *val_decimal(my_decimal *); - enum Item_result result_type() const { return STRING_RESULT; } CHARSET_INFO *charset() const { return value->charset(); }; int save_in_field(Field *field, bool no_conversions); bool cache_value(); @@ -5791,9 +5944,7 @@ public: return 0; }; - enum Item_result result_type() const { return ROW_RESULT; } - - uint cols() { return item_count; } + uint cols() const { return item_count; } Item *element_index(uint i) { return values[i]; } Item **addr(uint i) { return (Item **) (values + i); } bool check_cols(uint c); @@ -5825,50 +5976,65 @@ public: single SP/PS execution. */ class Item_type_holder: public Item, - public Type_handler_hybrid_real_field_type + public Type_handler_hybrid_field_type, + public Type_geometry_attributes { protected: TYPELIB *enum_set_typelib; - Field::geometry_type geometry_type; - - void get_full_info(Item *item); - - /* It is used to count decimal precision in join_types */ - int prev_decimal_int_part; public: - Item_type_holder(THD*, Item*); + Item_type_holder(THD *thd, Item *item) + :Item(thd, item), + Type_handler_hybrid_field_type(item->real_type_handler()), + enum_set_typelib(0) + { + DBUG_ASSERT(item->fixed); + maybe_null= item->maybe_null; + } + Item_type_holder(THD *thd, + const LEX_CSTRING *name_arg, + const Type_handler *handler, + const Type_all_attributes *attr, + bool maybe_null_arg) + :Item(thd), + Type_handler_hybrid_field_type(handler), + Type_geometry_attributes(handler, attr), + enum_set_typelib(attr->get_typelib()) + { + name= *name_arg; + Type_std_attributes::set(*attr); + maybe_null= maybe_null_arg; + } - enum_field_types field_type() const - { return Type_handler_hybrid_real_field_type::field_type(); } - enum_field_types real_field_type() const - { return Type_handler_hybrid_real_field_type::real_field_type(); } - enum Item_result result_type () const + const Type_handler *type_handler() const { - /* - In 10.1 Item_type_holder::result_type() returned - Field::result_merge_type(field_type()), which returned STRING_RESULT - for the BIT data type. In 10.2 it returns INT_RESULT, similar - to what Field_bit::result_type() does. This should not be - important because Item_type_holder is a limited purpose Item - and its result_type() should not be called from outside of - Item_type_holder. It's called only internally from decimal_int_part() - from join_types(), to calculate "decimals" of the result data type. - As soon as we get BIT as one of the joined types, the result field - type cannot be numeric: it's either BIT, or VARBINARY. - */ - return Type_handler_hybrid_real_field_type::result_type(); + const Type_handler *handler= Type_handler_hybrid_field_type::type_handler(); + return handler->type_handler_for_item_field(); + } + const Type_handler *real_type_handler() const + { + return Type_handler_hybrid_field_type::type_handler(); } enum Type type() const { return TYPE_HOLDER; } + TYPELIB *get_typelib() const { return enum_set_typelib; } double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *); String *val_str(String*); - bool join_types(THD *thd, Item *); - Field *make_field_by_type(TABLE *table); - static uint32 display_length(Item *item); - static enum_field_types get_real_type(Item *); - Field::geometry_type get_geometry_type() const { return geometry_type; }; + Field *create_tmp_field(bool group, TABLE *table) + { + return Item_type_holder::real_type_handler()-> + make_and_init_table_field(&name, Record_addr(maybe_null), + *this, table); + } + Field::geometry_type get_geometry_type() const + { + return Type_geometry_attributes::get_geometry_type(); + } + void set_geometry_type(uint type) + { + Type_geometry_attributes::set_geometry_type(type); + } Item* get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; } }; diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index 1702e450c2a..23b90235cdf 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -98,34 +98,54 @@ static int cmp_row_type(Item* item1, Item* item2) /** Aggregates result types from the array of items. - SYNOPSIS: - agg_cmp_type() - type [out] the aggregated type - items array of items to aggregate the type from - nitems number of items in the array - - DESCRIPTION - This function aggregates result types from the array of items. Found type - supposed to be used later for comparison of values of these items. - Aggregation itself is performed by the item_cmp_type() function. - @param[out] type the aggregated type - @param items array of items to aggregate the type from - @param nitems number of items in the array - - @retval - 1 type incompatibility has been detected - @retval - 0 otherwise + This method aggregates comparison handler from the array of items. + The result handler is used later for comparison of values of these items. + + aggregate_for_comparison() + funcname the function or operator name, + for error reporting + items array of items to aggregate the type from + nitems number of items in the array + int_uint_as_dec what to do when comparing INT to UINT: + set the comparison handler to decimal or int. + + @retval true type incompatibility has been detected + @retval false otherwise */ -static int agg_cmp_type(Item_result *type, Item **items, uint nitems) +bool +Type_handler_hybrid_field_type::aggregate_for_comparison(const char *funcname, + Item **items, + uint nitems, + bool int_uint_as_dec) { uint unsigned_count= items[0]->unsigned_flag; - type[0]= items[0]->cmp_type(); + /* + Convert sub-type to super-type (e.g. DATE to DATETIME, INT to BIGINT, etc). + Otherwise Predicant_to_list_comparator will treat sub-types of the same + super-type as different data types and won't be able to use bisection in + many cases. + */ + set_handler(items[0]->type_handler()->type_handler_for_comparison()); for (uint i= 1 ; i < nitems ; i++) { unsigned_count+= items[i]->unsigned_flag; - type[0]= item_cmp_type(type[0], items[i]); + if (aggregate_for_comparison(items[i]->type_handler()-> + type_handler_for_comparison())) + { + /* + For more precise error messages if aggregation failed on the first pair + {items[0],items[1]}, use the name of items[0]->data_handler(). + Otherwise use the name of this->type_handler(), which is already a + result of aggregation for items[0]..items[i-1]. + */ + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + i == 1 ? items[0]->type_handler()->name().ptr() : + type_handler()->name().ptr(), + items[i]->type_handler()->name().ptr(), + funcname); + return true; + } /* When aggregating types of two row expressions we have to check that they have the same cardinality and that each component @@ -133,100 +153,21 @@ static int agg_cmp_type(Item_result *type, Item **items, uint nitems) the signature of the corresponding component of the second row expression. */ - if (type[0] == ROW_RESULT && cmp_row_type(items[0], items[i])) - return 1; // error found: invalid usage of rows + if (cmp_type() == ROW_RESULT && cmp_row_type(items[0], items[i])) + return true; // error found: invalid usage of rows } /** If all arguments are of INT type but have different unsigned_flag values, switch to DECIMAL_RESULT. */ - if (type[0] == INT_RESULT && unsigned_count != nitems && unsigned_count != 0) - type[0]= DECIMAL_RESULT; + if (int_uint_as_dec && + cmp_type() == INT_RESULT && + unsigned_count != nitems && unsigned_count != 0) + set_handler(&type_handler_newdecimal); return 0; } -/** - @brief Aggregates field types from the array of items. - - @param[in] items array of items to aggregate the type from - @paran[in] nitems number of items in the array - @param[in] treat_bit_as_number - if BIT should be aggregated to a non-BIT - counterpart as a LONGLONG number or as a VARBINARY string. - - Currently behaviour depends on the function: - - LEAST/GREATEST treat BIT as VARBINARY when - aggregating with a non-BIT counterpart. - Note, UNION also works this way. - - - CASE, COALESCE, IF, IFNULL treat BIT as LONGLONG when - aggregating with a non-BIT counterpart; - - This inconsistency may be changed in the future. See MDEV-8867. - - Note, independently from "treat_bit_as_number": - - a single BIT argument gives BIT as a result - - two BIT couterparts give BIT as a result - - @details This function aggregates field types from the array of items. - Found type is supposed to be used later as the result field type - of a multi-argument function. - Aggregation itself is performed by the Field::field_type_merge() - function. - - @note The term "aggregation" is used here in the sense of inferring the - result type of a function from its argument types. - - @return aggregated field type. -*/ - -enum_field_types agg_field_type(Item **items, uint nitems, - bool treat_bit_as_number) -{ - uint i; - if (!nitems || items[0]->result_type() == ROW_RESULT) - { - DBUG_ASSERT(0); - return MYSQL_TYPE_NULL; - } - enum_field_types res= items[0]->field_type(); - uint unsigned_count= items[0]->unsigned_flag; - for (i= 1 ; i < nitems ; i++) - { - enum_field_types cur= items[i]->field_type(); - if (treat_bit_as_number && - ((res == MYSQL_TYPE_BIT) ^ (cur == MYSQL_TYPE_BIT))) - { - if (res == MYSQL_TYPE_BIT) - res= MYSQL_TYPE_LONGLONG; // BIT + non-BIT - else - cur= MYSQL_TYPE_LONGLONG; // non-BIT + BIT - } - res= Field::field_type_merge(res, cur); - unsigned_count+= items[i]->unsigned_flag; - } - switch (res) { - case MYSQL_TYPE_TINY: - case MYSQL_TYPE_SHORT: - case MYSQL_TYPE_LONG: - case MYSQL_TYPE_LONGLONG: - case MYSQL_TYPE_INT24: - case MYSQL_TYPE_YEAR: - case MYSQL_TYPE_BIT: - if (unsigned_count != 0 && unsigned_count != nitems) - { - /* - If all arguments are of INT-alike type but have different - unsigned_flag, then convert to DECIMAL. - */ - return MYSQL_TYPE_NEWDECIMAL; - } - default: - break; - } - return res; -} - /* Collects different types for comparison of first item with each other items @@ -522,78 +463,6 @@ void Item_bool_rowready_func2::fix_length_and_dec() } -int Arg_comparator::set_compare_func(Item_func_or_sum *item, Item_result type) -{ - owner= item; - func= comparator_matrix[type] - [is_owner_equal_func()]; - - switch (type) { - case TIME_RESULT: - m_compare_collation= &my_charset_numeric; - break; - case ROW_RESULT: - { - uint n= (*a)->cols(); - if (n != (*b)->cols()) - { - my_error(ER_OPERAND_COLUMNS, MYF(0), n); - comparators= 0; - return 1; - } - if (!(comparators= new Arg_comparator[n])) - return 1; - for (uint i=0; i < n; i++) - { - if ((*a)->element_index(i)->cols() != (*b)->element_index(i)->cols()) - { - my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols()); - return 1; - } - if (comparators[i].set_cmp_func(owner, (*a)->addr(i), - (*b)->addr(i), set_null)) - return 1; - } - break; - } - case INT_RESULT: - { - if (func == &Arg_comparator::compare_int_signed) - { - if ((*a)->unsigned_flag) - func= (((*b)->unsigned_flag)? - &Arg_comparator::compare_int_unsigned : - &Arg_comparator::compare_int_unsigned_signed); - else if ((*b)->unsigned_flag) - func= &Arg_comparator::compare_int_signed_unsigned; - } - else if (func== &Arg_comparator::compare_e_int) - { - if ((*a)->unsigned_flag ^ (*b)->unsigned_flag) - func= &Arg_comparator::compare_e_int_diff_signedness; - } - break; - } - case STRING_RESULT: - case DECIMAL_RESULT: - break; - case REAL_RESULT: - { - if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC) - { - precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1]; - if (func == &Arg_comparator::compare_real) - func= &Arg_comparator::compare_real_fixed; - else if (func == &Arg_comparator::compare_e_real) - func= &Arg_comparator::compare_e_real_fixed; - } - break; - } - } - return 0; -} - - /** Prepare the comparator (set the comparison function) for comparing items *a1 and *a2 in the context of 'type'. @@ -610,14 +479,62 @@ int Arg_comparator::set_compare_func(Item_func_or_sum *item, Item_result type) int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, Item **a1, Item **a2) { - THD *thd= current_thd; owner= owner_arg; set_null= set_null && owner_arg; a= a1; b= a2; - m_compare_type= item_cmp_type(*a1, *a2); + Item *tmp_args[2]= {*a1, *a2}; + Type_handler_hybrid_field_type tmp; + if (tmp.aggregate_for_comparison(owner_arg->func_name(), tmp_args, 2, false)) + { + DBUG_ASSERT(current_thd->is_error()); + return 1; + } + m_compare_handler= tmp.type_handler(); + return m_compare_handler->set_comparator_func(this); +} + - if (m_compare_type == STRING_RESULT && +bool Arg_comparator::set_cmp_func_for_row_arguments() +{ + uint n= (*a)->cols(); + if (n != (*b)->cols()) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), n); + comparators= 0; + return true; + } + if (!(comparators= new Arg_comparator[n])) + return true; + for (uint i=0; i < n; i++) + { + if ((*a)->element_index(i)->cols() != (*b)->element_index(i)->cols()) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), (*a)->element_index(i)->cols()); + return true; + } + if (comparators[i].set_cmp_func(owner, (*a)->addr(i), + (*b)->addr(i), set_null)) + return true; + } + return false; +} + + +bool Arg_comparator::set_cmp_func_row() +{ + func= is_owner_equal_func() ? &Arg_comparator::compare_e_row : + &Arg_comparator::compare_row; + return set_cmp_func_for_row_arguments(); +} + + +bool Arg_comparator::set_cmp_func_string() +{ + THD *thd= current_thd; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_string : + &Arg_comparator::compare_string; + if (compare_type() == STRING_RESULT && (*a)->result_type() == STRING_RESULT && (*b)->result_type() == STRING_RESULT) { @@ -626,7 +543,7 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, generated item, like in natural join */ if (owner->agg_arg_charsets_for_comparison(&m_compare_collation, a, b)) - return 1; + return true; if ((*a)->type() == Item::FUNC_ITEM && ((Item_func *) (*a))->functype() == Item_func::JSON_EXTRACT_FUNC) @@ -644,27 +561,67 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, } } - if (m_compare_type == TIME_RESULT) + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; +} + + +bool Arg_comparator::set_cmp_func_time() +{ + m_compare_collation= &my_charset_numeric; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_time : + &Arg_comparator::compare_time; + return false; +} + + +bool Arg_comparator::set_cmp_func_datetime() +{ + m_compare_collation= &my_charset_numeric; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime : + &Arg_comparator::compare_datetime; + return false; +} + + +bool Arg_comparator::set_cmp_func_int() +{ + THD *thd= current_thd; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_int : + &Arg_comparator::compare_int_signed; + if ((*a)->field_type() == MYSQL_TYPE_YEAR && + (*b)->field_type() == MYSQL_TYPE_YEAR) { - enum_field_types f_type= a[0]->field_type_for_temporal_comparison(b[0]); - if (f_type == MYSQL_TYPE_TIME) - { - func= is_owner_equal_func() ? &Arg_comparator::compare_e_time : - &Arg_comparator::compare_time; - } - else - { - func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime : - &Arg_comparator::compare_datetime; - } - return 0; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime : + &Arg_comparator::compare_datetime; } + else if (func == &Arg_comparator::compare_int_signed) + { + if ((*a)->unsigned_flag) + func= (((*b)->unsigned_flag)? + &Arg_comparator::compare_int_unsigned : + &Arg_comparator::compare_int_unsigned_signed); + else if ((*b)->unsigned_flag) + func= &Arg_comparator::compare_int_signed_unsigned; + } + else if (func== &Arg_comparator::compare_e_int) + { + if ((*a)->unsigned_flag ^ (*b)->unsigned_flag) + func= &Arg_comparator::compare_e_int_diff_signedness; + } + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; +} - if (m_compare_type == REAL_RESULT && - (((*a)->result_type() == DECIMAL_RESULT && !(*a)->const_item() && + +bool Arg_comparator::set_cmp_func_real() +{ + if ((((*a)->result_type() == DECIMAL_RESULT && !(*a)->const_item() && (*b)->result_type() == STRING_RESULT && (*b)->const_item()) || - ((*b)->result_type() == DECIMAL_RESULT && !(*b)->const_item() && - (*a)->result_type() == STRING_RESULT && (*a)->const_item()))) + ((*b)->result_type() == DECIMAL_RESULT && !(*b)->const_item() && + (*a)->result_type() == STRING_RESULT && (*a)->const_item()))) { /* <non-const decimal expression> <cmp> <const string expression> @@ -673,21 +630,34 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, Do comparison as decimal rather than float, in order not to lose precision. */ - m_compare_type= DECIMAL_RESULT; + m_compare_handler= &type_handler_newdecimal; + return set_cmp_func_decimal(); } - if (m_compare_type == INT_RESULT && - (*a)->field_type() == MYSQL_TYPE_YEAR && - (*b)->field_type() == MYSQL_TYPE_YEAR) - { - m_compare_type= TIME_RESULT; - func= is_owner_equal_func() ? &Arg_comparator::compare_e_datetime : - &Arg_comparator::compare_datetime; - } + THD *thd= current_thd; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_real : + &Arg_comparator::compare_real; + if ((*a)->decimals < NOT_FIXED_DEC && (*b)->decimals < NOT_FIXED_DEC) + { + precision= 5 / log_10[MY_MAX((*a)->decimals, (*b)->decimals) + 1]; + if (func == &Arg_comparator::compare_real) + func= &Arg_comparator::compare_real_fixed; + else if (func == &Arg_comparator::compare_e_real) + func= &Arg_comparator::compare_e_real_fixed; + } + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; +} - a= cache_converted_constant(thd, a, &a_cache, m_compare_type); - b= cache_converted_constant(thd, b, &b_cache, m_compare_type); - return set_compare_func(owner_arg, m_compare_type); +bool Arg_comparator::set_cmp_func_decimal() +{ + THD *thd= current_thd; + func= is_owner_equal_func() ? &Arg_comparator::compare_e_decimal : + &Arg_comparator::compare_decimal; + a= cache_converted_constant(thd, a, &a_cache, compare_type_handler()); + b= cache_converted_constant(thd, b, &b_cache, compare_type_handler()); + return false; } @@ -710,18 +680,22 @@ int Arg_comparator::set_cmp_func(Item_func_or_sum *owner_arg, Item** Arg_comparator::cache_converted_constant(THD *thd_arg, Item **value, Item **cache_item, - Item_result type) + const Type_handler *handler) { /* + get_datetime_value creates Item_cache internally when comparing + values for the first row. + Arg_comparator::cache_converted_constant() is never called for TIME_RESULT. + */ + DBUG_ASSERT(handler->cmp_type() != TIME_RESULT); + /* Don't need cache if doing context analysis only. - Also, get_datetime_value creates Item_cache internally. - Unless fixed, we should not do it here. */ if (!thd_arg->lex->is_ps_or_view_context_analysis() && - (*value)->const_item() && type != (*value)->result_type() && - type != TIME_RESULT) + (*value)->const_item() && + handler->cmp_type() != (*value)->cmp_type()) { - Item_cache *cache= Item_cache::get_cache(thd_arg, *value, type); + Item_cache *cache= handler->Item_get_cache(thd_arg, *value); cache->setup(thd_arg, *value); *cache_item= cache; return cache_item; @@ -775,8 +749,8 @@ get_datetime_value(THD *thd, Item ***item_arg, Item **cache_arg, { if (!thd) thd= current_thd; - - Item_cache_temporal *cache= new (thd->mem_root) Item_cache_temporal(thd, f_type); + const Type_handler *h= Type_handler::get_handler_by_field_type(f_type); + Item_cache_temporal *cache= new (thd->mem_root) Item_cache_temporal(thd, h); cache->store_packed(value, item); *cache_arg= cache; *item_arg= cache_arg; @@ -1306,7 +1280,7 @@ bool Item_in_optimizer::fix_left(THD *thd) args[0]= ((Item_in_subselect *)args[1])->left_expr; } if ((!(*ref0)->fixed && (*ref0)->fix_fields(thd, ref0)) || - (!cache && !(cache= Item_cache::get_cache(thd, *ref0)))) + (!cache && !(cache= (*ref0)->get_cache(thd)))) DBUG_RETURN(1); /* During fix_field() expression could be substituted. @@ -2096,9 +2070,7 @@ void Item_func_between::fix_after_pullout(st_select_lex *new_parent, Item **ref) void Item_func_between::fix_length_and_dec() { - THD *thd= current_thd; max_length= 1; - compare_as_dates= 0; /* As some compare functions are generated after sql_yacc, @@ -2106,24 +2078,19 @@ void Item_func_between::fix_length_and_dec() */ if (!args[0] || !args[1] || !args[2]) return; - if (agg_cmp_type(&m_compare_type, args, 3)) + if (m_comparator.aggregate_for_comparison(Item_func_between::func_name(), + args, 3, true)) + { + DBUG_ASSERT(current_thd->is_error()); return; + } - if (m_compare_type == STRING_RESULT && - agg_arg_charsets_for_comparison(cmp_collation, args, 3)) - return; + m_comparator.type_handler()->Item_func_between_fix_length_and_dec(this); +} - /* - When comparing as date/time, we need to convert non-temporal values - (e.g. strings) to MYSQL_TIME. get_datetime_value() does it - automatically when one of the operands is a date/time. But here we - may need to compare two strings as dates (str1 BETWEEN str2 AND date). - For this to work, we need to know what date/time type we compare - strings as. - */ - if (m_compare_type == TIME_RESULT) - compare_as_dates= find_date_time_item(args, 3, 0); +bool Item_func_between::fix_length_and_dec_numeric(THD *thd) +{ /* See the comment about the similar block in Item_bool_func2 */ if (args[0]->real_item()->type() == FIELD_ITEM && !thd->lex->is_ps_or_view_context_analysis()) @@ -2135,145 +2102,144 @@ void Item_func_between::fix_length_and_dec() const bool cvt_arg1= convert_const_to_int(thd, field_item, &args[1]); const bool cvt_arg2= convert_const_to_int(thd, field_item, &args[2]); if (cvt_arg1 && cvt_arg2) - m_compare_type= INT_RESULT; // Works for all types. + { + // Works for all types + m_comparator.set_handler(&type_handler_longlong); + } } } + return false; } -longlong Item_func_between::val_int() +longlong Item_func_between::val_int_cmp_temporal() { - DBUG_ASSERT(fixed == 1); - - switch (m_compare_type) { - case TIME_RESULT: - { - THD *thd= current_thd; - longlong value, a, b; - Item *cache, **ptr; - bool value_is_null, a_is_null, b_is_null; + THD *thd= current_thd; + longlong value, a, b; + Item *cache, **ptr; + bool value_is_null, a_is_null, b_is_null; - ptr= &args[0]; - enum_field_types f_type= field_type_for_temporal_comparison(compare_as_dates); - value= get_datetime_value(thd, &ptr, &cache, f_type, &value_is_null); - if (ptr != &args[0]) - thd->change_item_tree(&args[0], *ptr); + ptr= &args[0]; + enum_field_types f_type= m_comparator.type_handler()->field_type(); + value= get_datetime_value(thd, &ptr, &cache, f_type, &value_is_null); + if (ptr != &args[0]) + thd->change_item_tree(&args[0], *ptr); - if ((null_value= value_is_null)) - return 0; + if ((null_value= value_is_null)) + return 0; - ptr= &args[1]; - a= get_datetime_value(thd, &ptr, &cache, f_type, &a_is_null); - if (ptr != &args[1]) - thd->change_item_tree(&args[1], *ptr); + ptr= &args[1]; + a= get_datetime_value(thd, &ptr, &cache, f_type, &a_is_null); + if (ptr != &args[1]) + thd->change_item_tree(&args[1], *ptr); + + ptr= &args[2]; + b= get_datetime_value(thd, &ptr, &cache, f_type, &b_is_null); + if (ptr != &args[2]) + thd->change_item_tree(&args[2], *ptr); + + if (!a_is_null && !b_is_null) + return (longlong) ((value >= a && value <= b) != negated); + if (a_is_null && b_is_null) + null_value= true; + else if (a_is_null) + null_value= value <= b; // not null if false range. + else + null_value= value >= a; + return (longlong) (!null_value && negated); +} - ptr= &args[2]; - b= get_datetime_value(thd, &ptr, &cache, f_type, &b_is_null); - if (ptr != &args[2]) - thd->change_item_tree(&args[2], *ptr); - if (!a_is_null && !b_is_null) - return (longlong) ((value >= a && value <= b) != negated); - if (a_is_null && b_is_null) - null_value=1; - else if (a_is_null) - null_value= value <= b; // not null if false range. - else - null_value= value >= a; - break; +longlong Item_func_between::val_int_cmp_string() +{ + String *value,*a,*b; + value=args[0]->val_str(&value0); + if ((null_value=args[0]->null_value)) + return 0; + a= args[1]->val_str(&value1); + b= args[2]->val_str(&value2); + if (!args[1]->null_value && !args[2]->null_value) + return (longlong) ((sortcmp(value,a,cmp_collation.collation) >= 0 && + sortcmp(value,b,cmp_collation.collation) <= 0) != + negated); + if (args[1]->null_value && args[2]->null_value) + null_value= true; + else if (args[1]->null_value) + { + // Set to not null if false range. + null_value= sortcmp(value,b,cmp_collation.collation) <= 0; } - - case STRING_RESULT: + else { - String *value,*a,*b; - value=args[0]->val_str(&value0); - if ((null_value=args[0]->null_value)) - return 0; - a=args[1]->val_str(&value1); - b=args[2]->val_str(&value2); - if (!args[1]->null_value && !args[2]->null_value) - return (longlong) ((sortcmp(value,a,cmp_collation.collation) >= 0 && - sortcmp(value,b,cmp_collation.collation) <= 0) != - negated); - if (args[1]->null_value && args[2]->null_value) - null_value=1; - else if (args[1]->null_value) - { - // Set to not null if false range. - null_value= sortcmp(value,b,cmp_collation.collation) <= 0; - } - else - { - // Set to not null if false range. - null_value= sortcmp(value,a,cmp_collation.collation) >= 0; - } - break; + // Set to not null if false range. + null_value= sortcmp(value,a,cmp_collation.collation) >= 0; } - case INT_RESULT: + return (longlong) (!null_value && negated); +} + + +longlong Item_func_between::val_int_cmp_int() +{ + longlong value= args[0]->val_int(), a, b; + if ((null_value= args[0]->null_value)) + return 0; /* purecov: inspected */ + a= args[1]->val_int(); + b= args[2]->val_int(); + if (!args[1]->null_value && !args[2]->null_value) + return (longlong) ((value >= a && value <= b) != negated); + if (args[1]->null_value && args[2]->null_value) + null_value= true; + else if (args[1]->null_value) { - longlong value=args[0]->val_int(), a, b; - if ((null_value=args[0]->null_value)) - return 0; /* purecov: inspected */ - a=args[1]->val_int(); - b=args[2]->val_int(); - if (!args[1]->null_value && !args[2]->null_value) - return (longlong) ((value >= a && value <= b) != negated); - if (args[1]->null_value && args[2]->null_value) - null_value=1; - else if (args[1]->null_value) - { - null_value= value <= b; // not null if false range. - } - else - { - null_value= value >= a; - } - break; + null_value= value <= b; // not null if false range. } - case DECIMAL_RESULT: - { - my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf), - a_buf, *a_dec, b_buf, *b_dec; - if ((null_value=args[0]->null_value)) - return 0; /* purecov: inspected */ - a_dec= args[1]->val_decimal(&a_buf); - b_dec= args[2]->val_decimal(&b_buf); - if (!args[1]->null_value && !args[2]->null_value) - return (longlong) ((my_decimal_cmp(dec, a_dec) >= 0 && - my_decimal_cmp(dec, b_dec) <= 0) != negated); - if (args[1]->null_value && args[2]->null_value) - null_value=1; - else if (args[1]->null_value) - null_value= (my_decimal_cmp(dec, b_dec) <= 0); - else - null_value= (my_decimal_cmp(dec, a_dec) >= 0); - break; + else + { + null_value= value >= a; } - case REAL_RESULT: + return (longlong) (!null_value && negated); +} + + +longlong Item_func_between::val_int_cmp_decimal() +{ + my_decimal dec_buf, *dec= args[0]->val_decimal(&dec_buf), + a_buf, *a_dec, b_buf, *b_dec; + if ((null_value=args[0]->null_value)) + return 0; /* purecov: inspected */ + a_dec= args[1]->val_decimal(&a_buf); + b_dec= args[2]->val_decimal(&b_buf); + if (!args[1]->null_value && !args[2]->null_value) + return (longlong) ((my_decimal_cmp(dec, a_dec) >= 0 && + my_decimal_cmp(dec, b_dec) <= 0) != negated); + if (args[1]->null_value && args[2]->null_value) + null_value= true; + else if (args[1]->null_value) + null_value= (my_decimal_cmp(dec, b_dec) <= 0); + else + null_value= (my_decimal_cmp(dec, a_dec) >= 0); + return (longlong) (!null_value && negated); +} + + +longlong Item_func_between::val_int_cmp_real() +{ + double value= args[0]->val_real(),a,b; + if ((null_value=args[0]->null_value)) + return 0; /* purecov: inspected */ + a= args[1]->val_real(); + b= args[2]->val_real(); + if (!args[1]->null_value && !args[2]->null_value) + return (longlong) ((value >= a && value <= b) != negated); + if (args[1]->null_value && args[2]->null_value) + null_value= true; + else if (args[1]->null_value) { - double value= args[0]->val_real(),a,b; - if ((null_value=args[0]->null_value)) - return 0; /* purecov: inspected */ - a= args[1]->val_real(); - b= args[2]->val_real(); - if (!args[1]->null_value && !args[2]->null_value) - return (longlong) ((value >= a && value <= b) != negated); - if (args[1]->null_value && args[2]->null_value) - null_value=1; - else if (args[1]->null_value) - { - null_value= value <= b; // not null if false range. - } - else - { - null_value= value >= a; - } - break; + null_value= value <= b; // not null if false range. } - case ROW_RESULT: - DBUG_ASSERT(0); - null_value= 1; - return 0; + else + { + null_value= value >= a; } return (longlong) (!null_value && negated); } @@ -2291,16 +2257,6 @@ void Item_func_between::print(String *str, enum_query_type query_type) } -uint Item_func_case_abbreviation2::decimal_precision2(Item **args) const -{ - int arg0_int_part= args[0]->decimal_int_part(); - int arg1_int_part= args[1]->decimal_int_part(); - int max_int_part= MY_MAX(arg0_int_part, arg1_int_part); - int precision= max_int_part + decimals; - return MY_MIN(precision, DECIMAL_MAX_PRECISION); -} - - double Item_func_ifnull::real_op() { @@ -2439,91 +2395,6 @@ void Item_func_if::fix_after_pullout(st_select_lex *new_parent, Item **ref) } -void Item_func_if::cache_type_info(Item *source) -{ - Type_std_attributes::set(source); - set_handler_by_field_type(source->field_type()); - maybe_null= source->maybe_null; -} - - -void -Item_func_if::fix_length_and_dec() -{ - // Let IF(cond, expr, NULL) and IF(cond, NULL, expr) inherit type from expr. - if (args[1]->type() == NULL_ITEM) - { - cache_type_info(args[2]); - maybe_null= true; - // If both arguments are NULL, make resulting type BINARY(0). - if (args[2]->type() == NULL_ITEM) - set_handler_by_field_type(MYSQL_TYPE_STRING); - return; - } - if (args[2]->type() == NULL_ITEM) - { - cache_type_info(args[1]); - maybe_null= true; - return; - } - Item_func_case_abbreviation2::fix_length_and_dec2(args + 1); -} - - -double -Item_func_if::real_op() -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - double value= arg->val_real(); - null_value=arg->null_value; - return value; -} - -longlong -Item_func_if::int_op() -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - longlong value=arg->val_int(); - null_value=arg->null_value; - return value; -} - -String * -Item_func_if::str_op(String *str) -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - String *res=arg->val_str(str); - if (res) - res->set_charset(collation.collation); - if ((null_value=arg->null_value)) - res= NULL; - return res; -} - - -my_decimal * -Item_func_if::decimal_op(my_decimal *decimal_value) -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - my_decimal *value= arg->val_decimal(decimal_value); - if ((null_value= arg->null_value)) - value= NULL; - return value; -} - - -bool Item_func_if::date_op(MYSQL_TIME *ltime, uint fuzzydate) -{ - DBUG_ASSERT(fixed == 1); - Item *arg= args[0]->val_bool() ? args[1] : args[2]; - return (null_value= arg->get_date_with_conversion(ltime, fuzzydate)); -} - - void Item_func_nullif::split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array, List<Item> &fields, uint flags) { @@ -2729,14 +2600,14 @@ Item_func_nullif::fix_length_and_dec() */ m_cache= args[0]->cmp_type() == STRING_RESULT ? new (thd->mem_root) Item_cache_str_for_nullif(thd, args[0]) : - Item_cache::get_cache(thd, args[0]); + args[0]->get_cache(thd); m_cache->setup(thd, args[0]); m_cache->store(args[0]); m_cache->set_used_tables(args[0]->used_tables()); thd->change_item_tree(&args[0], m_cache); thd->change_item_tree(&args[2], m_cache); } - set_handler_by_field_type(args[2]->field_type()); + set_handler(args[2]->type_handler()); collation.set(args[2]->collation); decimals= args[2]->decimals; unsigned_flag= args[2]->unsigned_flag; @@ -2943,26 +2814,6 @@ Item_func_nullif::is_null() } -Item_func_case::Item_func_case(THD *thd, List<Item> &list, - Item *first_expr_arg, Item *else_expr_arg): - Item_func_hybrid_field_type(thd), first_expr_num(-1), else_expr_num(-1), - left_cmp_type(INT_RESULT), case_item(0), m_found_types(0) -{ - ncases= list.elements; - if (first_expr_arg) - { - first_expr_num= list.elements; - list.push_back(first_expr_arg, thd->mem_root); - } - if (else_expr_arg) - { - else_expr_num= list.elements; - list.push_back(else_expr_arg, thd->mem_root); - } - set_arguments(thd, list); - bzero(&cmp_items, sizeof(cmp_items)); -} - /** Find and return matching items for CASE or ELSE item if all compares are failed or NULL if ELSE item isn't defined. @@ -2984,43 +2835,37 @@ Item_func_case::Item_func_case(THD *thd, List<Item> &list, failed */ -Item *Item_func_case::find_item(String *str) +Item *Item_func_case_searched::find_item() { - uint value_added_map= 0; - - if (first_expr_num == -1) + uint count= when_count(); + for (uint i= 0 ; i < count ; i++) { - for (uint i=0 ; i < ncases ; i+=2) - { - // No expression between CASE and the first WHEN - if (args[i]->val_bool()) - return args[i+1]; - continue; - } + if (args[2 * i]->val_bool()) + return args[2 * i + 1]; } - else - { - /* Compare every WHEN argument with it and return the first match */ - for (uint i=0 ; i < ncases ; i+=2) - { - if (args[i]->real_item()->type() == NULL_ITEM) - continue; - cmp_type= item_cmp_type(left_cmp_type, args[i]); - DBUG_ASSERT(cmp_type != ROW_RESULT); - DBUG_ASSERT(cmp_items[(uint)cmp_type]); - if (!(value_added_map & (1U << (uint)cmp_type))) - { - cmp_items[(uint)cmp_type]->store_value(args[first_expr_num]); - if ((null_value=args[first_expr_num]->null_value)) - return else_expr_num != -1 ? args[else_expr_num] : 0; - value_added_map|= 1U << (uint)cmp_type; - } - if (cmp_items[(uint)cmp_type]->cmp(args[i]) == FALSE) - return args[i + 1]; - } - } - // No, WHEN clauses all missed, return ELSE expression - return else_expr_num != -1 ? args[else_expr_num] : 0; + Item **pos= Item_func_case_searched::else_expr_addr(); + return pos ? pos[0] : 0; +} + + +Item *Item_func_case_simple::find_item() +{ + /* Compare every WHEN argument with it and return the first match */ + uint idx; + if (!Predicant_to_list_comparator::cmp(this, &idx, NULL)) + return args[idx + 1]; + Item **pos= Item_func_case_simple::else_expr_addr(); + return pos ? pos[0] : 0; +} + + +Item *Item_func_decode_oracle::find_item() +{ + uint idx; + if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx)) + return args[idx + 1]; + Item **pos= Item_func_decode_oracle::else_expr_addr(); + return pos ? pos[0] : 0; } @@ -3028,7 +2873,7 @@ String *Item_func_case::str_op(String *str) { DBUG_ASSERT(fixed == 1); String *res; - Item *item=find_item(str); + Item *item= find_item(); if (!item) { @@ -3045,9 +2890,7 @@ String *Item_func_case::str_op(String *str) longlong Item_func_case::int_op() { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff,sizeof(buff),default_charset()); - Item *item=find_item(&dummy_str); + Item *item= find_item(); longlong res; if (!item) @@ -3063,9 +2906,7 @@ longlong Item_func_case::int_op() double Item_func_case::real_op() { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff,sizeof(buff),default_charset()); - Item *item=find_item(&dummy_str); + Item *item= find_item(); double res; if (!item) @@ -3082,9 +2923,7 @@ double Item_func_case::real_op() my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff, sizeof(buff), default_charset()); - Item *item= find_item(&dummy_str); + Item *item= find_item(); my_decimal *res; if (!item) @@ -3102,9 +2941,7 @@ my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value) bool Item_func_case::date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff, sizeof(buff), default_charset()); - Item *item= find_item(&dummy_str); + Item *item= find_item(); if (!item) return (null_value= true); return (null_value= item->get_date_with_conversion(ltime, fuzzydate)); @@ -3119,10 +2956,15 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref) */ uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2]; - if (!(arg_buffer= (Item**) thd->alloc(sizeof(Item*)*(ncases+1)))) + if (!(arg_buffer= (Item**) thd->alloc(sizeof(Item*)*(arg_count)))) return TRUE; bool res= Item_func::fix_fields(thd, ref); + + Item **pos= else_expr_addr(); + if (!pos || pos[0]->maybe_null) + maybe_null= 1; + /* Call check_stack_overrun after fix_fields to be sure that stack variable is not optimized away @@ -3155,138 +2997,187 @@ static void change_item_tree_if_needed(THD *thd, } -void Item_func_case::fix_length_and_dec() +bool Item_func_case_simple::prepare_predicant_and_values(THD *thd, + uint *found_types, + bool nulls_equal) +{ + bool have_null= false; + uint type_cnt; + Type_handler_hybrid_field_type tmp; + uint ncases= when_count(); + add_predicant(this, 0); + for (uint i= 0 ; i < ncases; i++) + { + if (nulls_equal ? + add_value("case..when", this, i * 2 + 1) : + add_value_skip_null("case..when", this, i * 2 + 1, &have_null)) + return true; + } + all_values_added(&tmp, &type_cnt, &m_found_types); +#ifndef DBUG_OFF + Predicant_to_list_comparator::debug_print(thd); +#endif + return false; +} + + +void Item_func_case_searched::fix_length_and_dec() +{ + THD *thd= current_thd; + Item **else_ptr= Item_func_case_searched::else_expr_addr(); + aggregate_then_and_else_arguments(thd, &args[1], when_count(), else_ptr); +} + + +void Item_func_case_simple::fix_length_and_dec() +{ + THD *thd= current_thd; + Item **else_ptr= Item_func_case_simple::else_expr_addr(); + if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr)) + aggregate_switch_and_when_arguments(thd, false); +} + + +void Item_func_decode_oracle::fix_length_and_dec() +{ + THD *thd= current_thd; + Item **else_ptr= Item_func_decode_oracle::else_expr_addr(); + if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr)) + aggregate_switch_and_when_arguments(thd, true); +} + + +/* + Aggregate all THEN and ELSE expression types + and collations when string result + + @param THD - current thd + @param them_expr - the pointer to the leftmost THEN argument in args[] + @param count - the number or THEN..ELSE pairs + @param else_epxr - the pointer to the ELSE arguments in args[] + (or NULL is there is not ELSE) +*/ +bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, + Item **then_expr, + uint count, + Item **else_expr) { Item **agg= arg_buffer; uint nagg; - THD *thd= current_thd; - m_found_types= 0; - if (else_expr_num == -1 || args[else_expr_num]->maybe_null) - maybe_null= 1; + for (nagg= 0 ; nagg < count ; nagg++) + agg[nagg]= then_expr[nagg * 2]; + + if (else_expr) + agg[nagg++]= *else_expr; + + if (aggregate_for_result(func_name(), agg, nagg, true)) + return true; + + if (fix_attributes(agg, nagg)) + return true; /* - Aggregate all THEN and ELSE expression types - and collations when string result + Copy all modified THEN and ELSE items back to then_expr[] array. + Some of the items might have been changed to Item_func_conv_charset. */ - - for (nagg= 0 ; nagg < ncases/2 ; nagg++) - agg[nagg]= args[nagg*2+1]; - - if (else_expr_num != -1) - agg[nagg++]= args[else_expr_num]; - - set_handler_by_field_type(agg_field_type(agg, nagg, true)); + for (nagg= 0 ; nagg < count ; nagg++) + change_item_tree_if_needed(thd, &then_expr[nagg * 2], agg[nagg]); + + if (else_expr) + change_item_tree_if_needed(thd, else_expr, agg[nagg++]); + return false; +} + - if (Item_func_case::result_type() == STRING_RESULT) +/* + Aggregate the predicant expression and all WHEN expression types + and collations when string comparison +*/ +bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd, + bool nulls_eq) +{ + Item **agg= arg_buffer; + uint nagg; + uint ncases= when_count(); + m_found_types= 0; + if (prepare_predicant_and_values(thd, &m_found_types, nulls_eq)) { - if (count_string_result_length(Item_func_case::field_type(), agg, nagg)) - return; /* - Copy all THEN and ELSE items back to args[] array. - Some of the items might have been changed to Item_func_conv_charset. + If Predicant_to_list_comparator() fails to prepare components, + it must put an error into the diagnostics area. This is needed + to make fix_fields() catches such errors. */ - for (nagg= 0 ; nagg < ncases / 2 ; nagg++) - change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg]); - - if (else_expr_num != -1) - change_item_tree_if_needed(thd, &args[else_expr_num], agg[nagg++]); - } - else - { - fix_attributes(agg, nagg); + DBUG_ASSERT(thd->is_error()); + return true; } - + /* - Aggregate first expression and all WHEN expression types - and collations when string comparison + As the predicant expression and WHEN expressions + are intermixed in args[] array THEN and ELSE items, + extract the first expression and all WHEN expressions into + a temporary array, to process them easier. */ - if (first_expr_num != -1) - { - uint i; - agg[0]= args[first_expr_num]; - left_cmp_type= agg[0]->cmp_type(); + agg[0]= args[0]; // The predicant + for (nagg= 0; nagg < ncases ; nagg++) + agg[nagg+1]= args[nagg * 2 + 1]; + nagg++; + if (!(m_found_types= collect_cmp_types(agg, nagg))) + return true; + if (m_found_types & (1U << STRING_RESULT)) + { /* - As the first expression and WHEN expressions - are intermixed in args[] array THEN and ELSE items, - extract the first expression and all WHEN expressions into - a temporary array, to process them easier. - */ - for (nagg= 0; nagg < ncases/2 ; nagg++) - agg[nagg+1]= args[nagg*2]; - nagg++; - if (!(m_found_types= collect_cmp_types(agg, nagg))) - return; - - Item *date_arg= 0; - if (m_found_types & (1U << TIME_RESULT)) - date_arg= find_date_time_item(args, arg_count, 0); + If we'll do string comparison, we also need to aggregate + character set and collation for first/WHEN items and + install converters for some of them to cmp_collation when necessary. + This is done because cmp_item compatators cannot compare + strings in two different character sets. + Some examples when we install converters: - if (m_found_types & (1U << STRING_RESULT)) - { - /* - If we'll do string comparison, we also need to aggregate - character set and collation for first/WHEN items and - install converters for some of them to cmp_collation when necessary. - This is done because cmp_item compatators cannot compare - strings in two different character sets. - Some examples when we install converters: + 1. Converter installed for the first expression: - 1. Converter installed for the first expression: + CASE latin1_item WHEN utf16_item THEN ... END - CASE latin1_item WHEN utf16_item THEN ... END + is replaced to: - is replaced to: + CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END - CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END + 2. Converter installed for the left WHEN item: - 2. Converter installed for the left WHEN item: + CASE utf16_item WHEN latin1_item THEN ... END - CASE utf16_item WHEN latin1_item THEN ... END + is replaced to: - is replaced to: - - CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END - */ - if (agg_arg_charsets_for_comparison(cmp_collation, agg, nagg)) - return; - /* - Now copy first expression and all WHEN expressions back to args[] - arrray, because some of the items might have been changed to converters - (e.g. Item_func_conv_charset, or Item_string for constants). - */ - change_item_tree_if_needed(thd, &args[first_expr_num], agg[0]); + CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END + */ + if (agg_arg_charsets_for_comparison(cmp_collation, agg, nagg)) + return true; + /* + Now copy first expression and all WHEN expressions back to args[] + arrray, because some of the items might have been changed to converters + (e.g. Item_func_conv_charset, or Item_string for constants). + */ + change_item_tree_if_needed(thd, &args[0], agg[0]); - for (nagg= 0; nagg < ncases / 2; nagg++) - change_item_tree_if_needed(thd, &args[nagg * 2], agg[nagg + 1]); - } + for (nagg= 0; nagg < ncases; nagg++) + change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg + 1]); + } - for (i= 0; i <= (uint)TIME_RESULT; i++) - { - if (m_found_types & (1U << i) && !cmp_items[i]) - { - DBUG_ASSERT((Item_result)i != ROW_RESULT); + if (make_unique_cmp_items(thd, cmp_collation.collation)) + return true; - if (!(cmp_items[i]= - cmp_item::get_comparator((Item_result)i, date_arg, - cmp_collation.collation))) - return; - } - } - } + return false; } -Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) +Item* Item_func_case_simple::propagate_equal_fields(THD *thd, + const Context &ctx, + COND_EQUAL *cond) { - if (first_expr_num == -1) - { - // None of the arguments are in a comparison context - Item_args::propagate_equal_fields(thd, Context_identity(), cond); - return this; - } + const Type_handler *first_expr_cmp_handler; + first_expr_cmp_handler= args[0]->type_handler_for_comparison(); for (uint i= 0; i < arg_count; i++) { /* @@ -3296,7 +3187,7 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ CASE ... THEN y1 ... THEN y2 ... THEN y3 ... ELSE y4 END */ Item *new_item= 0; - if ((int) i == first_expr_num) // Then CASE (the switch) argument + if (i == 0) // Then CASE (the switch) argument { /* Cannot replace the CASE (the switch) argument if @@ -3325,15 +3216,15 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ WHEN 'str2' THEN TRUE ELSE FALSE END; */ - if (m_found_types == (1UL << left_cmp_type)) + if (m_found_types == (1UL << first_expr_cmp_handler->cmp_type())) new_item= args[i]->propagate_equal_fields(thd, Context( ANY_SUBST, - left_cmp_type, + first_expr_cmp_handler, cmp_collation.collation), cond); } - else if ((i % 2) == 0) // WHEN arguments + else if ((i % 2) == 1 && i != arg_count - 1) // WHEN arguments { /* These arguments are in comparison. @@ -3342,13 +3233,14 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ replaced to zero-filled constants (only IDENTITY_SUBST allows this). Such a change for WHEN arguments would require rebuilding cmp_items. */ - Item_result tmp_cmp_type= item_cmp_type(args[first_expr_num], args[i]); - new_item= args[i]->propagate_equal_fields(thd, - Context( - ANY_SUBST, - tmp_cmp_type, - cmp_collation.collation), - cond); + Type_handler_hybrid_field_type tmp(first_expr_cmp_handler); + if (!tmp.aggregate_for_comparison(args[i]->type_handler_for_comparison())) + new_item= args[i]->propagate_equal_fields(thd, + Context( + ANY_SUBST, + tmp.type_handler(), + cmp_collation.collation), + cond); } else // THEN and ELSE arguments (they are not in comparison) { @@ -3361,60 +3253,52 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ } -uint Item_func_case::decimal_precision() const +void Item_func_case::print_when_then_arguments(String *str, + enum_query_type query_type, + Item **items, uint count) { - int max_int_part=0; - for (uint i=0 ; i < ncases ; i+=2) - set_if_bigger(max_int_part, args[i+1]->decimal_int_part()); - - if (else_expr_num != -1) - set_if_bigger(max_int_part, args[else_expr_num]->decimal_int_part()); - return MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION); + for (uint i=0 ; i < count ; i++) + { + str->append(STRING_WITH_LEN("when ")); + items[i * 2]->print_parenthesised(str, query_type, precedence()); + str->append(STRING_WITH_LEN(" then ")); + items[i * 2 + 1]->print_parenthesised(str, query_type, precedence()); + str->append(' '); + } } -/** - @todo - Fix this so that it prints the whole CASE expression -*/ +void Item_func_case::print_else_argument(String *str, + enum_query_type query_type, + Item *item) +{ + str->append(STRING_WITH_LEN("else ")); + item->print_parenthesised(str, query_type, precedence()); + str->append(' '); +} + -void Item_func_case::print(String *str, enum_query_type query_type) +void Item_func_case_searched::print(String *str, enum_query_type query_type) { + Item **pos; str->append(STRING_WITH_LEN("case ")); - if (first_expr_num != -1) - { - args[first_expr_num]->print_parenthesised(str, query_type, precedence()); - str->append(' '); - } - for (uint i=0 ; i < ncases ; i+=2) - { - str->append(STRING_WITH_LEN("when ")); - args[i]->print_parenthesised(str, query_type, precedence()); - str->append(STRING_WITH_LEN(" then ")); - args[i+1]->print_parenthesised(str, query_type, precedence()); - str->append(' '); - } - if (else_expr_num != -1) - { - str->append(STRING_WITH_LEN("else ")); - args[else_expr_num]->print_parenthesised(str, query_type, precedence()); - str->append(' '); - } + print_when_then_arguments(str, query_type, &args[0], when_count()); + if ((pos= Item_func_case_searched::else_expr_addr())) + print_else_argument(str, query_type, pos[0]); str->append(STRING_WITH_LEN("end")); } -void Item_func_case::cleanup() +void Item_func_case_simple::print(String *str, enum_query_type query_type) { - uint i; - DBUG_ENTER("Item_func_case::cleanup"); - Item_func::cleanup(); - for (i= 0; i <= (uint)TIME_RESULT; i++) - { - delete cmp_items[i]; - cmp_items[i]= 0; - } - DBUG_VOID_RETURN; + Item **pos; + str->append(STRING_WITH_LEN("case ")); + args[0]->print_parenthesised(str, query_type, precedence()); + str->append(' '); + print_when_then_arguments(str, query_type, &args[1], when_count()); + if ((pos= Item_func_case_simple::else_expr_addr())) + print_else_argument(str, query_type, pos[0]); + str->append(STRING_WITH_LEN("end")); } @@ -3492,33 +3376,6 @@ my_decimal *Item_func_coalesce::decimal_op(my_decimal *decimal_value) } -void Item_hybrid_func::fix_attributes(Item **items, uint nitems) -{ - switch (Item_hybrid_func::result_type()) { - case STRING_RESULT: - if (count_string_result_length(Item_hybrid_func::field_type(), - items, nitems)) - return; - break; - case DECIMAL_RESULT: - collation.set_numeric(); - count_decimal_length(items, nitems); - break; - case REAL_RESULT: - collation.set_numeric(); - count_real_length(items, nitems); - break; - case INT_RESULT: - collation.set_numeric(); - count_only_length(items, nitems); - decimals= 0; - break; - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); - } -} - /**************************************************************************** Classes and function for the IN operator ****************************************************************************/ @@ -3785,16 +3642,22 @@ void in_datetime::set(uint pos,Item *item) { struct packed_longlong *buff= &((packed_longlong*) base)[pos]; - buff->val= item->val_temporal_packed(warn_item); + buff->val= item->val_datetime_packed(); buff->unsigned_flag= 1L; } -uchar *in_datetime::get_value(Item *item) +void in_time::set(uint pos,Item *item) +{ + struct packed_longlong *buff= &((packed_longlong*) base)[pos]; + + buff->val= item->val_time_packed(); + buff->unsigned_flag= 1L; +} + +uchar *in_temporal::get_value_internal(Item *item, enum_field_types f_type) { bool is_null; Item **tmp_item= lval_cache ? &lval_cache : &item; - enum_field_types f_type= - tmp_item[0]->field_type_for_temporal_comparison(warn_item); tmp.val= get_datetime_value(0, &tmp_item, &lval_cache, f_type, &is_null); if (item->null_value) return 0; @@ -3802,7 +3665,7 @@ uchar *in_datetime::get_value(Item *item) return (uchar*) &tmp; } -Item *in_datetime::create_item(THD *thd) +Item *in_temporal::create_item(THD *thd) { return new (thd->mem_root) Item_datetime(thd); } @@ -3863,25 +3726,95 @@ Item *in_decimal::create_item(THD *thd) } -cmp_item* cmp_item::get_comparator(Item_result type, Item *warn_item, - CHARSET_INFO *cs) +bool Predicant_to_list_comparator::alloc_comparators(THD *thd, uint nargs) { - switch (type) { - case STRING_RESULT: - return new cmp_item_sort_string(cs); - case INT_RESULT: - return new cmp_item_int; - case REAL_RESULT: - return new cmp_item_real; - case ROW_RESULT: - return new cmp_item_row; - case DECIMAL_RESULT: - return new cmp_item_decimal; - case TIME_RESULT: - DBUG_ASSERT(warn_item); - return new cmp_item_datetime(warn_item); + size_t nbytes= sizeof(Predicant_to_value_comparator) * nargs; + if (!(m_comparators= (Predicant_to_value_comparator *) thd->alloc(nbytes))) + return true; + memset(m_comparators, 0, nbytes); + return false; +} + + +bool Predicant_to_list_comparator::add_value(const char *funcname, + Item_args *args, + uint value_index) +{ + DBUG_ASSERT(m_predicant_index < args->argument_count()); + DBUG_ASSERT(value_index < args->argument_count()); + Type_handler_hybrid_field_type tmp; + Item *tmpargs[2]; + tmpargs[0]= args->arguments()[m_predicant_index]; + tmpargs[1]= args->arguments()[value_index]; + if (tmp.aggregate_for_comparison(funcname, tmpargs, 2, true)) + { + DBUG_ASSERT(current_thd->is_error()); + return true; + } + m_comparators[m_comparator_count].m_handler= tmp.type_handler(); + m_comparators[m_comparator_count].m_arg_index= value_index; + m_comparator_count++; + return false; +} + + +bool Predicant_to_list_comparator::add_value_skip_null(const char *funcname, + Item_args *args, + uint value_index, + bool *nulls_found) +{ + /* + Skip explicit NULL constant items. + Using real_item() to correctly detect references to explicit NULLs + in HAVING clause, e.g. in this example "b" is skipped: + SELECT a,NULL AS b FROM t1 GROUP BY a HAVING 'A' IN (b,'A'); + */ + if (args->arguments()[value_index]->real_item()->type() == Item::NULL_ITEM) + { + *nulls_found= true; + return false; } - return 0; // to satisfy compiler :) + return add_value(funcname, args, value_index); +} + + +void Predicant_to_list_comparator:: + detect_unique_handlers(Type_handler_hybrid_field_type *compatible, + uint *unique_count, + uint *found_types) +{ + *unique_count= 0; + *found_types= 0; + for (uint i= 0; i < m_comparator_count; i++) + { + uint idx; + if (find_handler(&idx, m_comparators[i].m_handler, i)) + { + m_comparators[i].m_handler_index= i; // New unique handler + (*unique_count)++; + (*found_types)|= 1U << m_comparators[i].m_handler->cmp_type(); + compatible->set_handler(m_comparators[i].m_handler); + } + else + { + m_comparators[i].m_handler_index= idx; // Non-unique handler + } + } +} + + +bool Predicant_to_list_comparator::make_unique_cmp_items(THD *thd, + CHARSET_INFO *cs) +{ + for (uint i= 0; i < m_comparator_count; i++) + { + if (m_comparators[i].m_handler && // Skip implicit NULLs + m_comparators[i].m_handler_index == i && // Skip non-unuque + !(m_comparators[i].m_cmp_item= + m_comparators[i].m_handler->make_cmp_item(thd, cs))) + return true; + } + return false; } @@ -3922,19 +3855,23 @@ cmp_item_row::~cmp_item_row() } -void cmp_item_row::alloc_comparators() +bool cmp_item_row::alloc_comparators(THD *thd, uint cols) { - if (!comparators) - comparators= (cmp_item **) current_thd->calloc(sizeof(cmp_item *)*n); + if (comparators) + { + DBUG_ASSERT(cols == n); + return false; + } + return + !(comparators= (cmp_item **) thd->calloc(sizeof(cmp_item *) * (n= cols))); } void cmp_item_row::store_value(Item *item) { DBUG_ENTER("cmp_item_row::store_value"); - n= item->cols(); - alloc_comparators(); - if (comparators) + THD *thd= current_thd; + if (!alloc_comparators(thd, item->cols())) { item->bring_value(); item->null_value= 0; @@ -3942,10 +3879,25 @@ void cmp_item_row::store_value(Item *item) { if (!comparators[i]) { - DBUG_ASSERT(item->element_index(i)->cmp_type() != TIME_RESULT); + /** + Comparators for the row elements that have temporal data types + are installed at initialization time by prepare_comparators(). + Here we install comparators for the other data types. + There is a bug in the below code. See MDEV-11511. + When performing: + (predicant0,predicant1) IN ((value00,value01),(value10,value11)) + It uses only the data type and the collation of the predicant + elements only. It should be fixed to aggregate the data type and + the collation for all elements at the N-th positions of the + predicate and all values: + - predicate0, value00, value01 + - predicate1, value10, value11 + */ + Item *elem= item->element_index(i); + const Type_handler *handler= elem->type_handler(); + DBUG_ASSERT(elem->cmp_type() != TIME_RESULT); if (!(comparators[i]= - cmp_item::get_comparator(item->element_index(i)->result_type(), 0, - item->element_index(i)->collation.collation))) + handler->make_cmp_item(thd, elem->collation.collation))) break; // new failed } comparators[i]->store_value(item->element_index(i)); @@ -4033,6 +3985,14 @@ void cmp_item_decimal::store_value(Item *item) } +int cmp_item_decimal::cmp_not_null(const Value *val) +{ + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_decimal()); + return my_decimal_cmp(&value, &val->m_decimal); +} + + int cmp_item_decimal::cmp(Item *arg) { my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf); @@ -4054,34 +4014,62 @@ cmp_item* cmp_item_decimal::make_same() } -void cmp_item_datetime::store_value(Item *item) +void cmp_item_temporal::store_value_internal(Item *item, + enum_field_types f_type) { bool is_null; Item **tmp_item= lval_cache ? &lval_cache : &item; - enum_field_types f_type= - tmp_item[0]->field_type_for_temporal_comparison(warn_item); value= get_datetime_value(0, &tmp_item, &lval_cache, f_type, &is_null); m_null_value= item->null_value; } +int cmp_item_datetime::cmp_not_null(const Value *val) +{ + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_temporal()); + return value != pack_time(&val->value.m_time); +} + + int cmp_item_datetime::cmp(Item *arg) { - const bool rc= value != arg->val_temporal_packed(warn_item); + const bool rc= value != arg->val_datetime_packed(); + return (m_null_value || arg->null_value) ? UNKNOWN : rc; +} + + +int cmp_item_time::cmp_not_null(const Value *val) +{ + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_temporal()); + return value != pack_time(&val->value.m_time); +} + + +int cmp_item_time::cmp(Item *arg) +{ + const bool rc= value != arg->val_time_packed(); return (m_null_value || arg->null_value) ? UNKNOWN : rc; } -int cmp_item_datetime::compare(cmp_item *ci) +int cmp_item_temporal::compare(cmp_item *ci) { - cmp_item_datetime *l_cmp= (cmp_item_datetime *)ci; + cmp_item_temporal *l_cmp= (cmp_item_temporal *)ci; return (value < l_cmp->value) ? -1 : ((value == l_cmp->value) ? 0 : 1); } cmp_item *cmp_item_datetime::make_same() { - return new cmp_item_datetime(warn_item); + return new cmp_item_datetime(); +} + + +cmp_item *cmp_item_time::make_same() +{ + return new cmp_item_time(); } @@ -4172,207 +4160,207 @@ void Item_func_in::fix_after_pullout(st_select_lex *new_parent, Item **ref) eval_not_null_tables(NULL); } -static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) + +bool Item_func_in::prepare_predicant_and_values(THD *thd, uint *found_types) { - return cs->coll->strnncollsp(cs, - (uchar *) x->ptr(),x->length(), - (uchar *) y->ptr(),y->length()); + uint type_cnt; + have_null= false; + + add_predicant(this, 0); + for (uint i= 1 ; i < arg_count; i++) + { + if (add_value_skip_null(Item_func_in::func_name(), this, i, &have_null)) + return true; + } + all_values_added(&m_comparator, &type_cnt, found_types); + arg_types_compatible= type_cnt < 2; + +#ifndef DBUG_OFF + Predicant_to_list_comparator::debug_print(thd); +#endif + return false; } + void Item_func_in::fix_length_and_dec() { - Item **arg, **arg_end; - bool const_itm= 1; THD *thd= current_thd; - /* TRUE <=> arguments values will be compared as DATETIMEs. */ - Item *date_arg= 0; - uint found_types= 0; - uint type_cnt= 0, i; - m_compare_type= STRING_RESULT; - left_cmp_type= args[0]->cmp_type(); - if (!(found_types= collect_cmp_types(args, arg_count, true))) - return; - - for (arg= args + 1, arg_end= args + arg_count; arg != arg_end ; arg++) - { - if (!arg[0]->const_item()) - { - const_itm= 0; - break; - } - } - for (i= 0; i <= (uint)TIME_RESULT; i++) + uint found_types; + m_comparator.set_handler(type_handler_varchar.type_handler_for_comparison()); + max_length= 1; + + if (prepare_predicant_and_values(thd, &found_types)) { - if (found_types & (1U << i)) - { - (type_cnt)++; - m_compare_type= (Item_result) i; - } + DBUG_ASSERT(thd->is_error()); // Must set error + return; } - /* - First conditions for bisection to be possible: - 1. All types are similar, and - 2. All expressions in <in value list> are const - */ - bool bisection_possible= - type_cnt == 1 && // 1 - const_itm; // 2 - if (bisection_possible) + if (arg_types_compatible) // Bisection condition #1 { - /* - In the presence of NULLs, the correct result of evaluating this item - must be UNKNOWN or FALSE. To achieve that: - - If type is scalar, we can use bisection and the "have_null" boolean. - - If type is ROW, we will need to scan all of <in value list> when - searching, so bisection is impossible. Unless: - 3. UNKNOWN and FALSE are equivalent results - 4. Neither left expression nor <in value list> contain any NULL value - */ - - if (m_compare_type == ROW_RESULT && - ((!is_top_level_item() || negated) && // 3 - (list_contains_null() || args[0]->maybe_null))) // 4 - bisection_possible= false; + m_comparator.type_handler()-> + Item_func_in_fix_comparator_compatible_types(thd, this); } - - if (type_cnt == 1) + else { - if (m_compare_type == STRING_RESULT && - agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) - return; - arg_types_compatible= TRUE; + DBUG_ASSERT(m_comparator.cmp_type() != ROW_RESULT); + fix_for_scalar_comparison_using_cmp_items(thd, found_types); + } - if (m_compare_type == ROW_RESULT) - { - uint cols= args[0]->cols(); - cmp_item_row *cmp= 0; + DBUG_EXECUTE_IF("Item_func_in", + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, "DBUG: types_compatible=%s bisect=%s", + arg_types_compatible ? "yes" : "no", + array != NULL ? "yes" : "no");); +} - if (bisection_possible) - { - array= new (thd->mem_root) in_row(thd, arg_count-1, 0); - cmp= &((in_row*)array)->tmp; - } - else - { - if (!(cmp= new (thd->mem_root) cmp_item_row)) - return; - cmp_items[ROW_RESULT]= cmp; - } - cmp->n= cols; - cmp->alloc_comparators(); - for (uint col= 0; col < cols; col++) - { - date_arg= find_date_time_item(args, arg_count, col); - if (date_arg) - { - cmp_item **cmp= 0; - if (array) - cmp= ((in_row*)array)->tmp.comparators + col; - else - cmp= ((cmp_item_row*)cmp_items[ROW_RESULT])->comparators + col; - *cmp= new (thd->mem_root) cmp_item_datetime(date_arg); - } - } - } - } +/** + Populate Item_func_in::array with constant not-NULL arguments and sort them. - if (bisection_possible) + Sets "have_null" to true if some of the values appeared to be NULL. + Note, explicit NULLs were found during prepare_predicant_and_values(). + So "have_null" can already be true before the fix_in_vector() call. + Here we additionally catch implicit NULLs. +*/ +void Item_func_in::fix_in_vector() +{ + DBUG_ASSERT(array); + uint j=0; + for (uint i=1 ; i < arg_count ; i++) { - /* - IN must compare INT columns and constants as int values (the same - way as equality does). - So we must check here if the column on the left and all the constant - values on the right can be compared as integers and adjust the - comparison type accordingly. - - See the comment about the similar block in Item_bool_func2 - */ - if (args[0]->real_item()->type() == FIELD_ITEM && - !thd->lex->is_view_context_analysis() && m_compare_type != INT_RESULT) + array->set(j,args[i]); + if (!args[i]->null_value) + j++; // include this cell in the array. + else { - Item_field *field_item= (Item_field*) (args[0]->real_item()); - if (field_item->field_type() == MYSQL_TYPE_LONGLONG || - field_item->field_type() == MYSQL_TYPE_YEAR) - { - bool all_converted= TRUE; - for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) - { - if (!convert_const_to_int(thd, field_item, &arg[0])) - all_converted= FALSE; - } - if (all_converted) - m_compare_type= INT_RESULT; - } - } - switch (m_compare_type) { - case STRING_RESULT: - array=new (thd->mem_root) in_string(thd, arg_count - 1, - (qsort2_cmp) srtcmp_in, - cmp_collation.collation); - break; - case INT_RESULT: - array= new (thd->mem_root) in_longlong(thd, arg_count - 1); - break; - case REAL_RESULT: - array= new (thd->mem_root) in_double(thd, arg_count - 1); - break; - case ROW_RESULT: /* - The row comparator was created at the beginning but only DATETIME - items comparators were initialized. Call store_value() to setup - others. + We don't put NULL values in array, to avoid erronous matches in + bisection. */ - ((in_row*)array)->tmp.store_value(args[0]); - break; - case DECIMAL_RESULT: - array= new (thd->mem_root) in_decimal(thd, arg_count - 1); - break; - case TIME_RESULT: - date_arg= find_date_time_item(args, arg_count, 0); - array= new (thd->mem_root) in_datetime(thd, date_arg, arg_count - 1); - break; + have_null= 1; } - if (!array || thd->is_fatal_error) // OOM - return; - uint j=0; - for (uint i=1 ; i < arg_count ; i++) + } + if ((array->used_count= j)) + array->sort(); +} + + +/** + Convert all items in <in value list> to INT. + + IN must compare INT columns and constants as int values (the same + way as equality does). + So we must check here if the column on the left and all the constant + values on the right can be compared as integers and adjust the + comparison type accordingly. + + See the comment about the similar block in Item_bool_func2 +*/ +bool Item_func_in::value_list_convert_const_to_int(THD *thd) +{ + if (args[0]->real_item()->type() == FIELD_ITEM && + !thd->lex->is_view_context_analysis()) + { + Item_field *field_item= (Item_field*) (args[0]->real_item()); + if (field_item->field_type() == MYSQL_TYPE_LONGLONG || + field_item->field_type() == MYSQL_TYPE_YEAR) { - array->set(j,args[i]); - if (!args[i]->null_value) - j++; // include this cell in the array. - else + bool all_converted= TRUE; + Item **arg, **arg_end; + for (arg=args+1, arg_end=args+arg_count; arg != arg_end ; arg++) { - /* - We don't put NULL values in array, to avoid erronous matches in - bisection. - */ - have_null= 1; + if (!convert_const_to_int(thd, field_item, &arg[0])) + all_converted= FALSE; } + if (all_converted) + m_comparator.set_handler(&type_handler_longlong); } - if ((array->used_count= j)) - array->sort(); } - else + return thd->is_fatal_error; // Catch errrors in convert_const_to_int +} + + +/** + Historically this code installs comparators at initialization time + for temporal ROW elements only. All other comparators are installed later, + during the first store_value(). This causes the bug MDEV-11511. + See also comments in cmp_item_row::store_value(). +*/ +bool cmp_item_row::prepare_comparators(THD *thd, Item **args, uint arg_count) +{ + for (uint col= 0; col < n; col++) { - if (found_types & (1U << TIME_RESULT)) - date_arg= find_date_time_item(args, arg_count, 0); - if (found_types & (1U << STRING_RESULT) && - agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) - return; - for (i= 0; i <= (uint) TIME_RESULT; i++) + Item *date_arg= find_date_time_item(args, arg_count, col); + if (date_arg) { - if (found_types & (1U << i) && !cmp_items[i]) - { - if (!cmp_items[i] && !(cmp_items[i]= - cmp_item::get_comparator((Item_result)i, date_arg, - cmp_collation.collation))) - return; - } + // TODO: do like the scalar comparators do + const Type_handler *h= date_arg->type_handler(); + comparators[col]= h->field_type() == MYSQL_TYPE_TIME ? + (cmp_item *) new (thd->mem_root) cmp_item_time() : + (cmp_item *) new (thd->mem_root) cmp_item_datetime(); + if (!comparators[col]) + return true; } } - max_length= 1; + return false; +} + + +bool Item_func_in::fix_for_row_comparison_using_bisection(THD *thd) +{ + uint cols= args[0]->cols(); + if (!(array= new (thd->mem_root) in_row(thd, arg_count-1, 0))) + return true; + cmp_item_row *cmp= &((in_row*)array)->tmp; + if (cmp->alloc_comparators(thd, cols) || + cmp->prepare_comparators(thd, args, arg_count)) + return true; + /* + Only DATETIME items comparators were initialized. + Call store_value() to setup others. + */ + cmp->store_value(args[0]); + if (thd->is_fatal_error) // OOM + return true; + fix_in_vector(); + return false; +} + + +/** + This method is called for scalar data types when bisection is not possible, + for example: + - Some of args[1..arg_count] are not constants. + - args[1..arg_count] are constants, but pairs {args[0],args[1..arg_count]} + are compared by different data types, e.g.: + WHERE decimal_expr IN (1, 1e0) + The pair {args[0],args[1]} is compared by type_handler_decimal. + The pair {args[0],args[2]} is compared by type_handler_double. +*/ +bool Item_func_in::fix_for_scalar_comparison_using_cmp_items(THD *thd, + uint found_types) +{ + if (found_types & (1U << STRING_RESULT) && + agg_arg_charsets_for_comparison(cmp_collation, args, arg_count)) + return true; + if (make_unique_cmp_items(thd, cmp_collation.collation)) + return true; + return false; +} + + +/** + This method is called for the ROW data type when bisection is not possible. +*/ +bool Item_func_in::fix_for_row_comparison_using_cmp_items(THD *thd) +{ + if (make_unique_cmp_items(thd, cmp_collation.collation)) + return true; + DBUG_ASSERT(get_comparator_type_handler(0) == &type_handler_row); + DBUG_ASSERT(get_comparator_cmp_item(0)); + cmp_item_row *cmp_row= (cmp_item_row*) get_comparator_cmp_item(0); + return cmp_row->alloc_comparators(thd, args[0]->cols()) || + cmp_row->prepare_comparators(thd, args, arg_count); } @@ -4414,9 +4402,7 @@ void Item_func_in::print(String *str, enum_query_type query_type) longlong Item_func_in::val_int() { - cmp_item *in_item; DBUG_ASSERT(fixed == 1); - uint value_added_map= 0; if (array) { bool tmp=array->find(args[0]); @@ -4434,29 +4420,13 @@ longlong Item_func_in::val_int() if ((null_value= args[0]->real_item()->type() == NULL_ITEM)) return 0; - have_null= 0; - for (uint i= 1 ; i < arg_count ; i++) + null_value= have_null; + uint idx; + if (!Predicant_to_list_comparator::cmp(this, &idx, &null_value)) { - if (args[i]->real_item()->type() == NULL_ITEM) - { - have_null= TRUE; - continue; - } - Item_result cmp_type= item_cmp_type(left_cmp_type, args[i]); - in_item= cmp_items[(uint)cmp_type]; - DBUG_ASSERT(in_item); - if (!(value_added_map & (1U << (uint)cmp_type))) - { - in_item->store_value(args[0]); - value_added_map|= 1U << (uint)cmp_type; - } - const int rc= in_item->cmp(args[i]); - if (rc == FALSE) - return (longlong) (!negated); - have_null|= (rc == UNKNOWN); + null_value= false; + return (longlong) (!negated); } - - null_value= have_null; return (longlong) (!null_value && negated); } @@ -5714,6 +5684,7 @@ Item_func_regexp_instr::fix_length_and_dec() re.init(cmp_collation.collation, 0); re.fix_owner(this, args[0], args[1]); + max_length= MY_INT32_NUM_DECIMAL_DIGITS; // See also Item_func_locate } @@ -6193,10 +6164,11 @@ Item *Item_bool_rowready_func2::negated_item(THD *thd) of the type Item_field or Item_direct_view_ref(Item_field). */ -Item_equal::Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item): +Item_equal::Item_equal(THD *thd, const Type_handler *handler, + Item *f1, Item *f2, bool with_const_item): Item_bool_func(thd), eval_item(0), cond_false(0), cond_true(0), context_field(NULL), link_equal_fields(FALSE), - m_compare_type(item_cmp_type(f1, f2)), + m_compare_handler(handler), m_compare_collation(f2->collation.collation) { const_item_cache= 0; @@ -6222,7 +6194,7 @@ Item_equal::Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item): Item_equal::Item_equal(THD *thd, Item_equal *item_equal): Item_bool_func(thd), eval_item(0), cond_false(0), cond_true(0), context_field(NULL), link_equal_fields(FALSE), - m_compare_type(item_equal->m_compare_type), + m_compare_handler(item_equal->m_compare_handler), m_compare_collation(item_equal->m_compare_collation) { const_item_cache= 0; @@ -6265,7 +6237,7 @@ void Item_equal::add_const(THD *thd, Item *c) return; } Item *const_item= get_const(); - switch (Item_equal::compare_type()) { + switch (Item_equal::compare_type_handler()->cmp_type()) { case TIME_RESULT: { enum_field_types f_type= context_field->field_type(); @@ -6744,8 +6716,8 @@ longlong Item_equal::val_int() void Item_equal::fix_length_and_dec() { Item *item= get_first(NO_PARTICULAR_TAB, NULL); - eval_item= cmp_item::get_comparator(item->cmp_type(), item, - item->collation.collation); + const Type_handler *handler= item->type_handler(); + eval_item= handler->make_cmp_item(current_thd, item->collation.collation); } diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index ceca57264b7..0cb66cd1d33 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -47,7 +47,7 @@ typedef int (*Item_field_cmpfunc)(Item *f1, Item *f2, void *arg); class Arg_comparator: public Sql_alloc { Item **a, **b; - Item_result m_compare_type; + const Type_handler *m_compare_handler; CHARSET_INFO *m_compare_collation; arg_cmp_func func; Item_func_or_sum *owner; @@ -57,7 +57,7 @@ class Arg_comparator: public Sql_alloc /* Fields used in DATE/DATETIME comparison. */ Item *a_cache, *b_cache; // Cached values of a and b items // when one of arguments is NULL. - int set_compare_func(Item_func_or_sum *owner, Item_result type); + int set_cmp_func(Item_func_or_sum *owner_arg, Item **a1, Item **a2); int compare_temporal(enum_field_types type); @@ -67,17 +67,27 @@ public: /* Allow owner function to use string buffers. */ String value1, value2; - Arg_comparator(): m_compare_type(STRING_RESULT), + Arg_comparator(): + m_compare_handler(&type_handler_null), m_compare_collation(&my_charset_bin), set_null(TRUE), comparators(0), a_cache(0), b_cache(0) {}; Arg_comparator(Item **a1, Item **a2): a(a1), b(a2), - m_compare_type(STRING_RESULT), + m_compare_handler(&type_handler_null), m_compare_collation(&my_charset_bin), set_null(TRUE), comparators(0), a_cache(0), b_cache(0) {}; public: + bool set_cmp_func_for_row_arguments(); + bool set_cmp_func_row(); + bool set_cmp_func_string(); + bool set_cmp_func_time(); + bool set_cmp_func_datetime(); + bool set_cmp_func_int(); + bool set_cmp_func_real(); + bool set_cmp_func_decimal(); + inline int set_cmp_func(Item_func_or_sum *owner_arg, Item **a1, Item **a2, bool set_null_arg) { @@ -114,14 +124,14 @@ public: int compare_e_str_json(); Item** cache_converted_constant(THD *thd, Item **value, Item **cache, - Item_result type); - static arg_cmp_func comparator_matrix [6][2]; + const Type_handler *type); inline bool is_owner_equal_func() { return (owner->type() == Item::FUNC_ITEM && ((Item_func*)owner)->functype() == Item_func::EQUAL_FUNC); } - Item_result compare_type() const { return m_compare_type; } + const Type_handler *compare_type_handler() const { return m_compare_handler; } + Item_result compare_type() const { return m_compare_handler->cmp_type(); } CHARSET_INFO *compare_collation() const { return m_compare_collation; } Arg_comparator *subcomparators() const { return comparators; } void cleanup() @@ -200,6 +210,7 @@ public: Item_bool_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {} Item_bool_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { } Item_bool_func(THD *thd, Item_bool_func *item) :Item_int_func(thd, item) {} + const Type_handler *type_handler() const { return &type_handler_long; } bool is_bool_type() { return true; } virtual CHARSET_INFO *compare_collation() const { return NULL; } void fix_length_and_dec() { decimals=0; max_length=1; } @@ -388,7 +399,7 @@ public: Specifies which result type the function uses to compare its arguments. This method is used in equal field propagation. */ - virtual Item_result compare_type() const + virtual const Type_handler *compare_type_handler() const { /* Have STRING_RESULT by default, which means the function compares @@ -396,7 +407,7 @@ public: and for Item_func_spatial_rel. Note, Item_bool_rowready_func2 overrides this default behaviour. */ - return STRING_RESULT; + return &type_handler_varchar; } SEL_TREE *get_mm_tree(RANGE_OPT_PARAM *param, Item **cond_ptr) { @@ -476,12 +487,14 @@ class Item_bool_rowready_func2 :public Item_bool_func2_with_rev { protected: Arg_comparator cmp; + bool check_arguments() const + { + return check_argument_types_like_args0(); + } public: Item_bool_rowready_func2(THD *thd, Item *a, Item *b): Item_bool_func2_with_rev(thd, a, b), cmp(tmp_arg, tmp_arg + 1) - { - allowed_arg_cols= 0; // Fetch this value from first argument - } + { } void print(String *str, enum_query_type query_type) { Item_func::print_op(str, query_type); @@ -493,7 +506,7 @@ public: { Item_args::propagate_equal_fields(thd, Context(ANY_SUBST, - cmp.compare_type(), + cmp.compare_type_handler(), compare_collation()), cond); return this; @@ -504,7 +517,10 @@ public: return cmp.set_cmp_func(this, tmp_arg, tmp_arg + 1, true); } CHARSET_INFO *compare_collation() const { return cmp.compare_collation(); } - Item_result compare_type() const { return cmp.compare_type(); } + const Type_handler *compare_type_handler() const + { + return cmp.compare_type_handler(); + } Arg_comparator *get_comparator() { return &cmp; } void cleanup() { @@ -830,10 +846,10 @@ class Item_func_opt_neg :public Item_bool_func { protected: /* - The result type that will be used for comparison. - cmp_type() of all arguments are collected to here. + The data type handler that will be used for comparison. + Data type handlers of all arguments are mixed to here. */ - Item_result m_compare_type; + Type_handler_hybrid_field_type m_comparator; /* The collation that will be used for comparison in case when m_compare_type is STRING_RESULT. @@ -868,15 +884,22 @@ protected: Field *field, Item *value); public: String value0,value1,value2; - /* TRUE <=> arguments will be compared as dates. */ - Item *compare_as_dates; Item_func_between(THD *thd, Item *a, Item *b, Item *c): - Item_func_opt_neg(thd, a, b, c), compare_as_dates(FALSE) { } - longlong val_int(); + Item_func_opt_neg(thd, a, b, c) { } + longlong val_int() + { + DBUG_ASSERT(fixed); + return m_comparator.type_handler()->Item_func_between_val_int(this); + } enum Functype functype() const { return BETWEEN; } const char *func_name() const { return "between"; } enum precedence precedence() const { return BETWEEN_PRECEDENCE; } void fix_length_and_dec(); + bool fix_length_and_dec_string(THD *) + { + return agg_arg_charsets_for_comparison(cmp_collation, args, 3); + } + bool fix_length_and_dec_numeric(THD *); virtual void print(String *str, enum_query_type query_type); bool eval_not_null_tables(void *opt_arg); void fix_after_pullout(st_select_lex *new_parent, Item **ref); @@ -889,23 +912,31 @@ public: { Item_args::propagate_equal_fields(thd, Context(ANY_SUBST, - m_compare_type, + m_comparator.type_handler(), compare_collation()), cond); return this; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_between>(thd, mem_root, this); } + + longlong val_int_cmp_string(); + longlong val_int_cmp_temporal(); + longlong val_int_cmp_int(); + longlong val_int_cmp_real(); + longlong val_int_cmp_decimal(); }; -class Item_func_strcmp :public Item_int_func +class Item_func_strcmp :public Item_long_func { + bool check_arguments() const + { return check_argument_types_can_return_str(0, 2); } String value1, value2; DTCollation cmp_collation; public: Item_func_strcmp(THD *thd, Item *a, Item *b): - Item_int_func(thd, a, b) {} + Item_long_func(thd, a, b) {} longlong val_int(); uint decimal_precision() const { return 1; } const char *func_name() const { return "strcmp"; } @@ -926,17 +957,19 @@ struct interval_range my_decimal dec; }; -class Item_func_interval :public Item_int_func +class Item_func_interval :public Item_long_func { Item_row *row; bool use_decimal_comparison; interval_range *intervals; -public: - Item_func_interval(THD *thd, Item_row *a): - Item_int_func(thd, a), row(a), intervals(0) + bool check_arguments() const { - allowed_arg_cols= 0; // Fetch this value from first argument + return check_argument_types_like_args0(); } +public: + Item_func_interval(THD *thd, Item_row *a): + Item_long_func(thd, a), row(a), intervals(0) + { } longlong val_int(); void fix_length_and_dec(); const char *func_name() const { return "interval"; } @@ -951,13 +984,13 @@ public: }; -class Item_func_coalesce :public Item_func_hybrid_field_type +class Item_func_coalesce :public Item_func_case_expression { public: Item_func_coalesce(THD *thd, Item *a, Item *b): - Item_func_hybrid_field_type(thd, a, b) {} + Item_func_case_expression(thd, a, b) {} Item_func_coalesce(THD *thd, List<Item> &list): - Item_func_hybrid_field_type(thd, list) {} + Item_func_case_expression(thd, list) {} double real_op(); longlong int_op(); String *str_op(String *); @@ -965,8 +998,8 @@ public: bool date_op(MYSQL_TIME *ltime,uint fuzzydate); void fix_length_and_dec() { - set_handler_by_field_type(agg_field_type(args, arg_count, true)); - fix_attributes(args, arg_count); + if (!aggregate_for_result(func_name(), args, arg_count, true)) + fix_attributes(args, arg_count); } const char *func_name() const { return "coalesce"; } table_map not_null_tables() const { return 0; } @@ -979,21 +1012,49 @@ public: Case abbreviations that aggregate its result field type by two arguments: IFNULL(arg1, arg2) IF(switch, arg1, arg2) + NVL2(switch, arg1, arg2) */ -class Item_func_case_abbreviation2 :public Item_func_hybrid_field_type +class Item_func_case_abbreviation2 :public Item_func_case_expression { protected: void fix_length_and_dec2(Item **items) { - set_handler_by_field_type(agg_field_type(items, 2, true)); - fix_attributes(items, 2); + if (!aggregate_for_result(func_name(), items, 2, true)) + fix_attributes(items, 2); + } + + void cache_type_info(const Item *source, bool maybe_null_arg) + { + Type_std_attributes::set(source); + set_handler(source->type_handler()); + maybe_null= maybe_null_arg; } - uint decimal_precision2(Item **args) const; + + void fix_length_and_dec2_eliminate_null(Item **items) + { + // Let IF(cond, expr, NULL) and IF(cond, NULL, expr) inherit type from expr. + if (items[0]->type() == NULL_ITEM) + { + cache_type_info(items[1], true); + // If both arguments are NULL, make resulting type BINARY(0). + if (items[1]->type() == NULL_ITEM) + set_handler(&type_handler_string); + } + else if (items[1]->type() == NULL_ITEM) + { + cache_type_info(items[0], true); + } + else + { + fix_length_and_dec2(items); + } + } + public: Item_func_case_abbreviation2(THD *thd, Item *a, Item *b): - Item_func_hybrid_field_type(thd, a, b) { } + Item_func_case_expression(thd, a, b) { } Item_func_case_abbreviation2(THD *thd, Item *a, Item *b, Item *c): - Item_func_hybrid_field_type(thd, a, b, c) { } + Item_func_case_expression(thd, a, b, c) { } }; @@ -1013,35 +1074,67 @@ public: maybe_null= args[1]->maybe_null; } const char *func_name() const { return "ifnull"; } - Field *create_field_for_create_select(TABLE *table) - { return tmp_table_field_from_field_type(table, false, false); } table_map not_null_tables() const { return 0; } - uint decimal_precision() const - { - return Item_func_case_abbreviation2::decimal_precision2(args); - } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_ifnull>(thd, mem_root, this); } }; -class Item_func_if :public Item_func_case_abbreviation2 +/** + Case abbreviations that have a switch argument and + two return arguments to choose from. Returns the value + of either of the two return arguments depending on the switch argument value. + + IF(switch, arg1, arg2) + NVL(switch, arg1, arg2) +*/ +class Item_func_case_abbreviation2_switch: public Item_func_case_abbreviation2 { +protected: + virtual Item *find_item() const= 0; + +public: + Item_func_case_abbreviation2_switch(THD *thd, Item *a, Item *b, Item *c) + :Item_func_case_abbreviation2(thd, a, b, c) + { } + + bool date_op(MYSQL_TIME *ltime, uint fuzzydate) + { + return get_date_with_conversion_from_item(find_item(), ltime, fuzzydate); + } + longlong int_op() + { + return val_int_from_item(find_item()); + } + double real_op() + { + return val_real_from_item(find_item()); + } + my_decimal *decimal_op(my_decimal *decimal_value) + { + return val_decimal_from_item(find_item(), decimal_value); + } + String *str_op(String *str) + { + return val_str_from_item(find_item(), str); + } +}; + + +class Item_func_if :public Item_func_case_abbreviation2_switch +{ +protected: + Item *find_item() const { return args[0]->val_bool() ? args[1] : args[2]; } + public: Item_func_if(THD *thd, Item *a, Item *b, Item *c): - Item_func_case_abbreviation2(thd, a, b, c) + Item_func_case_abbreviation2_switch(thd, a, b, c) {} - bool date_op(MYSQL_TIME *ltime, uint fuzzydate); - longlong int_op(); - double real_op(); - my_decimal *decimal_op(my_decimal *); - String *str_op(String *); bool fix_fields(THD *, Item **); - void fix_length_and_dec(); - uint decimal_precision() const + void fix_length_and_dec() { - return Item_func_case_abbreviation2::decimal_precision2(args + 1); + fix_length_and_dec2_eliminate_null(args + 1); } const char *func_name() const { return "if"; } bool eval_not_null_tables(void *opt_arg); @@ -1053,7 +1146,26 @@ private: }; -class Item_func_nullif :public Item_func_hybrid_field_type +class Item_func_nvl2 :public Item_func_case_abbreviation2_switch +{ +protected: + Item *find_item() const { return args[0]->is_null() ? args[2] : args[1]; } + +public: + Item_func_nvl2(THD *thd, Item *a, Item *b, Item *c): + Item_func_case_abbreviation2_switch(thd, a, b, c) + {} + const char *func_name() const { return "nvl2"; } + void fix_length_and_dec() + { + fix_length_and_dec2_eliminate_null(args + 1); + } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_nvl2>(thd, mem_root, this); } +}; + + +class Item_func_nullif :public Item_func_case_expression { Arg_comparator cmp; /* @@ -1091,7 +1203,7 @@ public: See also Item_func_nullif::fix_length_and_dec(). */ Item_func_nullif(THD *thd, Item *a, Item *b): - Item_func_hybrid_field_type(thd, a, b, a), + Item_func_case_expression(thd, a, b, a), m_cache(NULL), m_arg0(NULL) { arg_count--; } @@ -1107,7 +1219,6 @@ public: my_decimal *decimal_op(my_decimal *); void fix_length_and_dec(); bool walk(Item_processor processor, bool walk_subquery, void *arg); - uint decimal_precision() const { return args[2]->decimal_precision(); } const char *func_name() const { return "nullif"; } void print(String *str, enum_query_type query_type); void split_sum_func(THD *thd, Ref_ptr_array ref_pointer_array, @@ -1117,7 +1228,8 @@ public: bool is_null(); Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) { - Context cmpctx(ANY_SUBST, cmp.compare_type(), cmp.compare_collation()); + Context cmpctx(ANY_SUBST, cmp.compare_type_handler(), + cmp.compare_collation()); const Item *old0= args[0]; args[0]->propagate_equal_fields_and_change_item_tree(thd, cmpctx, cond, &args[0]); @@ -1270,18 +1382,16 @@ public: If the left item is a constant one then its value is cached in the lval_cache variable. */ -class in_datetime :public in_longlong +class in_temporal :public in_longlong { +protected: + uchar *get_value_internal(Item *item, enum_field_types f_type); public: - /* An item used to issue warnings. */ - Item *warn_item; /* Cache for the left item. */ Item *lval_cache; - in_datetime(THD *thd, Item *warn_item_arg, uint elements) - :in_longlong(thd, elements), warn_item(warn_item_arg), lval_cache(0) {}; - void set(uint pos,Item *item); - uchar *get_value(Item *item); + in_temporal(THD *thd, uint elements) + :in_longlong(thd, elements), lval_cache(0) {}; Item *create_item(THD *thd); void value_to_item(uint pos, Item *item) { @@ -1293,6 +1403,30 @@ public: }; +class in_datetime :public in_temporal +{ +public: + in_datetime(THD *thd, uint elements) + :in_temporal(thd, elements) + {} + void set(uint pos,Item *item); + uchar *get_value(Item *item) + { return get_value_internal(item, MYSQL_TYPE_DATETIME); } +}; + + +class in_time :public in_temporal +{ +public: + in_time(THD *thd, uint elements) + :in_temporal(thd, elements) + {} + void set(uint pos,Item *item); + uchar *get_value(Item *item) + { return get_value_internal(item, MYSQL_TYPE_TIME); } +}; + + class in_double :public in_vector { double tmp; @@ -1343,10 +1477,9 @@ public: "stored argument's value <> item's value" */ virtual int cmp(Item *item)= 0; + virtual int cmp_not_null(const Value *value)= 0; // for optimized IN with row virtual int compare(cmp_item *item)= 0; - static cmp_item* get_comparator(Item_result type, Item * warn_item, - CHARSET_INFO *cs); virtual cmp_item *make_same()= 0; virtual void store_value_by_template(THD *thd, cmp_item *tmpl, Item *item) { @@ -1389,6 +1522,12 @@ public: value_res= item->val_str(&value); m_null_value= item->null_value; } + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_string()); + return sortcmp(value_res, &val->m_string, cmp_charset) != 0; + } int cmp(Item *arg) { char buff[STRING_BUFFER_USUAL_SIZE]; @@ -1425,6 +1564,12 @@ public: value= item->val_int(); m_null_value= item->null_value; } + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_longlong()); + return value != val->value.m_longlong; + } int cmp(Item *arg) { const bool rc= value != arg->val_int(); @@ -1444,20 +1589,49 @@ public: If the left item is a constant one then its value is cached in the lval_cache variable. */ -class cmp_item_datetime : public cmp_item_scalar +class cmp_item_temporal: public cmp_item_scalar { +protected: longlong value; + void store_value_internal(Item *item, enum_field_types type); public: - /* Item used for issuing warnings. */ - Item *warn_item; /* Cache for the left item. */ Item *lval_cache; - cmp_item_datetime(Item *warn_item_arg) - : warn_item(warn_item_arg), lval_cache(0) {} - void store_value(Item *item); - int cmp(Item *arg); + cmp_item_temporal() + :lval_cache(0) {} int compare(cmp_item *ci); +}; + + +class cmp_item_datetime: public cmp_item_temporal +{ +public: + cmp_item_datetime() + :cmp_item_temporal() + { } + void store_value(Item *item) + { + store_value_internal(item, MYSQL_TYPE_DATETIME); + } + int cmp_not_null(const Value *val); + int cmp(Item *arg); + cmp_item *make_same(); +}; + + +class cmp_item_time: public cmp_item_temporal +{ +public: + cmp_item_time() + :cmp_item_temporal() + { } + void store_value(Item *item) + { + store_value_internal(item, MYSQL_TYPE_TIME); + } + int cmp_not_null(const Value *val); + int cmp(Item *arg); cmp_item *make_same(); }; @@ -1471,6 +1645,12 @@ public: value= item->val_real(); m_null_value= item->null_value; } + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_double()); + return value != val->value.m_double; + } int cmp(Item *arg) { const bool rc= value != arg->val_real(); @@ -1492,6 +1672,7 @@ public: cmp_item_decimal() {} /* Remove gcc warning */ void store_value(Item *item); int cmp(Item *arg); + int cmp_not_null(const Value *val); int compare(cmp_item *c); cmp_item *make_same(); }; @@ -1514,6 +1695,11 @@ public: value_res= item->val_str(&value); m_null_value= item->null_value; } + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(false); + return TRUE; + } int cmp(Item *item) { // Should never be called @@ -1532,71 +1718,509 @@ public: }; +/** + A helper class to handle situations when some item "pred" (the predicant) + is consequently compared to a list of other items value0..valueN (the values). + Currently used to handle: + - <in predicate> + pred IN (value0, value1, value2) + - <simple case> + CASE pred WHEN value0 .. WHEN value1 .. WHEN value2 .. END + + Every pair {pred,valueN} can be compared by its own Type_handler. + Some pairs can use the same Type_handler. + In cases when all pairs use exactly the same Type_handler, + we say "all types are compatible". + + For example, for an expression + 1 IN (1, 1e0, 1.0, 2) + - pred is 1 + - value0 is 1 + - value1 is 1e0 + - value2 is 1.1 + - value3 is 2 + + Pairs (pred,valueN) are compared as follows: + N expr1 Type + - ----- ---- + 0 1 INT + 1 1e0 DOUBLE + 2 1.0 DECIMAL + 3 2 INT + + Types are not compatible in this example. + + During add_value() calls, each pair {pred,valueN} is analysed: + - If valueN is an explicit NULL, it can be ignored in the caller asks to do so + - If valueN is not an explicit NULL (or if the caller didn't ask to skip + NULLs), then the value add an element in the array m_comparators[]. + + Every element m_comparators[] stores the following information: + 1. m_arg_index - the position of the value expression in the original + argument array, e.g. in Item_func_in::args[] or Item_func_case::args[]. + + 2. m_handler - the pointer to the data type handler that the owner + will use to compare the pair {args[m_predicate_index],args[m_arg_index]}. + + 3. m_handler_index - the index of an m_comparators[] element corresponding + to the leftmost pair that uses exactly the same Type_handler for + comparison. m_handler_index helps to maintain unique data type handlers. + - m_comparators[i].m_handler_index==i means that this is the + leftmost pair that uses the Type_handler m_handler for comparision. + - If m_comparators[i].m_handlex_index!=i, it means that some earlier + element m_comparators[j<i] is already using this Type_handler + pointed by m_handler. + + 4. m_cmp_item - the pointer to a cmp_item instance to handle comparison + for this pair. Only unique type handlers have m_cmp_item!=NULL. + Non-unique type handlers share the same cmp_item instance. + For all m_comparators[] elements the following assersion it true: + (m_handler_index==i) == (m_cmp_item!=NULL) +*/ +class Predicant_to_list_comparator +{ + // Allocate memory on thd memory root for "nvalues" values. + bool alloc_comparators(THD *thd, uint nvalues); + + /** + Look up m_comparators[] for a comparator using the given data type handler. + @param [OUT] idx - the index of the found comparator is returned here + @param [IN] handler - the data type handler to find + @param [IN] count - search in the range [0,count) only + @retval true - this type handler was not found + (*idx is not defined in this case). + @retval false - this type handler was found (the position of the + found handler is returned in idx). + */ + bool find_handler(uint *idx, const Type_handler *handler, uint count) + { + DBUG_ASSERT(count < m_comparator_count); + for (uint i= 0 ; i < count; i++) + { + if (m_comparators[i].m_handler == handler) + { + *idx= i; + return false; + } + } + return true; + } + + /** + Populate m_comparators[i].m_handler_index for all elements in + m_comparators using the information in m_comparators[i].m_handlers, + which was previously populated by a add_predicant() call and a number + of add_value() calls. + @param [OUT] compatible - If all comparator types are compatible, + their data type handler is returned here. + @param [OUT] unuque_cnt - The number of unique data type handlers found. + If the value returned in *unique_cnt is 0, + it means all values were explicit NULLs: + expr0 IN (NULL,NULL,..,NULL) + @param [OUT] found_type - The bit mask for all found cmp_type()'s. + */ + void detect_unique_handlers(Type_handler_hybrid_field_type *compatible, + uint *unique_cnt, uint *found_types); + /** + Creates a cmp_item instances for all unique handlers and stores + them into m_comparators[i].m_cmp_item, using the information previously + populated by add_predicant(), add_value(), detect_unque_handlers(). + */ + + /* + Compare the predicant to the value pointed by m_comparators[i]. + @param args - the same argument array which was previously used + with add_predicant() and add_value(). + @param i - which pair to check. + @retval true - the predicant is not equal to the value. + @retval false - the predicant is equal to the value. + @retval UNKNOWN - the result is uncertain yet because the predicant + and/or the value returned NULL, + more pairs {pred,valueN} should be checked. + */ + int cmp_arg(Item_args *args, uint i) + { + Predicant_to_value_comparator *cmp= + &m_comparators[m_comparators[i].m_handler_index]; + cmp_item *in_item= cmp->m_cmp_item; + DBUG_ASSERT(in_item); + /* + If this is the leftmost pair that uses the data type handler + pointed by m_comparators[i].m_handler, then we need to cache + the predicant value representation used by this handler. + */ + if (m_comparators[i].m_handler_index == i) + in_item->store_value(args->arguments()[m_predicant_index]); + /* + If the predicant item has null_value==true then: + - In case of scalar expression we can returns UNKNOWN immediately. + No needs to check the result of the value item. + - In case of ROW, null_value==true means that *some* row elements + returned NULL, but *some* elements can still be non-NULL! + We need to get the result of the value item and test + if non-NULL elements in the predicant and the value produce + TRUE (not equal), or UNKNOWN. + */ + if (args->arguments()[m_predicant_index]->null_value && + m_comparators[i].m_handler != &type_handler_row) + return UNKNOWN; + return in_item->cmp(args->arguments()[m_comparators[i].m_arg_index]); + } + int cmp_args_nulls_equal(Item_args *args, uint i) + { + Predicant_to_value_comparator *cmp= + &m_comparators[m_comparators[i].m_handler_index]; + cmp_item *in_item= cmp->m_cmp_item; + DBUG_ASSERT(in_item); + Item *predicant= args->arguments()[m_predicant_index]; + Item *arg= args->arguments()[m_comparators[i].m_arg_index]; + ValueBuffer<MAX_FIELD_WIDTH> val; + if (m_comparators[i].m_handler_index == i) + in_item->store_value(predicant); + m_comparators[i].m_handler->Item_save_in_value(arg, &val); + if (predicant->null_value && val.is_null()) + return FALSE; // Two nulls are equal + if (predicant->null_value || val.is_null()) + return UNKNOWN; + return in_item->cmp_not_null(&val); + } + /** + Predicant_to_value_comparator - a comparator for one pair (pred,valueN). + See comments above. + */ + struct Predicant_to_value_comparator + { + const Type_handler *m_handler; + cmp_item *m_cmp_item; + uint m_arg_index; + uint m_handler_index; + void cleanup() + { + if (m_cmp_item) + delete m_cmp_item; + memset(this, 0, sizeof(*this)); + } + }; + + Predicant_to_value_comparator *m_comparators; // The comparator array + uint m_comparator_count;// The number of elements in m_comparators[] + uint m_predicant_index; // The position of the predicant in its argument list, + // e.g. for Item_func_in m_predicant_index is 0, + // as predicant is stored in Item_func_in::args[0]. + // For Item_func_case m_predicant_index is + // set to Item_func_case::first_expr_num. + +public: + Predicant_to_list_comparator(THD *thd, uint nvalues) + :m_comparator_count(0), + m_predicant_index(0) + { + alloc_comparators(thd, nvalues); + } + + uint comparator_count() const { return m_comparator_count; } + const Type_handler *get_comparator_type_handler(uint i) const + { + DBUG_ASSERT(i < m_comparator_count); + return m_comparators[i].m_handler; + } + uint get_comparator_arg_index(uint i) const + { + DBUG_ASSERT(i < m_comparator_count); + return m_comparators[i].m_arg_index; + } + cmp_item *get_comparator_cmp_item(uint i) const + { + DBUG_ASSERT(i < m_comparator_count); + return m_comparators[i].m_cmp_item; + } + +#ifndef DBUG_OFF + void debug_print(THD *thd) + { + for (uint i= 0; i < m_comparator_count; i++) + { + DBUG_EXECUTE_IF("Predicant_to_list_comparator", + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_ERROR, "DBUG: [%d] arg=%d handler=%d (%s)", i, + m_comparators[i].m_arg_index, + m_comparators[i].m_handler_index, + m_comparators[m_comparators[i].m_handler_index]. + m_handler->name().ptr());); + } + } +#endif + + void add_predicant(Item_args *args, uint predicant_index) + { + DBUG_ASSERT(m_comparator_count == 0); // Set in constructor + DBUG_ASSERT(m_predicant_index == 0); // Set in constructor + DBUG_ASSERT(predicant_index < args->argument_count()); + m_predicant_index= predicant_index; + } + /** + Add a new element into m_comparators[], using a {pred,valueN} pair. + + @param funcname - the name of the operation, for error reporting + @param args - the owner function's argument list + @param value_index - the value position in args + @retval true - could not add an element because of non-comparable + arguments (e.g. ROWs with size) + @retval false - a new element was successfully added. + */ + bool add_value(const char *funcname, Item_args *args, uint value_index); + + /** + Add a new element into m_comparators[], ignoring explicit NULL values. + If the value appeared to be an explicit NULL, nulls_found[0] is set to true. + */ + bool add_value_skip_null(const char *funcname, + Item_args *args, uint value_index, + bool *nulls_found); + + /** + Signal "this" that there will be no new add_value*() calls, + so it can prepare its internal structures for comparison. + + @param [OUT] compatible - If all comparators are compatible, + their data type handler is returned here. + @param [OUT] unuque_cnt - The number of unique data type handlers found. + If the value returned in *unique_cnt is 0, + it means all values were explicit NULLs: + expr0 IN (NULL,NULL,..,NULL) + @param [OUT] found_type - The bit mask for all found cmp_type()'s. + */ + void all_values_added(Type_handler_hybrid_field_type *compatible, + uint *unique_cnt, uint *found_types) + { + detect_unique_handlers(compatible, unique_cnt, found_types); + } + /** + Creates cmp_item instances for all unique handlers and stores + them into m_comparators[].m_cmp_item, using the information previously + populated by add_predicant(), add_value() and detect_unque_handlers(). + */ + bool make_unique_cmp_items(THD *thd, CHARSET_INFO *cs); + void cleanup() + { + DBUG_ASSERT(m_comparators); + for (uint i= 0; i < m_comparator_count; i++) + m_comparators[i].cleanup(); + memset(m_comparators, 0, sizeof(m_comparators[0]) * m_comparator_count); + m_comparator_count= 0; + m_predicant_index= 0; + } + bool init_clone(THD *thd, uint nvalues) + { + m_comparator_count= 0; + m_predicant_index= 0; + return alloc_comparators(thd, nvalues); + } + /** + @param [IN] args - The argument list that was previously used with + add_predicant() and add_value(). + @param [OUT] idx - In case if a value that is equal to the predicant + was found, the index of the matching value is returned + here. Otherwise, *idx is not changed. + @param [IN/OUT] found_unknown_values - how to handle UNKNOWN results. + If found_unknown_values is NULL (e.g. Item_func_case), + cmp() returns immediately when the first UNKNOWN + result is found. + If found_unknown_values is non-NULL (Item_func_in), + cmp() does not return when an UNKNOWN result is found, + sets *found_unknown_values to true, and continues + to compare the remaining pairs to find FALSE + (i.e. the value that is equal to the predicant). + + @retval false - Found a value that is equal to the predicant + @retval true - Didn't find an equal value + */ + bool cmp(Item_args *args, uint *idx, bool *found_unknown_values) + { + for (uint i= 0 ; i < m_comparator_count ; i++) + { + DBUG_ASSERT(m_comparators[i].m_handler != NULL); + const int rc= cmp_arg(args, i); + if (rc == FALSE) + { + *idx= m_comparators[i].m_arg_index; + return false; // Found a matching value + } + if (rc == UNKNOWN) + { + if (!found_unknown_values) + return true; + *found_unknown_values= true; + } + } + return true; // Not found + } + /* + Same as above, but treats two NULLs as equal, e.g. as in DECODE_ORACLE(). + */ + bool cmp_nulls_equal(Item_args *args, uint *idx) + { + for (uint i= 0 ; i < m_comparator_count ; i++) + { + DBUG_ASSERT(m_comparators[i].m_handler != NULL); + if (cmp_args_nulls_equal(args, i) == FALSE) + { + *idx= m_comparators[i].m_arg_index; + return false; // Found a matching value + } + } + return true; // Not found + } +}; + + /* The class Item_func_case is the CASE ... WHEN ... THEN ... END function implementation. - - When there is no expression between CASE and the first WHEN - (the CASE expression) then this function simple checks all WHEN expressions - one after another. When some WHEN expression evaluated to TRUE then the - value of the corresponding THEN expression is returned. - - When the CASE expression is specified then it is compared to each WHEN - expression individually. When an equal WHEN expression is found - corresponding THEN expression is returned. - In order to do correct comparisons several comparators are used. One for - each result type. Different result types that are used in particular - CASE ... END expression are collected in the fix_length_and_dec() member - function and only comparators for there result types are used. */ -class Item_func_case :public Item_func_hybrid_field_type +class Item_func_case :public Item_func_case_expression { - int first_expr_num, else_expr_num; - enum Item_result left_cmp_type; +protected: String tmp_value; - uint ncases; - Item_result cmp_type; DTCollation cmp_collation; - cmp_item *cmp_items[6]; /* For all result types */ - cmp_item *case_item; Item **arg_buffer; - uint m_found_types; -public: - Item_func_case(THD *thd, List<Item> &list, Item *first_expr_arg, - Item *else_expr_arg); + bool aggregate_then_and_else_arguments(THD *thd, + Item **items, uint count, + Item **else_expr); + virtual Item **else_expr_addr() const= 0; + virtual Item *find_item()= 0; + void print_when_then_arguments(String *str, enum_query_type query_type, + Item **items, uint count); + void print_else_argument(String *str, enum_query_type query_type, Item *item); +public: + Item_func_case(THD *thd, List<Item> &list) + :Item_func_case_expression(thd, list) + { } double real_op(); longlong int_op(); String *str_op(String *); my_decimal *decimal_op(my_decimal *); bool date_op(MYSQL_TIME *ltime, uint fuzzydate); bool fix_fields(THD *thd, Item **ref); - void fix_length_and_dec(); - uint decimal_precision() const; table_map not_null_tables() const { return 0; } const char *func_name() const { return "case"; } enum precedence precedence() const { return BETWEEN_PRECEDENCE; } - virtual void print(String *str, enum_query_type query_type); - Item *find_item(String *str); CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } - void cleanup(); - Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_case>(thd, mem_root, this); } Item *build_clone(THD *thd, MEM_ROOT *mem_root) { Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd, mem_root); if (clone) - { - clone->case_item= 0; clone->arg_buffer= 0; - bzero(&clone->cmp_items, sizeof(cmp_items)); - } + return clone; + } +}; + + +/* + CASE WHEN cond THEN res [WHEN cond THEN res...] [ELSE res] END + + Searched CASE checks all WHEN expressions one after another. + When some WHEN expression evaluated to TRUE then the + value of the corresponding THEN expression is returned. +*/ +class Item_func_case_searched: public Item_func_case +{ + uint when_count() const { return arg_count / 2; } + bool with_else() const { return arg_count % 2; } + Item **else_expr_addr() const { return with_else() ? &args[arg_count - 1] : 0; } +public: + Item_func_case_searched(THD *thd, List<Item> &list) + :Item_func_case(thd, list) + { + DBUG_ASSERT(arg_count >= 2); + } + void print(String *str, enum_query_type query_type); + void fix_length_and_dec(); + Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) + { + // None of the arguments are in a comparison context + Item_args::propagate_equal_fields(thd, Context_identity(), cond); + return this; + } + Item *find_item(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_case_searched>(thd, mem_root, this); } +}; + + +/* + CASE pred WHEN value THEN res [WHEN value THEN res...] [ELSE res] END + + When the predicant expression is specified then it is compared to each WHEN + expression individually. When an equal WHEN expression is found + the corresponding THEN expression is returned. + In order to do correct comparisons several comparators are used. One for + each result type. Different result types that are used in particular + CASE ... END expression are collected in the fix_length_and_dec() member + function and only comparators for there result types are used. +*/ +class Item_func_case_simple: public Item_func_case, + public Predicant_to_list_comparator +{ +protected: + uint m_found_types; + uint when_count() const { return (arg_count - 1) / 2; } + bool with_else() const { return arg_count % 2 == 0; } + Item **else_expr_addr() const { return with_else() ? &args[arg_count - 1] : 0; } + bool aggregate_switch_and_when_arguments(THD *thd, bool nulls_equal); + bool prepare_predicant_and_values(THD *thd, uint *found_types, + bool nulls_equal); +public: + Item_func_case_simple(THD *thd, List<Item> &list) + :Item_func_case(thd, list), + Predicant_to_list_comparator(thd, arg_count), + m_found_types(0) + { + DBUG_ASSERT(arg_count >= 3); + } + void cleanup() + { + DBUG_ENTER("Item_func_case_simple::cleanup"); + Item_func::cleanup(); + Predicant_to_list_comparator::cleanup(); + DBUG_VOID_RETURN; + } + void print(String *str, enum_query_type query_type); + void fix_length_and_dec(); + Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); + Item *find_item(); + Item *build_clone(THD *thd, MEM_ROOT *mem_root) + { + Item_func_case_simple *clone= (Item_func_case_simple *) + Item_func_case::build_clone(thd, mem_root); + uint ncases= when_count(); + if (clone && clone->Predicant_to_list_comparator::init_clone(thd, ncases)) + return NULL; return clone; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_case_simple>(thd, mem_root, this); } }; + +class Item_func_decode_oracle: public Item_func_case_simple +{ +public: + Item_func_decode_oracle(THD *thd, List<Item> &list) + :Item_func_case_simple(thd, list) + { } + const char *func_name() const { return "decode_oracle"; } + void print(String *str, enum_query_type query_type) + { Item_func::print(str, query_type); } + void fix_length_and_dec(); + Item *find_item(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_decode_oracle>(thd, mem_root, this); } +}; + + /* The Item_func_in class implements in_expr IN (<in value list>) @@ -1605,14 +2229,27 @@ public: The current implementation distinguishes 2 cases: 1) all items in <in value list> are constants and have the same - result type. This case is handled by in_vector class. + result type. This case is handled by in_vector class, + implementing fast bisection search. 2) otherwise Item_func_in employs several cmp_item objects to perform comparisons of in_expr and an item from <in value list>. One cmp_item object for each result type. Different result types are collected in the fix_length_and_dec() member function by means of collect_cmp_types() function. + + Bisection is possible when: + 1. All types are similar + 2. All expressions in <in value list> are const + In the presence of NULLs, the correct result of evaluating this item + must be UNKNOWN or FALSE. To achieve that: + - If type is scalar, we can use bisection and the "have_null" boolean. + - If type is ROW, we will need to scan all of <in value list> when + searching, so bisection is impossible. Unless: + 3. UNKNOWN and FALSE are equivalent results + 4. Neither left expression nor <in value list> contain any NULL value */ -class Item_func_in :public Item_func_opt_neg +class Item_func_in :public Item_func_opt_neg, + public Predicant_to_list_comparator { /** Usable if <in value list> is made only of constants. Returns true if one @@ -1620,6 +2257,20 @@ class Item_func_in :public Item_func_opt_neg IN ( (-5, (12,NULL)), ... ). */ bool list_contains_null(); + bool all_items_are_consts(Item **items, uint nitems) const + { + for (uint i= 0; i < nitems; i++) + { + if (!items[i]->const_item()) + return false; + } + return true; + } + bool prepare_predicant_and_values(THD *thd, uint *found_types); + bool check_arguments() const + { + return check_argument_types_like_args0(); + } protected: SEL_TREE *get_func_mm_tree(RANGE_OPT_PARAM *param, Field *field, Item *value); @@ -1638,31 +2289,54 @@ public: and can be used safely as comparisons for key conditions */ bool arg_types_compatible; - Item_result left_cmp_type; - cmp_item *cmp_items[6]; /* One cmp_item for each result type */ Item_func_in(THD *thd, List<Item> &list): - Item_func_opt_neg(thd, list), array(0), have_null(0), + Item_func_opt_neg(thd, list), + Predicant_to_list_comparator(thd, arg_count - 1), + array(0), have_null(0), arg_types_compatible(FALSE) - { - bzero(&cmp_items, sizeof(cmp_items)); - allowed_arg_cols= 0; // Fetch this value from first argument - } + { } longlong val_int(); bool fix_fields(THD *, Item **); void fix_length_and_dec(); + bool compatible_types_scalar_bisection_possible() + { + DBUG_ASSERT(m_comparator.cmp_type() != ROW_RESULT); + return all_items_are_consts(args + 1, arg_count - 1); // Bisection #2 + } + bool compatible_types_row_bisection_possible() + { + DBUG_ASSERT(m_comparator.cmp_type() == ROW_RESULT); + return all_items_are_consts(args + 1, arg_count - 1) && // Bisection #2 + ((is_top_level_item() && !negated) || // Bisection #3 + (!list_contains_null() && !args[0]->maybe_null)); // Bisection #4 + } + bool agg_all_arg_charsets_for_comparison() + { + return agg_arg_charsets_for_comparison(cmp_collation, args, arg_count); + } + void fix_in_vector(); + bool value_list_convert_const_to_int(THD *thd); + bool fix_for_scalar_comparison_using_bisection(THD *thd) + { + array= m_comparator.type_handler()->make_in_vector(thd, this, arg_count - 1); + if (!array) // OOM + return true; + fix_in_vector(); + return false; + } + bool fix_for_scalar_comparison_using_cmp_items(THD *thd, uint found_types); + + bool fix_for_row_comparison_using_cmp_items(THD *thd); + bool fix_for_row_comparison_using_bisection(THD *thd); + void cleanup() { - uint i; DBUG_ENTER("Item_func_in::cleanup"); Item_int_func::cleanup(); delete array; array= 0; - for (i= 0; i <= (uint)TIME_RESULT; i++) - { - delete cmp_items[i]; - cmp_items[i]= 0; - } + Predicant_to_list_comparator::cleanup(); DBUG_VOID_RETURN; } void add_key_fields(JOIN *join, KEY_FIELD **key_fields, uint *and_level, @@ -1676,13 +2350,20 @@ public: will be replaced to a zero-filled Item_string. Such a change would require rebuilding of cmp_items. */ - Context cmpctx(ANY_SUBST, m_compare_type, - Item_func_in::compare_collation()); - for (uint i= 0; i < arg_count; i++) + if (arg_types_compatible) { - if (arg_types_compatible || i > 0) - args[i]->propagate_equal_fields_and_change_item_tree(thd, cmpctx, - cond, &args[i]); + Context cmpctx(ANY_SUBST, m_comparator.type_handler(), + Item_func_in::compare_collation()); + args[0]->propagate_equal_fields_and_change_item_tree(thd, cmpctx, + cond, &args[0]); + } + for (uint i= 0; i < comparator_count(); i++) + { + Context cmpctx(ANY_SUBST, get_comparator_type_handler(i), + Item_func_in::compare_collation()); + uint idx= get_comparator_arg_index(i); + args[idx]->propagate_equal_fields_and_change_item_tree(thd, cmpctx, + cond, &args[idx]); } return this; } @@ -1701,7 +2382,8 @@ public: if (clone) { clone->array= 0; - bzero(&clone->cmp_items, sizeof(cmp_items)); + if (clone->Predicant_to_list_comparator::init_clone(thd, arg_count - 1)) + return NULL; } return clone; } @@ -1715,12 +2397,18 @@ public: cmp_item_row(): comparators(0), n(0) {} ~cmp_item_row(); void store_value(Item *item); - inline void alloc_comparators(); + bool alloc_comparators(THD *thd, uint n); + bool prepare_comparators(THD *, Item **args, uint arg_count); int cmp(Item *arg); + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(false); + return TRUE; + } int compare(cmp_item *arg); cmp_item *make_same(); void store_value_by_template(THD *thd, cmp_item *tmpl, Item *); - friend void Item_func_in::fix_length_and_dec(); + friend class Item_func_in; cmp_item *get_comparator(uint i) { return comparators[i]; } }; @@ -1733,7 +2421,7 @@ public: ~in_row(); void set(uint pos,Item *item); uchar *get_value(Item *item); - friend void Item_func_in::fix_length_and_dec(); + friend class Item_func_in; Item_result result_type() { return ROW_RESULT; } cmp_item *get_cmp_item() { return &tmp; } }; @@ -1981,7 +2669,7 @@ public: if ((flags & MY_CS_NOPAD) && !(flags & MY_CS_NON1TO1)) Item_args::propagate_equal_fields(thd, Context(ANY_SUBST, - STRING_RESULT, + &type_handler_long_blob, compare_collation()), cond); return this; @@ -2135,12 +2823,24 @@ public: }; -class Item_func_regexp_instr :public Item_int_func +/* + In the corner case REGEXP_INSTR could return (2^32 + 1), + which would not fit into Item_long_func range. + But string lengths are limited with max_allowed_packet, + which cannot be bigger than 1024*1024*1024. +*/ +class Item_func_regexp_instr :public Item_long_func { + bool check_arguments() const + { + return args[0]->check_type_can_return_str(func_name()) || + args[1]->check_type_can_return_text(func_name()); + } Regexp_processor_pcre re; DTCollation cmp_collation; public: - Item_func_regexp_instr(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) + Item_func_regexp_instr(THD *thd, Item *a, Item *b) + :Item_long_func(thd, a, b) {} void cleanup() { @@ -2227,7 +2927,6 @@ public: Item *transform(THD *thd, Item_transformer transformer, uchar *arg); void traverse_cond(Cond_traverser, void *arg, traverse_order order); void neg_arguments(THD *thd); - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } Item* propagate_equal_fields(THD *, const Context &, COND_EQUAL *); Item *compile(THD *thd, Item_analyzer analyzer, uchar **arg_p, Item_transformer transformer, uchar *arg_t); @@ -2360,14 +3059,15 @@ class Item_equal: public Item_bool_func bool link_equal_fields; - Item_result m_compare_type; + const Type_handler *m_compare_handler; CHARSET_INFO *m_compare_collation; String cmp_value1, cmp_value2; public: COND_EQUAL *upper_levels; /* multiple equalities of upper and levels */ - Item_equal(THD *thd, Item *f1, Item *f2, bool with_const_item); + Item_equal(THD *thd, const Type_handler *handler, + Item *f1, Item *f2, bool with_const_item); Item_equal(THD *thd, Item_equal *item_equal); /* Currently the const item is always the first in the list of equal items */ inline Item* get_const() { return with_const ? equal_items.head() : NULL; } @@ -2400,7 +3100,7 @@ public: bool walk(Item_processor processor, bool walk_subquery, void *arg); Item *transform(THD *thd, Item_transformer transformer, uchar *arg); virtual void print(String *str, enum_query_type query_type); - Item_result compare_type() const { return m_compare_type; } + const Type_handler *compare_type_handler() const { return m_compare_handler; } CHARSET_INFO *compare_collation() const { return m_compare_collation; } void set_context_field(Item_field *ctx_field) { context_field= ctx_field; } @@ -2613,6 +3313,61 @@ public: { return get_item_copy<Item_func_dyncol_exists>(thd, mem_root, this); } }; + +class Item_func_cursor_bool_attr: public Item_bool_func, public Cursor_ref +{ +public: + Item_func_cursor_bool_attr(THD *thd, const LEX_CSTRING *name, uint offset) + :Item_bool_func(thd), Cursor_ref(name, offset) + { } + bool check_vcol_func_processor(void *arg) + { + return mark_unsupported_function(func_name(), arg, VCOL_SESSION_FUNC); + } + void print(String *str, enum_query_type query_type) + { + Cursor_ref::print_func(str, func_name()); + } +}; + + +class Item_func_cursor_isopen: public Item_func_cursor_bool_attr +{ +public: + Item_func_cursor_isopen(THD *thd, const LEX_CSTRING *name, uint offset) + :Item_func_cursor_bool_attr(thd, name, offset) { } + const char *func_name() const { return "%ISOPEN"; } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_cursor_isopen>(thd, mem_root, this); } +}; + + +class Item_func_cursor_found: public Item_func_cursor_bool_attr +{ +public: + Item_func_cursor_found(THD *thd, const LEX_CSTRING *name, uint offset) + :Item_func_cursor_bool_attr(thd, name, offset) { maybe_null= true; } + const char *func_name() const { return "%FOUND"; } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_cursor_found>(thd, mem_root, this); } +}; + + +class Item_func_cursor_notfound: public Item_func_cursor_bool_attr +{ +public: + Item_func_cursor_notfound(THD *thd, const LEX_CSTRING *name, uint offset) + :Item_func_cursor_bool_attr(thd, name, offset) { maybe_null= true; } + const char *func_name() const { return "%NOTFOUND"; } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_cursor_notfound>(thd, mem_root, this); } +}; + + + inline bool is_cond_or(Item *item) { if (item->type() != Item::COND_ITEM) diff --git a/sql/item_create.cc b/sql/item_create.cc index b6430ecf18d..fdfa95912ac 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -38,69 +38,6 @@ /* ============================================================================= - HELPER FUNCTIONS -============================================================================= -*/ - -static const char* item_name(Item *a, String *str) -{ - if (a->name) - return a->name; - str->length(0); - a->print(str, QT_ORDINARY); - return str->c_ptr_safe(); -} - - -static void wrong_precision_error(uint errcode, Item *a, - ulonglong number, uint maximum) -{ - char buff[1024]; - String buf(buff, sizeof(buff), system_charset_info); - - my_error(errcode, MYF(0), number, item_name(a, &buf), maximum); -} - - -/** - Get precision and scale for a declaration - - return - 0 ok - 1 error -*/ - -bool get_length_and_scale(ulonglong length, ulonglong decimals, - uint *out_length, uint *out_decimals, - uint max_precision, uint max_scale, - Item *a) -{ - if (length > (ulonglong) max_precision) - { - wrong_precision_error(ER_TOO_BIG_PRECISION, a, length, max_precision); - return 1; - } - if (decimals > (ulonglong) max_scale) - { - wrong_precision_error(ER_TOO_BIG_SCALE, a, decimals, max_scale); - return 1; - } - - *out_decimals= (uint) decimals; - my_decimal_trim(&length, out_decimals); - *out_length= (uint) length; - - - if (*out_length < *out_decimals) - { - my_error(ER_M_BIGGER_THAN_D, MYF(0), ""); - return 1; - } - return 0; -} - -/* -============================================================================= LOCAL DECLARATIONS ============================================================================= */ @@ -116,7 +53,8 @@ bool get_length_and_scale(ulonglong length, ulonglong decimals, class Create_native_func : public Create_func { public: - virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_func(THD *thd, LEX_CSTRING *name, + List<Item> *item_list); /** Builder method, with no arguments. @@ -125,7 +63,7 @@ public: @param item_list The function parameters, none of which are named @return An item representing the function call */ - virtual Item *create_native(THD *thd, LEX_STRING name, + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) = 0; protected: @@ -143,7 +81,8 @@ protected: class Create_func_arg0 : public Create_func { public: - virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_func(THD *thd, LEX_CSTRING *name, + List<Item> *item_list); /** Builder method, with no arguments. @@ -167,7 +106,7 @@ protected: class Create_func_arg1 : public Create_func { public: - virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list); /** Builder method, with one argument. @@ -192,7 +131,7 @@ protected: class Create_func_arg2 : public Create_func { public: - virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list); /** Builder method, with two arguments. @@ -218,7 +157,7 @@ protected: class Create_func_arg3 : public Create_func { public: - virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list); /** Builder method, with three arguments. @@ -245,7 +184,7 @@ protected: class Create_sp_func : public Create_qfunc { public: - virtual Item *create_with_db(THD *thd, LEX_STRING db, LEX_STRING name, + virtual Item *create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name, bool use_explicit_name, List<Item> *item_list); static Create_sp_func s_singleton; @@ -268,7 +207,7 @@ protected: class Create_func_no_geom : public Create_func { public: - virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list); /** Singleton. */ static Create_func_no_geom s_singleton; @@ -414,7 +353,7 @@ protected: class Create_func_atan : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_atan s_singleton; @@ -516,6 +455,19 @@ protected: }; +class Create_func_chr : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_chr s_singleton; + +protected: + Create_func_chr() {} + virtual ~Create_func_chr() {} +}; + + class Create_func_convexhull : public Create_func_arg1 { public: @@ -635,7 +587,7 @@ protected: class Create_func_concat : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_concat s_singleton; @@ -645,6 +597,19 @@ protected: }; +class Create_func_concat_operator_oracle : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); + + static Create_func_concat_operator_oracle s_singleton; + +protected: + Create_func_concat_operator_oracle() {} + virtual ~Create_func_concat_operator_oracle() {} +}; + + class Create_func_decode_histogram : public Create_func_arg2 { public: @@ -658,10 +623,23 @@ protected: }; +class Create_func_decode_oracle : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); + + static Create_func_decode_oracle s_singleton; + +protected: + Create_func_decode_oracle() {} + virtual ~Create_func_decode_oracle() {} +}; + + class Create_func_concat_ws : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_concat_ws s_singleton; @@ -712,6 +690,19 @@ protected: #endif +class Create_func_nvl2 : public Create_func_arg3 +{ +public: + virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3); + + static Create_func_nvl2 s_singleton; + +protected: + Create_func_nvl2() {} + virtual ~Create_func_nvl2() {} +}; + + class Create_func_conv : public Create_func_arg3 { public: @@ -792,19 +783,6 @@ protected: #endif -class Create_func_date_format : public Create_func_arg2 -{ -public: - virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); - - static Create_func_date_format s_singleton; - -protected: - Create_func_date_format() {} - virtual ~Create_func_date_format() {} -}; - - class Create_func_datediff : public Create_func_arg2 { public: @@ -870,19 +848,6 @@ protected: }; -class Create_func_decode : public Create_func_arg2 -{ -public: - virtual Item *create_2_arg(THD *thd, Item *arg1, Item *arg2); - - static Create_func_decode s_singleton; - -protected: - Create_func_decode() {} - virtual ~Create_func_decode() {} -}; - - class Create_func_degrees : public Create_func_arg1 { public: @@ -899,7 +864,7 @@ protected: class Create_func_des_decrypt : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_des_decrypt s_singleton; @@ -912,7 +877,7 @@ protected: class Create_func_des_encrypt : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_des_encrypt s_singleton; @@ -981,7 +946,7 @@ class Create_func_distance : public Create_func_arg2 class Create_func_elt : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_elt s_singleton; @@ -1007,7 +972,7 @@ protected: class Create_func_encrypt : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_encrypt s_singleton; @@ -1103,7 +1068,7 @@ protected: class Create_func_export_set : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_export_set s_singleton; @@ -1131,7 +1096,7 @@ protected: class Create_func_field : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_field s_singleton; @@ -1170,7 +1135,7 @@ protected: class Create_func_format : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_format s_singleton; @@ -1222,7 +1187,7 @@ protected: class Create_func_from_unixtime : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_from_unixtime s_singleton; @@ -1236,7 +1201,7 @@ protected: class Create_func_geometry_from_text : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_geometry_from_text s_singleton; @@ -1251,7 +1216,7 @@ protected: class Create_func_geometry_from_wkb : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_geometry_from_wkb s_singleton; @@ -1266,7 +1231,7 @@ protected: class Create_func_geometry_from_json : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_geometry_from_json s_singleton; @@ -1279,7 +1244,7 @@ protected: class Create_func_as_geojson : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_as_geojson s_singleton; @@ -1366,7 +1331,7 @@ protected: class Create_func_greatest : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_greatest s_singleton; @@ -1792,7 +1757,7 @@ protected: class Create_func_json_detailed: public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_detailed s_singleton; @@ -1857,7 +1822,7 @@ protected: class Create_func_json_keys: public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_keys s_singleton; @@ -1870,7 +1835,7 @@ protected: class Create_func_json_contains: public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_contains s_singleton; @@ -1883,7 +1848,7 @@ protected: class Create_func_json_contains_path : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_contains_path s_singleton; @@ -1896,7 +1861,7 @@ protected: class Create_func_json_extract : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_extract s_singleton; @@ -1909,7 +1874,7 @@ protected: class Create_func_json_search : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_search s_singleton; @@ -1922,7 +1887,7 @@ protected: class Create_func_json_array : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_array s_singleton; @@ -1935,7 +1900,7 @@ protected: class Create_func_json_array_append : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_array_append s_singleton; @@ -1948,7 +1913,7 @@ protected: class Create_func_json_array_insert : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_array_insert s_singleton; @@ -1961,7 +1926,7 @@ protected: class Create_func_json_insert : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_insert s_singleton; @@ -1974,7 +1939,7 @@ protected: class Create_func_json_set : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_set s_singleton; @@ -1987,7 +1952,7 @@ protected: class Create_func_json_replace : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_replace s_singleton; @@ -2000,7 +1965,7 @@ protected: class Create_func_json_remove : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_remove s_singleton; @@ -2013,7 +1978,7 @@ protected: class Create_func_json_object : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_object s_singleton; @@ -2026,7 +1991,7 @@ protected: class Create_func_json_length : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_length s_singleton; @@ -2039,7 +2004,7 @@ protected: class Create_func_json_merge : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_json_merge s_singleton; @@ -2091,7 +2056,7 @@ protected: class Create_func_last_insert_id : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_last_insert_id s_singleton; @@ -2117,7 +2082,7 @@ protected: class Create_func_least : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_least s_singleton; @@ -2139,6 +2104,18 @@ protected: virtual ~Create_func_length() {} }; +class Create_func_octet_length : public Create_func_arg1 +{ +public: + virtual Item *create_1_arg(THD *thd, Item *arg1); + + static Create_func_octet_length s_singleton; + +protected: + Create_func_octet_length() {} + virtual ~Create_func_octet_length() {} +}; + #ifndef DBUG_OFF class Create_func_like_range_min : public Create_func_arg2 @@ -2197,7 +2174,7 @@ protected: class Create_func_locate : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_locate s_singleton; @@ -2210,7 +2187,7 @@ protected: class Create_func_log : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_log s_singleton; @@ -2246,10 +2223,11 @@ protected: }; -class Create_func_lpad : public Create_func_arg3 +class Create_func_lpad : public Create_native_func { public: - virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list); static Create_func_lpad s_singleton; @@ -2301,7 +2279,7 @@ protected: class Create_func_make_set : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_make_set s_singleton; @@ -2314,7 +2292,7 @@ protected: class Create_func_master_pos_wait : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_master_pos_wait s_singleton; @@ -2327,7 +2305,7 @@ protected: class Create_func_master_gtid_wait : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_master_gtid_wait s_singleton; @@ -2623,7 +2601,7 @@ protected: class Create_func_rand : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_rand s_singleton; @@ -2646,6 +2624,19 @@ protected: }; +class Create_func_replace_oracle : public Create_func_arg3 +{ +public: + virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3); + + static Create_func_replace_oracle s_singleton; + +protected: + Create_func_replace_oracle() {} + virtual ~Create_func_replace_oracle() {} +}; + + class Create_func_reverse : public Create_func_arg1 { public: @@ -2662,7 +2653,7 @@ protected: class Create_func_round : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_round s_singleton; @@ -2672,10 +2663,11 @@ protected: }; -class Create_func_rpad : public Create_func_arg3 +class Create_func_rpad : public Create_native_func { public: - virtual Item *create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list); static Create_func_rpad s_singleton; @@ -3057,7 +3049,7 @@ protected: class Create_func_unix_timestamp : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_unix_timestamp s_singleton; @@ -3219,7 +3211,7 @@ protected: class Create_func_year_week : public Create_native_func { public: - virtual Item *create_native(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); static Create_func_year_week s_singleton; @@ -3263,7 +3255,7 @@ Create_func_no_geom Create_func_no_geom::s_singleton; Item* Create_func_no_geom::create_func(THD * /* unused */, - LEX_STRING /* unused */, + LEX_CSTRING /* unused */, List<Item> * /* unused */) { /* FIXME: error message can't be translated. */ @@ -3275,9 +3267,9 @@ Create_func_no_geom::create_func(THD * /* unused */, Item* -Create_qfunc::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) +Create_qfunc::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { - LEX_STRING db; + LEX_CSTRING db; if (! thd->db && ! thd->lex->sphead) { @@ -3293,14 +3285,14 @@ Create_qfunc::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) the case when a default database exist, see Create_sp_func::create(). */ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), - "FUNCTION", name.str); + "FUNCTION", name->str); return NULL; } if (thd->lex->copy_db_to(&db.str, &db.length)) return NULL; - return create_with_db(thd, db, name, false, item_list); + return create_with_db(thd, &db, name, false, item_list); } @@ -3308,9 +3300,9 @@ Create_qfunc::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) Create_udf_func Create_udf_func::s_singleton; Item* -Create_udf_func::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) +Create_udf_func::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { - udf_func *udf= find_udf(name.str, (uint)name.length); + udf_func *udf= find_udf(name->str, (uint) name->length); DBUG_ASSERT(udf); return create(thd, udf, item_list); } @@ -3418,7 +3410,7 @@ Create_udf_func::create(THD *thd, udf_func *udf, List<Item> *item_list) Create_sp_func Create_sp_func::s_singleton; Item* -Create_sp_func::create_with_db(THD *thd, LEX_STRING db, LEX_STRING name, +Create_sp_func::create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name, bool use_explicit_name, List<Item> *item_list) { int arg_count= 0; @@ -3437,7 +3429,7 @@ Create_sp_func::create_with_db(THD *thd, LEX_STRING db, LEX_STRING name, because it can refer to a User Defined Function call. For a Stored Function however, this has no semantic. */ - my_error(ER_WRONG_PARAMETERS_TO_STORED_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMETERS_TO_STORED_FCT, MYF(0), name->str); return NULL; } @@ -3445,8 +3437,7 @@ Create_sp_func::create_with_db(THD *thd, LEX_STRING db, LEX_STRING name, arg_count= item_list->elements; qname= new (thd->mem_root) sp_name(db, name, use_explicit_name); - qname->init_qname(thd); - sp_add_used_routine(lex, thd, qname, TYPE_ENUM_FUNCTION); + sp_handler_function.add_used_routine(lex, thd, qname); if (arg_count > 0) func= new (thd->mem_root) Item_func_sp(thd, lex->current_context(), qname, @@ -3460,11 +3451,11 @@ Create_sp_func::create_with_db(THD *thd, LEX_STRING db, LEX_STRING name, Item* -Create_native_func::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) +Create_native_func::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { if (has_named_parameters(item_list)) { - my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -3473,7 +3464,7 @@ Create_native_func::create_func(THD *thd, LEX_STRING name, List<Item> *item_list Item* -Create_func_arg0::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) +Create_func_arg0::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -3482,7 +3473,7 @@ Create_func_arg0::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) if (arg_count != 0) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -3491,7 +3482,7 @@ Create_func_arg0::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) Item* -Create_func_arg1::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) +Create_func_arg1::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -3500,7 +3491,7 @@ Create_func_arg1::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) if (arg_count != 1) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -3508,7 +3499,7 @@ Create_func_arg1::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) if (! param_1->is_autogenerated_name) { - my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -3517,7 +3508,7 @@ Create_func_arg1::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) Item* -Create_func_arg2::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) +Create_func_arg2::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -3526,7 +3517,7 @@ Create_func_arg2::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) if (arg_count != 2) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -3536,7 +3527,7 @@ Create_func_arg2::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) if ( (! param_1->is_autogenerated_name) || (! param_2->is_autogenerated_name)) { - my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -3545,7 +3536,7 @@ Create_func_arg2::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) Item* -Create_func_arg3::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) +Create_func_arg3::create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -3554,7 +3545,7 @@ Create_func_arg3::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) if (arg_count != 3) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -3566,7 +3557,7 @@ Create_func_arg3::create_func(THD *thd, LEX_STRING name, List<Item> *item_list) || (! param_2->is_autogenerated_name) || (! param_3->is_autogenerated_name)) { - my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -3664,7 +3655,7 @@ Create_func_asin::create_1_arg(THD *thd, Item *arg1) Create_func_atan Create_func_atan::s_singleton; Item* -Create_func_atan::create_native(THD *thd, LEX_STRING name, +Create_func_atan::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item* func= NULL; @@ -3689,7 +3680,7 @@ Create_func_atan::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -3773,6 +3764,16 @@ Create_func_centroid::create_1_arg(THD *thd, Item *arg1) } +Create_func_chr Create_func_chr::s_singleton; + +Item* +Create_func_chr::create_1_arg(THD *thd, Item *arg1) +{ + CHARSET_INFO *cs_db= thd->variables.collation_database; + return new (thd->mem_root) Item_func_chr(thd, arg1, cs_db); +} + + Create_func_convexhull Create_func_convexhull::s_singleton; Item* @@ -3845,7 +3846,7 @@ Create_func_dyncol_json::create_1_arg(THD *thd, Item *arg1) Create_func_concat Create_func_concat::s_singleton; Item* -Create_func_concat::create_native(THD *thd, LEX_STRING name, +Create_func_concat::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -3855,11 +3856,34 @@ Create_func_concat::create_native(THD *thd, LEX_STRING name, if (arg_count < 1) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + return NULL; + } + + return thd->variables.sql_mode & MODE_ORACLE ? + new (thd->mem_root) Item_func_concat_operator_oracle(thd, *item_list) : + new (thd->mem_root) Item_func_concat(thd, *item_list); +} + +Create_func_concat_operator_oracle + Create_func_concat_operator_oracle::s_singleton; + +Item* +Create_func_concat_operator_oracle::create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list) +{ + int arg_count= 0; + + if (item_list != NULL) + arg_count= item_list->elements; + + if (arg_count < 1) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } - return new (thd->mem_root) Item_func_concat(thd, *item_list); + return new (thd->mem_root) Item_func_concat_operator_oracle(thd, *item_list); } Create_func_decode_histogram Create_func_decode_histogram::s_singleton; @@ -3870,10 +3894,25 @@ Create_func_decode_histogram::create_2_arg(THD *thd, Item *arg1, Item *arg2) return new (thd->mem_root) Item_func_decode_histogram(thd, arg1, arg2); } +Create_func_decode_oracle Create_func_decode_oracle::s_singleton; + +Item* +Create_func_decode_oracle::create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list) +{ + uint arg_count= item_list ? item_list->elements : 0; + if (arg_count < 3) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + return NULL; + } + return new (thd->mem_root) Item_func_decode_oracle(thd, *item_list); +} + Create_func_concat_ws Create_func_concat_ws::s_singleton; Item* -Create_func_concat_ws::create_native(THD *thd, LEX_STRING name, +Create_func_concat_ws::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -3884,7 +3923,7 @@ Create_func_concat_ws::create_native(THD *thd, LEX_STRING name, /* "WS" stands for "With Separator": this function takes 2+ arguments */ if (arg_count < 2) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -3933,6 +3972,15 @@ Create_func_contains::create_2_arg(THD *thd, Item *arg1, Item *arg2) #endif +Create_func_nvl2 Create_func_nvl2::s_singleton; + +Item* +Create_func_nvl2::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) +{ + return new (thd->mem_root) Item_func_nvl2(thd, arg1, arg2, arg3); +} + + Create_func_conv Create_func_conv::s_singleton; Item* @@ -3990,15 +4038,6 @@ Create_func_crosses::create_2_arg(THD *thd, Item *arg1, Item *arg2) #endif -Create_func_date_format Create_func_date_format::s_singleton; - -Item* -Create_func_date_format::create_2_arg(THD *thd, Item *arg1, Item *arg2) -{ - return new (thd->mem_root) Item_func_date_format(thd, arg1, arg2, 0); -} - - Create_func_datediff Create_func_datediff::s_singleton; Item* @@ -4047,15 +4086,6 @@ Create_func_dayofyear::create_1_arg(THD *thd, Item *arg1) } -Create_func_decode Create_func_decode::s_singleton; - -Item* -Create_func_decode::create_2_arg(THD *thd, Item *arg1, Item *arg2) -{ - return new (thd->mem_root) Item_func_decode(thd, arg1, arg2); -} - - Create_func_degrees Create_func_degrees::s_singleton; Item* @@ -4069,7 +4099,7 @@ Create_func_degrees::create_1_arg(THD *thd, Item *arg1) Create_func_des_decrypt Create_func_des_decrypt::s_singleton; Item* -Create_func_des_decrypt::create_native(THD *thd, LEX_STRING name, +Create_func_des_decrypt::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4094,7 +4124,7 @@ Create_func_des_decrypt::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -4106,7 +4136,7 @@ Create_func_des_decrypt::create_native(THD *thd, LEX_STRING name, Create_func_des_encrypt Create_func_des_encrypt::s_singleton; Item* -Create_func_des_encrypt::create_native(THD *thd, LEX_STRING name, +Create_func_des_encrypt::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4131,7 +4161,7 @@ Create_func_des_encrypt::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -4185,7 +4215,7 @@ Create_func_distance::create_2_arg(THD *thd, Item *arg1, Item *arg2) Create_func_elt Create_func_elt::s_singleton; Item* -Create_func_elt::create_native(THD *thd, LEX_STRING name, +Create_func_elt::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -4195,7 +4225,7 @@ Create_func_elt::create_native(THD *thd, LEX_STRING name, if (arg_count < 2) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -4215,7 +4245,7 @@ Create_func_encode::create_2_arg(THD *thd, Item *arg1, Item *arg2) Create_func_encrypt Create_func_encrypt::s_singleton; Item* -Create_func_encrypt::create_native(THD *thd, LEX_STRING name, +Create_func_encrypt::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4241,7 +4271,7 @@ Create_func_encrypt::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -4316,7 +4346,7 @@ Create_func_exp::create_1_arg(THD *thd, Item *arg1) Create_func_export_set Create_func_export_set::s_singleton; Item* -Create_func_export_set::create_native(THD *thd, LEX_STRING name, +Create_func_export_set::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4357,7 +4387,7 @@ Create_func_export_set::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -4381,7 +4411,7 @@ Create_func_exteriorring::create_1_arg(THD *thd, Item *arg1) Create_func_field Create_func_field::s_singleton; Item* -Create_func_field::create_native(THD *thd, LEX_STRING name, +Create_func_field::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -4391,7 +4421,7 @@ Create_func_field::create_native(THD *thd, LEX_STRING name, if (arg_count < 2) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -4420,7 +4450,7 @@ Create_func_floor::create_1_arg(THD *thd, Item *arg1) Create_func_format Create_func_format::s_singleton; Item* -Create_func_format::create_native(THD *thd, LEX_STRING name, +Create_func_format::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4443,7 +4473,7 @@ Create_func_format::create_native(THD *thd, LEX_STRING name, break; } default: - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } @@ -4485,7 +4515,7 @@ Create_func_from_days::create_1_arg(THD *thd, Item *arg1) Create_func_from_unixtime Create_func_from_unixtime::s_singleton; Item* -Create_func_from_unixtime::create_native(THD *thd, LEX_STRING name, +Create_func_from_unixtime::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4506,12 +4536,12 @@ Create_func_from_unixtime::create_native(THD *thd, LEX_STRING name, Item *param_1= item_list->pop(); Item *param_2= item_list->pop(); Item *ut= new (thd->mem_root) Item_func_from_unixtime(thd, param_1); - func= new (thd->mem_root) Item_func_date_format(thd, ut, param_2, 0); + func= new (thd->mem_root) Item_func_date_format(thd, ut, param_2); break; } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -4524,7 +4554,7 @@ Create_func_from_unixtime::create_native(THD *thd, LEX_STRING name, Create_func_geometry_from_text Create_func_geometry_from_text::s_singleton; Item* -Create_func_geometry_from_text::create_native(THD *thd, LEX_STRING name, +Create_func_geometry_from_text::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4550,7 +4580,7 @@ Create_func_geometry_from_text::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -4564,7 +4594,7 @@ Create_func_geometry_from_text::create_native(THD *thd, LEX_STRING name, Create_func_geometry_from_wkb Create_func_geometry_from_wkb::s_singleton; Item* -Create_func_geometry_from_wkb::create_native(THD *thd, LEX_STRING name, +Create_func_geometry_from_wkb::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4590,7 +4620,7 @@ Create_func_geometry_from_wkb::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -4604,7 +4634,7 @@ Create_func_geometry_from_wkb::create_native(THD *thd, LEX_STRING name, Create_func_geometry_from_json Create_func_geometry_from_json::s_singleton; Item* -Create_func_geometry_from_json::create_native(THD *thd, LEX_STRING name, +Create_func_geometry_from_json::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4639,7 +4669,7 @@ Create_func_geometry_from_json::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -4651,7 +4681,7 @@ Create_func_geometry_from_json::create_native(THD *thd, LEX_STRING name, Create_func_as_geojson Create_func_as_geojson::s_singleton; Item* -Create_func_as_geojson::create_native(THD *thd, LEX_STRING name, +Create_func_as_geojson::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -4685,7 +4715,7 @@ Create_func_as_geojson::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -4754,7 +4784,7 @@ Create_func_glength::create_1_arg(THD *thd, Item *arg1) Create_func_greatest Create_func_greatest::s_singleton; Item* -Create_func_greatest::create_native(THD *thd, LEX_STRING name, +Create_func_greatest::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -4764,7 +4794,7 @@ Create_func_greatest::create_native(THD *thd, LEX_STRING name, if (arg_count < 2) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -5046,7 +5076,7 @@ Create_func_json_exists::create_2_arg(THD *thd, Item *arg1, Item *arg2) Create_func_json_detailed Create_func_json_detailed::s_singleton; Item* -Create_func_json_detailed::create_native(THD *thd, LEX_STRING name, +Create_func_json_detailed::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5057,7 +5087,7 @@ Create_func_json_detailed::create_native(THD *thd, LEX_STRING name, if (arg_count < 1 || arg_count > 2 /* json_doc, [path]...*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5163,7 +5193,7 @@ Create_func_last_day::create_1_arg(THD *thd, Item *arg1) Create_func_json_array Create_func_json_array::s_singleton; Item* -Create_func_json_array::create_native(THD *thd, LEX_STRING name, +Create_func_json_array::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func; @@ -5184,7 +5214,7 @@ Create_func_json_array::create_native(THD *thd, LEX_STRING name, Create_func_json_array_append Create_func_json_array_append::s_singleton; Item* -Create_func_json_array_append::create_native(THD *thd, LEX_STRING name, +Create_func_json_array_append::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5195,7 +5225,7 @@ Create_func_json_array_append::create_native(THD *thd, LEX_STRING name, if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5209,7 +5239,7 @@ Create_func_json_array_append::create_native(THD *thd, LEX_STRING name, Create_func_json_array_insert Create_func_json_array_insert::s_singleton; Item* -Create_func_json_array_insert::create_native(THD *thd, LEX_STRING name, +Create_func_json_array_insert::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5220,7 +5250,7 @@ Create_func_json_array_insert::create_native(THD *thd, LEX_STRING name, if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5234,7 +5264,7 @@ Create_func_json_array_insert::create_native(THD *thd, LEX_STRING name, Create_func_json_insert Create_func_json_insert::s_singleton; Item* -Create_func_json_insert::create_native(THD *thd, LEX_STRING name, +Create_func_json_insert::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5245,7 +5275,7 @@ Create_func_json_insert::create_native(THD *thd, LEX_STRING name, if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5260,7 +5290,7 @@ Create_func_json_insert::create_native(THD *thd, LEX_STRING name, Create_func_json_set Create_func_json_set::s_singleton; Item* -Create_func_json_set::create_native(THD *thd, LEX_STRING name, +Create_func_json_set::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5271,7 +5301,7 @@ Create_func_json_set::create_native(THD *thd, LEX_STRING name, if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5286,7 +5316,7 @@ Create_func_json_set::create_native(THD *thd, LEX_STRING name, Create_func_json_replace Create_func_json_replace::s_singleton; Item* -Create_func_json_replace::create_native(THD *thd, LEX_STRING name, +Create_func_json_replace::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5297,7 +5327,7 @@ Create_func_json_replace::create_native(THD *thd, LEX_STRING name, if (arg_count < 3 || (arg_count & 1) == 0 /*is even*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5312,7 +5342,7 @@ Create_func_json_replace::create_native(THD *thd, LEX_STRING name, Create_func_json_remove Create_func_json_remove::s_singleton; Item* -Create_func_json_remove::create_native(THD *thd, LEX_STRING name, +Create_func_json_remove::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5323,7 +5353,7 @@ Create_func_json_remove::create_native(THD *thd, LEX_STRING name, if (arg_count < 2 /*json_doc, path [,path]*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5337,7 +5367,7 @@ Create_func_json_remove::create_native(THD *thd, LEX_STRING name, Create_func_json_object Create_func_json_object::s_singleton; Item* -Create_func_json_object::create_native(THD *thd, LEX_STRING name, +Create_func_json_object::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func; @@ -5348,7 +5378,7 @@ Create_func_json_object::create_native(THD *thd, LEX_STRING name, arg_count= item_list->elements; if ((arg_count & 1) != 0 /*is odd*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); func= NULL; } else @@ -5369,7 +5399,7 @@ Create_func_json_object::create_native(THD *thd, LEX_STRING name, Create_func_json_length Create_func_json_length::s_singleton; Item* -Create_func_json_length::create_native(THD *thd, LEX_STRING name, +Create_func_json_length::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func; @@ -5378,7 +5408,7 @@ Create_func_json_length::create_native(THD *thd, LEX_STRING name, if (item_list == NULL || (arg_count= item_list->elements) == 0) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); func= NULL; } else @@ -5393,7 +5423,7 @@ Create_func_json_length::create_native(THD *thd, LEX_STRING name, Create_func_json_merge Create_func_json_merge::s_singleton; Item* -Create_func_json_merge::create_native(THD *thd, LEX_STRING name, +Create_func_json_merge::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func; @@ -5402,7 +5432,7 @@ Create_func_json_merge::create_native(THD *thd, LEX_STRING name, if (item_list == NULL || (arg_count= item_list->elements) < 2) // json, json { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); func= NULL; } else @@ -5417,7 +5447,7 @@ Create_func_json_merge::create_native(THD *thd, LEX_STRING name, Create_func_json_contains Create_func_json_contains::s_singleton; Item* -Create_func_json_contains::create_native(THD *thd, LEX_STRING name, +Create_func_json_contains::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5432,7 +5462,7 @@ Create_func_json_contains::create_native(THD *thd, LEX_STRING name, } else { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } return func; @@ -5442,7 +5472,7 @@ Create_func_json_contains::create_native(THD *thd, LEX_STRING name, Create_func_json_keys Create_func_json_keys::s_singleton; Item* -Create_func_json_keys::create_native(THD *thd, LEX_STRING name, +Create_func_json_keys::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5453,7 +5483,7 @@ Create_func_json_keys::create_native(THD *thd, LEX_STRING name, if (arg_count < 1 || arg_count > 2 /* json_doc, [path]...*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5467,7 +5497,7 @@ Create_func_json_keys::create_native(THD *thd, LEX_STRING name, Create_func_json_contains_path Create_func_json_contains_path::s_singleton; Item* -Create_func_json_contains_path::create_native(THD *thd, LEX_STRING name, +Create_func_json_contains_path::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5478,7 +5508,7 @@ Create_func_json_contains_path::create_native(THD *thd, LEX_STRING name, if (arg_count < 3 /* json_doc, one_or_all, path, [path]...*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5492,7 +5522,7 @@ Create_func_json_contains_path::create_native(THD *thd, LEX_STRING name, Create_func_json_extract Create_func_json_extract::s_singleton; Item* -Create_func_json_extract::create_native(THD *thd, LEX_STRING name, +Create_func_json_extract::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5503,7 +5533,7 @@ Create_func_json_extract::create_native(THD *thd, LEX_STRING name, if (arg_count < 2 /* json_doc, path, [path]...*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5517,7 +5547,7 @@ Create_func_json_extract::create_native(THD *thd, LEX_STRING name, Create_func_json_search Create_func_json_search::s_singleton; Item* -Create_func_json_search::create_native(THD *thd, LEX_STRING name, +Create_func_json_search::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5528,7 +5558,7 @@ Create_func_json_search::create_native(THD *thd, LEX_STRING name, if (arg_count < 3 /* json_doc, one_or_all, search_str, [escape_char[, path]...*/) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); } else { @@ -5542,7 +5572,7 @@ Create_func_json_search::create_native(THD *thd, LEX_STRING name, Create_func_last_insert_id Create_func_last_insert_id::s_singleton; Item* -Create_func_last_insert_id::create_native(THD *thd, LEX_STRING name, +Create_func_last_insert_id::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5567,7 +5597,7 @@ Create_func_last_insert_id::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -5588,7 +5618,7 @@ Create_func_lcase::create_1_arg(THD *thd, Item *arg1) Create_func_least Create_func_least::s_singleton; Item* -Create_func_least::create_native(THD *thd, LEX_STRING name, +Create_func_least::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -5598,7 +5628,7 @@ Create_func_least::create_native(THD *thd, LEX_STRING name, if (arg_count < 2) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -5611,7 +5641,18 @@ Create_func_length Create_func_length::s_singleton; Item* Create_func_length::create_1_arg(THD *thd, Item *arg1) { - return new (thd->mem_root) Item_func_length(thd, arg1); + if (thd->variables.sql_mode & MODE_ORACLE) + return new (thd->mem_root) Item_func_char_length(thd, arg1); + else + return new (thd->mem_root) Item_func_octet_length(thd, arg1); +} + +Create_func_octet_length Create_func_octet_length::s_singleton; + +Item* +Create_func_octet_length::create_1_arg(THD *thd, Item *arg1) +{ + return new (thd->mem_root) Item_func_octet_length(thd, arg1); } @@ -5659,7 +5700,7 @@ Create_func_load_file::create_1_arg(THD *thd, Item *arg1) Create_func_locate Create_func_locate::s_singleton; Item* -Create_func_locate::create_native(THD *thd, LEX_STRING name, +Create_func_locate::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5688,7 +5729,7 @@ Create_func_locate::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -5700,7 +5741,7 @@ Create_func_locate::create_native(THD *thd, LEX_STRING name, Create_func_log Create_func_log::s_singleton; Item* -Create_func_log::create_native(THD *thd, LEX_STRING name, +Create_func_log::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5725,7 +5766,7 @@ Create_func_log::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -5755,9 +5796,34 @@ Create_func_log2::create_1_arg(THD *thd, Item *arg1) Create_func_lpad Create_func_lpad::s_singleton; Item* -Create_func_lpad::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) +Create_func_lpad::create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list) { - return new (thd->mem_root) Item_func_lpad(thd, arg1, arg2, arg3); + Item *func= NULL; + int arg_count= item_list ? item_list->elements : 0; + + switch (arg_count) { + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_lpad(thd, param_1, param_2); + break; + } + case 3: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *param_3= item_list->pop(); + func= new (thd->mem_root) Item_func_lpad(thd, param_1, param_2, param_3); + break; + } + default: + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + break; + } + + return func; } @@ -5791,7 +5857,7 @@ Create_func_maketime::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) Create_func_make_set Create_func_make_set::s_singleton; Item* -Create_func_make_set::create_native(THD *thd, LEX_STRING name, +Create_func_make_set::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { int arg_count= 0; @@ -5801,7 +5867,7 @@ Create_func_make_set::create_native(THD *thd, LEX_STRING name, if (arg_count < 2) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return NULL; } @@ -5812,7 +5878,7 @@ Create_func_make_set::create_native(THD *thd, LEX_STRING name, Create_func_master_pos_wait Create_func_master_pos_wait::s_singleton; Item* -Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name, +Create_func_master_pos_wait::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { @@ -5826,7 +5892,7 @@ Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name, if (arg_count < 2 || arg_count > 4) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return func; } @@ -5863,7 +5929,7 @@ Create_func_master_pos_wait::create_native(THD *thd, LEX_STRING name, Create_func_master_gtid_wait Create_func_master_gtid_wait::s_singleton; Item* -Create_func_master_gtid_wait::create_native(THD *thd, LEX_STRING name, +Create_func_master_gtid_wait::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -5876,7 +5942,7 @@ Create_func_master_gtid_wait::create_native(THD *thd, LEX_STRING name, if (arg_count < 1 || arg_count > 2) { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); return func; } @@ -6109,7 +6175,7 @@ Create_func_radians::create_1_arg(THD *thd, Item *arg1) Create_func_rand Create_func_rand::s_singleton; Item* -Create_func_rand::create_native(THD *thd, LEX_STRING name, +Create_func_rand::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -6147,7 +6213,7 @@ Create_func_rand::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -6167,6 +6233,16 @@ Create_func_release_lock::create_1_arg(THD *thd, Item *arg1) } +Create_func_replace_oracle Create_func_replace_oracle::s_singleton; + +Item* +Create_func_replace_oracle::create_3_arg(THD *thd, Item *arg1, Item *arg2, + Item *arg3) +{ + return new (thd->mem_root) Item_func_replace_oracle(thd, arg1, arg2, arg3); +} + + Create_func_reverse Create_func_reverse::s_singleton; Item* @@ -6179,7 +6255,7 @@ Create_func_reverse::create_1_arg(THD *thd, Item *arg1) Create_func_round Create_func_round::s_singleton; Item* -Create_func_round::create_native(THD *thd, LEX_STRING name, +Create_func_round::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -6205,7 +6281,7 @@ Create_func_round::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -6217,9 +6293,34 @@ Create_func_round::create_native(THD *thd, LEX_STRING name, Create_func_rpad Create_func_rpad::s_singleton; Item* -Create_func_rpad::create_3_arg(THD *thd, Item *arg1, Item *arg2, Item *arg3) +Create_func_rpad::create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list) { - return new (thd->mem_root) Item_func_rpad(thd, arg1, arg2, arg3); + Item *func= NULL; + int arg_count= item_list ? item_list->elements : 0; + + switch (arg_count) { + case 2: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + func= new (thd->mem_root) Item_func_rpad(thd, param_1, param_2); + break; + } + case 3: + { + Item *param_1= item_list->pop(); + Item *param_2= item_list->pop(); + Item *param_3= item_list->pop(); + func= new (thd->mem_root) Item_func_rpad(thd, param_1, param_2, param_3); + break; + } + default: + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + break; + } + + return func; } @@ -6388,7 +6489,7 @@ Create_func_time_format Create_func_time_format::s_singleton; Item* Create_func_time_format::create_2_arg(THD *thd, Item *arg1, Item *arg2) { - return new (thd->mem_root) Item_func_date_format(thd, arg1, arg2, 1); + return new (thd->mem_root) Item_func_time_format(thd, arg1, arg2); } @@ -6488,7 +6589,7 @@ Create_func_unhex::create_1_arg(THD *thd, Item *arg1) Create_func_unix_timestamp Create_func_unix_timestamp::s_singleton; Item* -Create_func_unix_timestamp::create_native(THD *thd, LEX_STRING name, +Create_func_unix_timestamp::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -6512,7 +6613,7 @@ Create_func_unix_timestamp::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -6643,7 +6744,7 @@ Create_func_y::create_1_arg(THD *thd, Item *arg1) Create_func_year_week Create_func_year_week::s_singleton; Item* -Create_func_year_week::create_native(THD *thd, LEX_STRING name, +Create_func_year_week::create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list) { Item *func= NULL; @@ -6669,7 +6770,7 @@ Create_func_year_week::create_native(THD *thd, LEX_STRING name, } default: { - my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name.str); + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); break; } } @@ -6680,7 +6781,7 @@ Create_func_year_week::create_native(THD *thd, LEX_STRING name, struct Native_func_registry { - LEX_STRING name; + LEX_CSTRING name; Create_func *builder; }; @@ -6730,6 +6831,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("CENTROID") }, GEOM_BUILDER(Create_func_centroid)}, { { C_STRING_WITH_LEN("CHARACTER_LENGTH") }, BUILDER(Create_func_char_length)}, { { C_STRING_WITH_LEN("CHAR_LENGTH") }, BUILDER(Create_func_char_length)}, + { { C_STRING_WITH_LEN("CHR") }, BUILDER(Create_func_chr)}, { { C_STRING_WITH_LEN("COERCIBILITY") }, BUILDER(Create_func_coercibility)}, { { C_STRING_WITH_LEN("COLUMN_CHECK") }, BUILDER(Create_func_dyncol_check)}, { { C_STRING_WITH_LEN("COLUMN_EXISTS") }, BUILDER(Create_func_dyncol_exists)}, @@ -6737,6 +6839,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("COLUMN_JSON") }, BUILDER(Create_func_dyncol_json)}, { { C_STRING_WITH_LEN("COMPRESS") }, BUILDER(Create_func_compress)}, { { C_STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat)}, + { { C_STRING_WITH_LEN("CONCAT_OPERATOR_ORACLE") }, BUILDER(Create_func_concat_operator_oracle)}, { { C_STRING_WITH_LEN("CONCAT_WS") }, BUILDER(Create_func_concat_ws)}, { { C_STRING_WITH_LEN("CONNECTION_ID") }, BUILDER(Create_func_connection_id)}, { { C_STRING_WITH_LEN("CONV") }, BUILDER(Create_func_conv)}, @@ -6747,14 +6850,13 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("CRC32") }, BUILDER(Create_func_crc32)}, { { C_STRING_WITH_LEN("CROSSES") }, GEOM_BUILDER(Create_func_crosses)}, { { C_STRING_WITH_LEN("DATEDIFF") }, BUILDER(Create_func_datediff)}, - { { C_STRING_WITH_LEN("DATE_FORMAT") }, BUILDER(Create_func_date_format)}, { { C_STRING_WITH_LEN("DAYNAME") }, BUILDER(Create_func_dayname)}, { { C_STRING_WITH_LEN("DAYOFMONTH") }, BUILDER(Create_func_dayofmonth)}, { { C_STRING_WITH_LEN("DAYOFWEEK") }, BUILDER(Create_func_dayofweek)}, { { C_STRING_WITH_LEN("DAYOFYEAR") }, BUILDER(Create_func_dayofyear)}, - { { C_STRING_WITH_LEN("DECODE") }, BUILDER(Create_func_decode)}, { { C_STRING_WITH_LEN("DEGREES") }, BUILDER(Create_func_degrees)}, { { C_STRING_WITH_LEN("DECODE_HISTOGRAM") }, BUILDER(Create_func_decode_histogram)}, + { { C_STRING_WITH_LEN("DECODE_ORACLE") }, BUILDER(Create_func_decode_oracle)}, { { C_STRING_WITH_LEN("DES_DECRYPT") }, BUILDER(Create_func_des_decrypt)}, { { C_STRING_WITH_LEN("DES_ENCRYPT") }, BUILDER(Create_func_des_encrypt)}, { { C_STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)}, @@ -6841,6 +6943,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("LCASE") }, BUILDER(Create_func_lcase)}, { { C_STRING_WITH_LEN("LEAST") }, BUILDER(Create_func_least)}, { { C_STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_length)}, + { { C_STRING_WITH_LEN("LENGTHB") }, BUILDER(Create_func_octet_length)}, #ifndef DBUG_OFF { { C_STRING_WITH_LEN("LIKE_RANGE_MIN") }, BUILDER(Create_func_like_range_min)}, { { C_STRING_WITH_LEN("LIKE_RANGE_MAX") }, BUILDER(Create_func_like_range_max)}, @@ -6886,12 +6989,14 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("MULTIPOLYGONFROMTEXT") }, GEOM_BUILDER(Create_func_geometry_from_text)}, { { C_STRING_WITH_LEN("MULTIPOLYGONFROMWKB") }, GEOM_BUILDER(Create_func_geometry_from_wkb)}, { { C_STRING_WITH_LEN("NAME_CONST") }, BUILDER(Create_func_name_const)}, + { { C_STRING_WITH_LEN("NVL") }, BUILDER(Create_func_ifnull)}, + { { C_STRING_WITH_LEN("NVL2") }, BUILDER(Create_func_nvl2)}, { { C_STRING_WITH_LEN("NULLIF") }, BUILDER(Create_func_nullif)}, { { C_STRING_WITH_LEN("NUMGEOMETRIES") }, GEOM_BUILDER(Create_func_numgeometries)}, { { C_STRING_WITH_LEN("NUMINTERIORRINGS") }, GEOM_BUILDER(Create_func_numinteriorring)}, { { C_STRING_WITH_LEN("NUMPOINTS") }, GEOM_BUILDER(Create_func_numpoints)}, { { C_STRING_WITH_LEN("OCT") }, BUILDER(Create_func_oct)}, - { { C_STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_length)}, + { { C_STRING_WITH_LEN("OCTET_LENGTH") }, BUILDER(Create_func_octet_length)}, { { C_STRING_WITH_LEN("ORD") }, BUILDER(Create_func_ord)}, { { C_STRING_WITH_LEN("OVERLAPS") }, GEOM_BUILDER(Create_func_mbr_overlaps)}, { { C_STRING_WITH_LEN("PERIOD_ADD") }, BUILDER(Create_func_period_add)}, @@ -6914,6 +7019,8 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("RADIANS") }, BUILDER(Create_func_radians)}, { { C_STRING_WITH_LEN("RAND") }, BUILDER(Create_func_rand)}, { { C_STRING_WITH_LEN("RELEASE_LOCK") }, BUILDER(Create_func_release_lock)}, + { { C_STRING_WITH_LEN("REPLACE_ORACLE") }, + BUILDER(Create_func_replace_oracle)}, { { C_STRING_WITH_LEN("REVERSE") }, BUILDER(Create_func_reverse)}, { { C_STRING_WITH_LEN("ROUND") }, BUILDER(Create_func_round)}, { { C_STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad)}, @@ -7107,15 +7214,15 @@ void item_create_cleanup() } Create_func * -find_native_function_builder(THD *thd, LEX_STRING name) +find_native_function_builder(THD *thd, const LEX_CSTRING *name) { Native_func_registry *func; Create_func *builder= NULL; /* Thread safe */ - func= (Native_func_registry*) my_hash_search(& native_functions_hash, - (uchar*) name.str, - name.length); + func= (Native_func_registry*) my_hash_search(&native_functions_hash, + (uchar*) name->str, + name->length); if (func) { @@ -7132,112 +7239,6 @@ find_qualified_function_builder(THD *thd) } -Item * -create_func_cast(THD *thd, Item *a, Cast_target cast_type, - const char *c_len, const char *c_dec, - CHARSET_INFO *cs) -{ - Item *UNINIT_VAR(res); - ulonglong length= 0, decimals= 0; - int error; - - /* - We don't have to check for error here as sql_yacc.yy has guaranteed - that the values are in range of ulonglong - */ - if (c_len) - length= (ulonglong) my_strtoll10(c_len, NULL, &error); - if (c_dec) - decimals= (ulonglong) my_strtoll10(c_dec, NULL, &error); - - switch (cast_type) { - case ITEM_CAST_BINARY: - res= new (thd->mem_root) Item_func_binary(thd, a); - break; - case ITEM_CAST_SIGNED_INT: - res= new (thd->mem_root) Item_func_signed(thd, a); - break; - case ITEM_CAST_UNSIGNED_INT: - res= new (thd->mem_root) Item_func_unsigned(thd, a); - break; - case ITEM_CAST_DATE: - res= new (thd->mem_root) Item_date_typecast(thd, a); - break; - case ITEM_CAST_TIME: - if (decimals > MAX_DATETIME_PRECISION) - { - wrong_precision_error(ER_TOO_BIG_PRECISION, a, decimals, - MAX_DATETIME_PRECISION); - return 0; - } - res= new (thd->mem_root) Item_time_typecast(thd, a, (uint) decimals); - break; - case ITEM_CAST_DATETIME: - if (decimals > MAX_DATETIME_PRECISION) - { - wrong_precision_error(ER_TOO_BIG_PRECISION, a, decimals, - MAX_DATETIME_PRECISION); - return 0; - } - res= new (thd->mem_root) Item_datetime_typecast(thd, a, (uint) decimals); - break; - case ITEM_CAST_DECIMAL: - { - uint len; - uint dec; - if (get_length_and_scale(length, decimals, &len, &dec, - DECIMAL_MAX_PRECISION, DECIMAL_MAX_SCALE, - a)) - return NULL; - res= new (thd->mem_root) Item_decimal_typecast(thd, a, len, dec); - break; - } - case ITEM_CAST_DOUBLE: - { - uint len, dec; - if (!c_len) - { - length= DBL_DIG+7; - decimals= NOT_FIXED_DEC; - } - else if (get_length_and_scale(length, decimals, &len, &dec, - DECIMAL_MAX_PRECISION, NOT_FIXED_DEC-1, - a)) - return NULL; - res= new (thd->mem_root) Item_double_typecast(thd, a, (uint) length, - (uint) decimals); - break; - } - case ITEM_CAST_CHAR: - { - int len= -1; - CHARSET_INFO *real_cs= (cs ? cs : thd->variables.collation_connection); - if (c_len) - { - if (length > MAX_FIELD_BLOBLENGTH) - { - char buff[1024]; - String buf(buff, sizeof(buff), system_charset_info); - my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), item_name(a, &buf), - MAX_FIELD_BLOBLENGTH); - return NULL; - } - len= (int) length; - } - res= new (thd->mem_root) Item_char_typecast(thd, a, len, real_cs); - break; - } - default: - { - DBUG_ASSERT(0); - res= 0; - break; - } - } - return res; -} - - static bool have_important_literal_warnings(const MYSQL_TIME_STATUS *status) { @@ -7395,7 +7396,7 @@ Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums) Item *create_func_dyncol_get(THD *thd, Item *str, Item *num, - Cast_target cast_type, + const Type_handler *handler, const char *c_len, const char *c_dec, CHARSET_INFO *cs) { @@ -7403,5 +7404,6 @@ Item *create_func_dyncol_get(THD *thd, Item *str, Item *num, if (!(res= new (thd->mem_root) Item_dyncol_get(thd, str, num))) return res; // Return NULL - return create_func_cast(thd, res, cast_type, c_len, c_dec, cs); + return handler->create_typecast_item(thd, res, + Type_cast_attributes(c_len, c_dec, cs)); } diff --git a/sql/item_create.h b/sql/item_create.h index 05fe48f656a..128a19a1c15 100644 --- a/sql/item_create.h +++ b/sql/item_create.h @@ -56,7 +56,7 @@ public: @param item_list The list of arguments to the function, can be NULL @return An item representing the parsed function call, or NULL */ - virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list) = 0; + virtual Item *create_func(THD *thd, LEX_CSTRING *name, List<Item> *item_list) = 0; protected: /** Constructor */ @@ -83,7 +83,8 @@ public: @param item_list The list of arguments to the function, can be NULL @return An item representing the parsed function call */ - virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_func(THD *thd, LEX_CSTRING *name, + List<Item> *item_list); /** The builder create method, for qualified functions. @@ -94,7 +95,7 @@ public: @param item_list The list of arguments to the function, can be NULL @return An item representing the parsed function call */ - virtual Item *create_with_db(THD *thd, LEX_STRING db, LEX_STRING name, + virtual Item *create_with_db(THD *thd, LEX_CSTRING *db, LEX_CSTRING *name, bool use_explicit_name, List<Item> *item_list) = 0; @@ -112,7 +113,8 @@ protected: @param name The native function name @return The native function builder associated with the name, or NULL */ -extern Create_func * find_native_function_builder(THD *thd, LEX_STRING name); +extern Create_func *find_native_function_builder(THD *thd, + const LEX_CSTRING *name); /** @@ -131,7 +133,8 @@ extern Create_qfunc * find_qualified_function_builder(THD *thd); class Create_udf_func : public Create_func { public: - virtual Item *create_func(THD *thd, LEX_STRING name, List<Item> *item_list); + virtual Item *create_func(THD *thd, LEX_CSTRING *name, + List<Item> *item_list); /** The builder create method, for User Defined Functions. @@ -154,20 +157,6 @@ protected: #endif -/** - Builder for cast expressions. - @param thd The current thread - @param a The item to cast - @param cast_type the type casted into - @param len TODO - @param dec TODO - @param cs The character set -*/ -Item * -create_func_cast(THD *thd, Item *a, Cast_target cast_type, - const char *len, const char *dec, - CHARSET_INFO *cs); - Item *create_temporal_literal(THD *thd, const char *str, uint length, CHARSET_INFO *cs, @@ -191,7 +180,7 @@ Item *create_func_dyncol_add(THD *thd, Item *str, List<DYNCALL_CREATE_DEF> &list); Item *create_func_dyncol_delete(THD *thd, Item *str, List<Item> &nums); Item *create_func_dyncol_get(THD *thd, Item *num, Item *str, - Cast_target cast_type, + const Type_handler *handler, const char *c_len, const char *c_dec, CHARSET_INFO *cs); Item *create_func_dyncol_json(THD *thd, Item *str); diff --git a/sql/item_func.cc b/sql/item_func.cc index f14222f4451..fc7417411e8 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -53,12 +53,13 @@ #include "sp.h" #include "set_var.h" #include "debug_sync.h" +#include "sql_base.h" #ifdef NO_EMBEDDED_ACCESS_CHECKS #define sp_restore_security_context(A,B) while (0) {} #endif -bool check_reserved_words(LEX_STRING *name) +bool check_reserved_words(const LEX_CSTRING *name) { if (!my_strcasecmp(system_charset_info, name->str, "GLOBAL") || !my_strcasecmp(system_charset_info, name->str, "LOCAL") || @@ -89,23 +90,35 @@ static inline bool test_if_sum_overflows_ull(ulonglong arg1, ulonglong arg2) } -void Item_args::set_arguments(THD *thd, List<Item> &list) +/** + Allocate memory for arguments using tmp_args or thd->alloc(). + @retval false - success + @retval true - error (arg_count is set to 0 for conveniece) +*/ +bool Item_args::alloc_arguments(THD *thd, uint count) { - arg_count= list.elements; - if (arg_count <= 2) + if (count <= 2) { args= tmp_arg; + return false; } - else if (!(args= (Item**) thd->alloc(sizeof(Item*) * arg_count))) + if ((args= (Item**) thd->alloc(sizeof(Item*) * count)) == NULL) { arg_count= 0; - return; + return true; } - uint i= 0; + return false; +} + + +void Item_args::set_arguments(THD *thd, List<Item> &list) +{ + if (alloc_arguments(thd, list.elements)) + return; List_iterator_fast<Item> li(list); Item *item; - while ((item= li++)) - args[i++]= item; + for (arg_count= 0; (item= li++); ) + args[arg_count++]= item; } @@ -138,6 +151,137 @@ void Item_func::sync_with_sum_func_and_with_field(List<Item> &list) } +bool Item_func::check_argument_types_like_args0() const +{ + uint cols; + if (arg_count == 0) + return false; + cols= args[0]->cols(); + for (uint i= 1; i < arg_count; i++) + { + if (args[i]->check_cols(cols)) + return true; + } + return false; +} + + +bool Item_func::check_argument_types_or_binary(const Type_handler *handler, + uint start, uint end) const +{ + for (uint i= start; i < end ; i++) + { + DBUG_ASSERT(i < arg_count); + if (args[i]->check_type_or_binary(func_name(), handler)) + return true; + } + return false; +} + + +bool Item_func::check_argument_types_traditional_scalar(uint start, + uint end) const +{ + for (uint i= start; i < end ; i++) + { + DBUG_ASSERT(i < arg_count); + if (args[i]->check_type_traditional_scalar(func_name())) + return true; + } + return false; +} + + +bool Item_func::check_argument_types_can_return_int(uint start, + uint end) const +{ + for (uint i= start; i < end ; i++) + { + DBUG_ASSERT(i < arg_count); + if (args[i]->check_type_can_return_int(func_name())) + return true; + } + return false; +} + + +bool Item_func::check_argument_types_can_return_real(uint start, + uint end) const +{ + for (uint i= start; i < end ; i++) + { + DBUG_ASSERT(i < arg_count); + if (args[i]->check_type_can_return_real(func_name())) + return true; + } + return false; +} + + +bool Item_func::check_argument_types_can_return_text(uint start, + uint end) const +{ + for (uint i= start; i < end ; i++) + { + DBUG_ASSERT(i < arg_count); + if (args[i]->check_type_can_return_text(func_name())) + return true; + } + return false; +} + + +bool Item_func::check_argument_types_can_return_str(uint start, + uint end) const +{ + for (uint i= start; i < end ; i++) + { + DBUG_ASSERT(i < arg_count); + if (args[i]->check_type_can_return_str(func_name())) + return true; + } + return false; +} + + +bool Item_func::check_argument_types_can_return_date(uint start, + uint end) const +{ + for (uint i= start; i < end ; i++) + { + DBUG_ASSERT(i < arg_count); + if (args[i]->check_type_can_return_date(func_name())) + return true; + } + return false; +} + + +bool Item_func::check_argument_types_can_return_time(uint start, + uint end) const +{ + for (uint i= start; i < end ; i++) + { + DBUG_ASSERT(i < arg_count); + if (args[i]->check_type_can_return_time(func_name())) + return true; + } + return false; +} + + +bool Item_func::check_argument_types_scalar(uint start, uint end) const +{ + for (uint i= start; i < end; i++) + { + DBUG_ASSERT(i < arg_count); + if (args[i]->check_type_scalar(func_name())) + return true; + } + return false; +} + + /* Resolve references to table column for a function and its argument @@ -211,19 +355,6 @@ Item_func::fix_fields(THD *thd, Item **ref) return TRUE; /* purecov: inspected */ item= *arg; - if (allowed_arg_cols) - { - if (item->check_cols(allowed_arg_cols)) - return 1; - } - else - { - /* we have to fetch allowed_arg_cols from first argument */ - DBUG_ASSERT(arg == args); // it is first argument - allowed_arg_cols= item->cols(); - DBUG_ASSERT(allowed_arg_cols); // Can't be 0 any more - } - if (item->maybe_null) maybe_null=1; @@ -234,6 +365,8 @@ Item_func::fix_fields(THD *thd, Item **ref) with_subselect|= item->has_subquery(); } } + if (check_arguments()) + return true; fix_length_and_dec(); if (thd->is_error()) // An error inside fix_length_and_dec occurred return TRUE; @@ -529,6 +662,17 @@ my_decimal *Item_func::val_decimal(my_decimal *decimal_value) } +bool Item_hybrid_func::fix_attributes(Item **items, uint nitems) +{ + bool rc= Item_hybrid_func::type_handler()-> + Item_hybrid_func_fix_attributes(current_thd, + func_name(), this, this, + items, nitems); + DBUG_ASSERT(!rc || current_thd->is_error()); + return rc; +} + + String *Item_real_func::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -569,128 +713,6 @@ void Item_udf_func::fix_num_length_and_dec() } -/** - Count max_length and decimals for temporal functions. - - @param item Argument array - @param nitems Number of arguments in the array. - - @retval False on success, true on error. -*/ -void Item_func::count_datetime_length(enum_field_types field_type_arg, - Item **item, uint nitems) -{ - unsigned_flag= 0; - decimals= 0; - if (field_type_arg != MYSQL_TYPE_DATE) - { - for (uint i= 0; i < nitems; i++) - set_if_bigger(decimals, item[i]->decimals); - } - set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); - uint len= decimals ? (decimals + 1) : 0; - len+= mysql_temporal_int_part_length(field_type_arg); - fix_char_length(len); -} - -/** - Set max_length/decimals of function if function is fixed point and - result length/precision depends on argument ones. -*/ - -void Item_func::count_decimal_length(Item **item, uint nitems) -{ - int max_int_part= 0; - decimals= 0; - unsigned_flag= 1; - for (uint i=0 ; i < nitems ; i++) - { - set_if_bigger(decimals, item[i]->decimals); - set_if_bigger(max_int_part, item[i]->decimal_int_part()); - set_if_smaller(unsigned_flag, item[i]->unsigned_flag); - } - int precision= MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION); - fix_char_length(my_decimal_precision_to_length_no_truncation(precision, - decimals, - unsigned_flag)); -} - - -/** - Set max_length of if it is maximum length of its arguments. -*/ - -void Item_func::count_only_length(Item **item, uint nitems) -{ - uint32 char_length= 0; - unsigned_flag= 0; - for (uint i= 0; i < nitems ; i++) - { - set_if_bigger(char_length, item[i]->max_char_length()); - set_if_bigger(unsigned_flag, item[i]->unsigned_flag); - } - fix_char_length(char_length); -} - - -/** - Set max_length/decimals of function if function is floating point and - result length/precision depends on argument ones. -*/ - -void Item_func::count_real_length(Item **items, uint nitems) -{ - uint32 length= 0; - decimals= 0; - max_length= 0; - unsigned_flag= false; - for (uint i=0 ; i < nitems ; i++) - { - if (decimals < FLOATING_POINT_DECIMALS) - { - set_if_bigger(decimals, items[i]->decimals); - /* Will be ignored if items[i]->decimals >= FLOATING_POINT_DECIMALS */ - set_if_bigger(length, (items[i]->max_length - items[i]->decimals)); - } - set_if_bigger(max_length, items[i]->max_length); - } - if (decimals < FLOATING_POINT_DECIMALS) - { - max_length= length; - length+= decimals; - if (length < max_length) // If previous operation gave overflow - max_length= UINT_MAX32; - else - max_length= length; - } -} - - -/** - Calculate max_length and decimals for STRING_RESULT functions. - - @param field_type Field type. - @param items Argument array. - @param nitems Number of arguments. - - @retval False on success, true on error. -*/ -bool Item_func::count_string_result_length(enum_field_types field_type_arg, - Item **items, uint nitems) -{ - if (agg_arg_charsets_for_string_result(collation, items, nitems, 1)) - return true; - if (is_temporal_type(field_type_arg)) - count_datetime_length(field_type_arg, items, nitems); - else - { - count_only_length(items, nitems); - decimals= max_length ? NOT_FIXED_DEC : 0; - } - return false; -} - - void Item_func::signal_divide_by_null() { THD *thd= current_thd; @@ -729,7 +751,7 @@ String *Item_int_func::val_str(String *str) void Item_func_connection_id::fix_length_and_dec() { - Item_int_func::fix_length_and_dec(); + Item_long_func::fix_length_and_dec(); max_length= 10; } @@ -744,404 +766,243 @@ bool Item_func_connection_id::fix_fields(THD *thd, Item **ref) } -/** - Check arguments here to determine result's type for a numeric - function of two arguments. -*/ - -void Item_num_op::fix_length_and_dec(void) +bool Item_num_op::fix_type_handler(const Type_aggregator *aggregator) { - DBUG_ENTER("Item_num_op::fix_length_and_dec"); - DBUG_PRINT("info", ("name %s", func_name())); DBUG_ASSERT(arg_count == 2); - Item_result r0= args[0]->cast_to_int_type(); - Item_result r1= args[1]->cast_to_int_type(); - - if (r0 == REAL_RESULT || r1 == REAL_RESULT || - r0 == STRING_RESULT || r1 ==STRING_RESULT) - { - count_real_length(args, arg_count); - max_length= float_length(decimals); - set_handler_by_result_type(REAL_RESULT); - } - else if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT || - r0 == TIME_RESULT || r1 == TIME_RESULT) - { - set_handler_by_result_type(DECIMAL_RESULT); - result_precision(); - fix_decimals(); - if ((r0 == TIME_RESULT || r1 == TIME_RESULT) && decimals == 0) - set_handler_by_result_type(INT_RESULT); - } - else - { - DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT); - set_handler_by_result_type(INT_RESULT); - result_precision(); - decimals= 0; - } - DBUG_PRINT("info", ("Type: %s", - (result_type() == REAL_RESULT ? "REAL_RESULT" : - result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" : - result_type() == INT_RESULT ? "INT_RESULT" : - "--ILLEGAL!!!--"))); - DBUG_VOID_RETURN; + const Type_handler *h0= args[0]->cast_to_int_type_handler(); + const Type_handler *h1= args[1]->cast_to_int_type_handler(); + if (!aggregate_for_num_op(aggregator, h0, h1)) + return false; + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + h0->name().ptr(), h1->name().ptr(), func_name()); + return true; } -/** - Set result type for a numeric function of one argument - (can be also used by a numeric function of many arguments, if the result - type depends only on the first argument) -*/ - -void Item_func_num1::fix_length_and_dec() +void Item_func_plus::fix_length_and_dec(void) { - DBUG_ENTER("Item_func_num1::fix_length_and_dec"); + DBUG_ENTER("Item_func_plus::fix_length_and_dec"); DBUG_PRINT("info", ("name %s", func_name())); - // Note, cast_to_int_type() can return TIME_RESULT - switch (args[0]->cast_to_int_type()) { - case INT_RESULT: - set_handler_by_result_type(INT_RESULT); - max_length= args[0]->max_length; - unsigned_flag= args[0]->unsigned_flag; - break; - case STRING_RESULT: - case REAL_RESULT: - set_handler_by_result_type(REAL_RESULT); - decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC - max_length= float_length(decimals); - break; - case TIME_RESULT: - case DECIMAL_RESULT: - set_handler_by_result_type(DECIMAL_RESULT); - decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC - max_length= args[0]->max_length; - break; - case ROW_RESULT: - DBUG_ASSERT(0); + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_plus; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_for_result;); + DBUG_ASSERT(aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) + { + Item_func_plus::type_handler()->Item_func_plus_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); } - DBUG_PRINT("info", ("Type: %s", - (result_type() == REAL_RESULT ? "REAL_RESULT" : - result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" : - result_type() == INT_RESULT ? "INT_RESULT" : - "--ILLEGAL!!!--"))); DBUG_VOID_RETURN; } -String *Item_func_hybrid_field_type::val_str(String *str) +String *Item_func_hybrid_field_type::val_str_from_decimal_op(String *str) { - DBUG_ASSERT(fixed == 1); - switch (Item_func_hybrid_field_type::cmp_type()) { - case DECIMAL_RESULT: - { - my_decimal decimal_value, *val; - if (!(val= decimal_op_with_null_check(&decimal_value))) - return 0; // null is set - my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val); - str->set_charset(collation.collation); - my_decimal2string(E_DEC_FATAL_ERROR, val, 0, 0, 0, str); - break; - } - case INT_RESULT: - { - longlong nr= int_op(); - if (null_value) - return 0; /* purecov: inspected */ - str->set_int(nr, unsigned_flag, collation.collation); - break; - } - case REAL_RESULT: - { - double nr= real_op(); - if (null_value) - return 0; /* purecov: inspected */ - str->set_real(nr, decimals, collation.collation); - break; - } - case TIME_RESULT: - { - MYSQL_TIME ltime; - if (date_op_with_null_check(<ime) || - (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH))) - return (String *) 0; - ltime.time_type= mysql_type_to_time_type(field_type()); - str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); - str->set_charset(&my_charset_bin); - DBUG_ASSERT(!null_value); - return str; - } - case STRING_RESULT: - return str_op_with_null_check(&str_value); - case ROW_RESULT: - DBUG_ASSERT(0); - } - DBUG_ASSERT(!null_value || (str == NULL)); + my_decimal decimal_value, *val; + if (!(val= decimal_op_with_null_check(&decimal_value))) + return 0; // null is set + DBUG_ASSERT(!null_value); + my_decimal_round(E_DEC_FATAL_ERROR, val, decimals, FALSE, val); + str->set_charset(collation.collation); + my_decimal2string(E_DEC_FATAL_ERROR, val, 0, 0, 0, str); return str; } - -double Item_func_hybrid_field_type::val_real() +double Item_func_hybrid_field_type::val_real_from_decimal_op() { - DBUG_ASSERT(fixed == 1); - switch (Item_func_hybrid_field_type::cmp_type()) { - case DECIMAL_RESULT: - { - my_decimal decimal_value, *val; - double result; - if (!(val= decimal_op_with_null_check(&decimal_value))) - return 0.0; // null is set - my_decimal2double(E_DEC_FATAL_ERROR, val, &result); - return result; - } - case INT_RESULT: - { - longlong result= int_op(); - return unsigned_flag ? (double) ((ulonglong) result) : (double) result; - } - case REAL_RESULT: - return real_op(); - case TIME_RESULT: - { - MYSQL_TIME ltime; - if (date_op_with_null_check(<ime)) - return 0; - ltime.time_type= mysql_type_to_time_type(field_type()); - return TIME_to_double(<ime); - } - case STRING_RESULT: - { - String *res= str_op_with_null_check(&str_value); - return res ? double_from_string_with_check(res) : 0.0; - } - case ROW_RESULT: - DBUG_ASSERT(0); - } - return 0.0; + my_decimal decimal_value, *val; + if (!(val= decimal_op_with_null_check(&decimal_value))) + return 0.0; // null is set + double result; + my_decimal2double(E_DEC_FATAL_ERROR, val, &result); + return result; } +longlong Item_func_hybrid_field_type::val_int_from_decimal_op() +{ + my_decimal decimal_value, *val; + if (!(val= decimal_op_with_null_check(&decimal_value))) + return 0; // null is set + longlong result; + my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result); + return result; +} -longlong Item_func_hybrid_field_type::val_int() +bool Item_func_hybrid_field_type::get_date_from_decimal_op(MYSQL_TIME *ltime, + ulonglong fuzzydate) { - DBUG_ASSERT(fixed == 1); - switch (Item_func_hybrid_field_type::cmp_type()) { - case DECIMAL_RESULT: - { - my_decimal decimal_value, *val; - if (!(val= decimal_op_with_null_check(&decimal_value))) - return 0; // null is set - longlong result; - my_decimal2int(E_DEC_FATAL_ERROR, val, unsigned_flag, &result); - return result; - } - case INT_RESULT: - return int_op(); - case REAL_RESULT: - return (longlong) rint(real_op()); - case TIME_RESULT: - { - MYSQL_TIME ltime; - if (date_op_with_null_check(<ime)) - return 0; - ltime.time_type= mysql_type_to_time_type(field_type()); - return TIME_to_ulonglong(<ime); - } - case STRING_RESULT: - { - String *res= str_op_with_null_check(&str_value); - return res ? longlong_from_string_with_check(res) : 0; - } - case ROW_RESULT: - DBUG_ASSERT(0); - } - return 0; + my_decimal value, *res; + if (!(res= decimal_op_with_null_check(&value)) || + decimal_to_datetime_with_warn(res, ltime, fuzzydate, + field_name_or_null())) + return make_zero_mysql_time(ltime, fuzzydate); + return (null_value= 0); } -my_decimal *Item_func_hybrid_field_type::val_decimal(my_decimal *decimal_value) +String *Item_func_hybrid_field_type::val_str_from_int_op(String *str) { - my_decimal *val= decimal_value; - DBUG_ASSERT(fixed == 1); - switch (Item_func_hybrid_field_type::cmp_type()) { - case DECIMAL_RESULT: - val= decimal_op_with_null_check(decimal_value); - break; - case INT_RESULT: - { - longlong result= int_op(); - if (null_value) - return NULL; - int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, decimal_value); - break; - } - case REAL_RESULT: - { - double result= (double)real_op(); - if (null_value) - return NULL; - double2my_decimal(E_DEC_FATAL_ERROR, result, decimal_value); - break; - } - case TIME_RESULT: - { - MYSQL_TIME ltime; - if (date_op_with_null_check(<ime)) - { - my_decimal_set_zero(decimal_value); - return 0; - } - ltime.time_type= mysql_type_to_time_type(field_type()); - return date2my_decimal(<ime, decimal_value); - } - case STRING_RESULT: - { - String *res= str_op_with_null_check(&str_value); - return res ? decimal_from_string_with_check(decimal_value, res) : 0; - } - case ROW_RESULT: - DBUG_ASSERT(0); - } - return val; + longlong nr= int_op(); + if (null_value) + return 0; /* purecov: inspected */ + str->set_int(nr, unsigned_flag, collation.collation); + return str; } +double Item_func_hybrid_field_type::val_real_from_int_op() +{ + longlong result= int_op(); + return unsigned_flag ? (double) ((ulonglong) result) : (double) result; +} -bool Item_func_hybrid_field_type::get_date(MYSQL_TIME *ltime, - ulonglong fuzzydate) +my_decimal * +Item_func_hybrid_field_type::val_decimal_from_int_op(my_decimal *dec) { - DBUG_ASSERT(fixed == 1); - switch (Item_func_hybrid_field_type::cmp_type()) { - case DECIMAL_RESULT: - { - my_decimal value, *res; - if (!(res= decimal_op_with_null_check(&value)) || - decimal_to_datetime_with_warn(res, ltime, fuzzydate, - field_name_or_null())) - goto err; - break; - } - case INT_RESULT: - { - longlong value= int_op(); - bool neg= !unsigned_flag && value < 0; - if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value, - ltime, fuzzydate, - field_name_or_null())) - goto err; - break; - } - case REAL_RESULT: - { - double value= real_op(); - if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, - field_name_or_null())) - goto err; - break; - } - case TIME_RESULT: - return date_op(ltime, - (uint)(fuzzydate | - (field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0))); - case STRING_RESULT: - { - char buff[40]; - String tmp(buff,sizeof(buff), &my_charset_bin),*res; - if (!(res= str_op_with_null_check(&tmp)) || - str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), - ltime, fuzzydate)) - goto err; - break; - } - case ROW_RESULT: - DBUG_ASSERT(0); - } + longlong result= int_op(); + if (null_value) + return NULL; + int2my_decimal(E_DEC_FATAL_ERROR, result, unsigned_flag, dec); + return dec; +} +bool Item_func_hybrid_field_type::get_date_from_int_op(MYSQL_TIME *ltime, + ulonglong fuzzydate) +{ + longlong value= int_op(); + bool neg= !unsigned_flag && value < 0; + if (null_value || int_to_datetime_with_warn(neg, neg ? -value : value, + ltime, fuzzydate, + field_name_or_null())) + return make_zero_mysql_time(ltime, fuzzydate); return (null_value= 0); +} -err: - bzero(ltime, sizeof(*ltime)); - return null_value|= !(fuzzydate & TIME_FUZZY_DATES); + +String *Item_func_hybrid_field_type::val_str_from_real_op(String *str) +{ + double nr= real_op(); + if (null_value) + return 0; /* purecov: inspected */ + str->set_real(nr, decimals, collation.collation); + return str; } +longlong Item_func_hybrid_field_type::val_int_from_real_op() +{ + return (longlong) rint(real_op()); +} -void Item_func_signed::print(String *str, enum_query_type query_type) +my_decimal * +Item_func_hybrid_field_type::val_decimal_from_real_op(my_decimal *dec) { - str->append(STRING_WITH_LEN("cast(")); - args[0]->print(str, query_type); - str->append(STRING_WITH_LEN(" as signed)")); + double result= (double) real_op(); + if (null_value) + return NULL; + double2my_decimal(E_DEC_FATAL_ERROR, result, dec); + return dec; +} +bool Item_func_hybrid_field_type::get_date_from_real_op(MYSQL_TIME *ltime, + ulonglong fuzzydate) +{ + double value= real_op(); + if (null_value || double_to_datetime_with_warn(value, ltime, fuzzydate, + field_name_or_null())) + return make_zero_mysql_time(ltime, fuzzydate); + return (null_value= 0); } -longlong Item::val_int_from_str(int *error) +String *Item_func_hybrid_field_type::val_str_from_date_op(String *str) { - char buff[MAX_FIELD_WIDTH]; - String tmp(buff,sizeof(buff), &my_charset_bin), *res; + MYSQL_TIME ltime; + if (date_op_with_null_check(<ime) || + (null_value= str->alloc(MAX_DATE_STRING_REP_LENGTH))) + return (String *) 0; + ltime.time_type= mysql_timestamp_type(); + str->length(my_TIME_to_str(<ime, const_cast<char*>(str->ptr()), decimals)); + str->set_charset(&my_charset_bin); + DBUG_ASSERT(!null_value); + return str; +} - /* - For a string result, we must first get the string and then convert it - to a longlong - */ +double Item_func_hybrid_field_type::val_real_from_date_op() +{ + MYSQL_TIME ltime; + if (date_op_with_null_check(<ime)) + return 0; + ltime.time_type= mysql_timestamp_type(); + return TIME_to_double(<ime); +} +longlong Item_func_hybrid_field_type::val_int_from_date_op() +{ + MYSQL_TIME ltime; + if (date_op_with_null_check(<ime)) + return 0; + ltime.time_type= mysql_timestamp_type(); + return TIME_to_ulonglong(<ime); +} - if (!(res= val_str(&tmp))) +my_decimal * +Item_func_hybrid_field_type::val_decimal_from_date_op(my_decimal *dec) +{ + MYSQL_TIME ltime; + if (date_op_with_null_check(<ime)) { - *error= 0; + my_decimal_set_zero(dec); return 0; } - Converter_strtoll10_with_warn cnv(NULL, Warn_filter_all(), - res->charset(), res->ptr(), res->length()); - *error= cnv.error(); - return cnv.result(); + ltime.time_type= mysql_timestamp_type(); + return date2my_decimal(<ime, dec); } -longlong Item::val_int_signed_typecast() +double Item_func_hybrid_field_type::val_real_from_str_op() { - if (cast_to_int_type() != STRING_RESULT) - return val_int(); + String *res= str_op_with_null_check(&str_value); + return res ? double_from_string_with_check(res) : 0.0; +} - int error; - longlong value= val_int_from_str(&error); - if (!null_value && value < 0 && error == 0) - push_note_converted_to_negative_complement(current_thd); - return value; +longlong Item_func_hybrid_field_type::val_int_from_str_op() +{ + String *res= str_op_with_null_check(&str_value); + return res ? longlong_from_string_with_check(res) : 0; } +my_decimal * +Item_func_hybrid_field_type::val_decimal_from_str_op(my_decimal *decimal_value) +{ + String *res= str_op_with_null_check(&str_value); + return res ? decimal_from_string_with_check(decimal_value, res) : 0; +} -void Item_func_unsigned::print(String *str, enum_query_type query_type) +bool Item_func_hybrid_field_type::get_date_from_str_op(MYSQL_TIME *ltime, + ulonglong fuzzydate) +{ + StringBuffer<40> tmp; + String *res; + if (!(res= str_op_with_null_check(&tmp)) || + str_to_datetime_with_warn(res->charset(), res->ptr(), res->length(), + ltime, fuzzydate)) + return make_zero_mysql_time(ltime, fuzzydate); + return (null_value= 0); +} + + +void Item_func_signed::print(String *str, enum_query_type query_type) { str->append(STRING_WITH_LEN("cast(")); args[0]->print(str, query_type); - str->append(STRING_WITH_LEN(" as unsigned)")); + str->append(STRING_WITH_LEN(" as signed)")); } -longlong Item::val_int_unsigned_typecast() +void Item_func_unsigned::print(String *str, enum_query_type query_type) { - if (cast_to_int_type() == DECIMAL_RESULT) - { - longlong value; - my_decimal tmp, *dec= val_decimal(&tmp); - if (!null_value) - my_decimal2int(E_DEC_FATAL_ERROR, dec, 1, &value); - else - value= 0; - return value; - } - else if (cast_to_int_type() != STRING_RESULT) - { - longlong value= val_int(); - if (!null_value && unsigned_flag == 0 && value < 0) - push_note_converted_to_positive_complement(current_thd); - return value; - } + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as unsigned)")); - int error; - longlong value= val_int_from_str(&error); - if (!null_value && error < 0) - push_note_converted_to_positive_complement(current_thd); - return value; } @@ -1210,7 +1071,7 @@ err: push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), - name, 1L); + name.str, 1L); return dec; } @@ -1253,7 +1114,7 @@ double Item_double_typecast::val_real() Sql_condition::WARN_LEVEL_WARN, ER_WARN_DATA_OUT_OF_RANGE, ER_THD(thd, ER_WARN_DATA_OUT_OF_RANGE), - name, 1); + name.str, (ulong) 1); if (error < 0) { null_value= 1; // Illegal value @@ -1396,11 +1257,6 @@ void Item_func_additive_op::result_precision() DBUG_ASSERT(arg1_int >= 0); DBUG_ASSERT(arg2_int >= 0); - /* Integer operations keep unsigned_flag if one of arguments is unsigned */ - if (result_type() == INT_RESULT) - unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; - else - unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); } @@ -1410,16 +1266,30 @@ void Item_func_additive_op::result_precision() The following function is here to allow the user to force subtraction of UNSIGNED BIGINT to return negative values. */ - -void Item_func_minus::fix_length_and_dec() +void Item_func_minus::fix_unsigned_flag() { - Item_num_op::fix_length_and_dec(); if (unsigned_flag && (current_thd->variables.sql_mode & MODE_NO_UNSIGNED_SUBTRACTION)) unsigned_flag=0; } +void Item_func_minus::fix_length_and_dec() +{ + DBUG_ENTER("Item_func_minus::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_minus; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;); + DBUG_ASSERT(!aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) + { + Item_func_minus::type_handler()->Item_func_minus_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); + } + DBUG_VOID_RETURN; +} + + double Item_func_minus::real_op() { double value= args[0]->val_real() - args[1]->val_real(); @@ -1627,13 +1497,8 @@ my_decimal *Item_func_mul::decimal_op(my_decimal *decimal_value) void Item_func_mul::result_precision() { - /* Integer operations keep unsigned_flag if one of arguments is unsigned */ - if (result_type() == INT_RESULT) - unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; - else - unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; decimals= MY_MIN(args[0]->decimal_scale() + args[1]->decimal_scale(), - DECIMAL_MAX_SCALE); + DECIMAL_MAX_SCALE); uint est_prec = args[0]->decimal_precision() + args[1]->decimal_precision(); uint precision= MY_MIN(est_prec, DECIMAL_MAX_PRECISION); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, @@ -1641,6 +1506,22 @@ void Item_func_mul::result_precision() } +void Item_func_mul::fix_length_and_dec(void) +{ + DBUG_ENTER("Item_func_mul::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_mul; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_for_result;); + DBUG_ASSERT(aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) + { + Item_func_mul::type_handler()->Item_func_mul_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); + } + DBUG_VOID_RETURN; +} + + double Item_func_div::real_op() { DBUG_ASSERT(fixed == 1); @@ -1702,53 +1583,51 @@ void Item_func_div::result_precision() uint precision=MY_MIN(args[0]->decimal_precision() + args[1]->divisor_precision_increment() + prec_increment, DECIMAL_MAX_PRECISION); - - /* Integer operations keep unsigned_flag if one of arguments is unsigned */ - if (result_type() == INT_RESULT) - unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; - else - unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; decimals= MY_MIN(args[0]->decimal_scale() + prec_increment, DECIMAL_MAX_SCALE); max_length= my_decimal_precision_to_length_no_truncation(precision, decimals, unsigned_flag); } -void Item_func_div::fix_length_and_dec() +void Item_func_div::fix_length_and_dec_double(void) +{ + Item_num_op::fix_length_and_dec_double(); + decimals= MY_MAX(args[0]->decimals, args[1]->decimals) + prec_increment; + set_if_smaller(decimals, NOT_FIXED_DEC); + uint tmp= float_length(decimals); + if (decimals == NOT_FIXED_DEC) + max_length= tmp; + else + { + max_length=args[0]->max_length - args[0]->decimals + decimals; + set_if_smaller(max_length, tmp); + } +} + + +void Item_func_div::fix_length_and_dec_int(void) +{ + set_handler(&type_handler_newdecimal); + DBUG_PRINT("info", ("Type changed: %s", type_handler()->name().ptr())); + Item_num_op::fix_length_and_dec_decimal(); +} + + +void Item_func_div::fix_length_and_dec(void) { DBUG_ENTER("Item_func_div::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); prec_increment= current_thd->variables.div_precincrement; - Item_num_op::fix_length_and_dec(); - switch (Item_func_div::result_type()) { - case REAL_RESULT: + maybe_null= 1; // devision by zero + + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_div; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;); + DBUG_ASSERT(!aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) { - decimals=MY_MAX(args[0]->decimals,args[1]->decimals)+prec_increment; - set_if_smaller(decimals, NOT_FIXED_DEC); - uint tmp=float_length(decimals); - if (decimals == NOT_FIXED_DEC) - max_length= tmp; - else - { - max_length=args[0]->max_length - args[0]->decimals + decimals; - set_if_smaller(max_length,tmp); - } - break; - } - case INT_RESULT: - set_handler_by_result_type(DECIMAL_RESULT); - DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); - result_precision(); - break; - case DECIMAL_RESULT: - result_precision(); - fix_decimals(); - break; - case STRING_RESULT: - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); + Item_func_div::type_handler()->Item_func_div_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); } - maybe_null= 1; // devision by zero DBUG_VOID_RETURN; } @@ -1921,9 +1800,18 @@ void Item_func_mod::result_precision() void Item_func_mod::fix_length_and_dec() { - Item_num_op::fix_length_and_dec(); - maybe_null= 1; - unsigned_flag= args[0]->unsigned_flag; + DBUG_ENTER("Item_func_mod::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + maybe_null= true; // division by zero + const Type_aggregator *aggregator= &type_handler_data->m_type_aggregator_for_mod; + DBUG_EXECUTE_IF("num_op", aggregator= &type_handler_data->m_type_aggregator_non_commutative_test;); + DBUG_ASSERT(!aggregator->is_commutative()); + if (!fix_type_handler(aggregator)) + { + Item_func_mod::type_handler()->Item_func_mod_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); + } + DBUG_VOID_RETURN; } @@ -1970,12 +1858,10 @@ my_decimal *Item_func_neg::decimal_op(my_decimal *decimal_value) } -void Item_func_neg::fix_length_and_dec() +void Item_func_neg::fix_length_and_dec_int() { - DBUG_ENTER("Item_func_neg::fix_length_and_dec"); - Item_func_num1::fix_length_and_dec(); - /* 1 add because sign can appear */ max_length= args[0]->max_length + 1; + set_handler(type_handler_long_or_longlong()); /* If this is in integer context keep the context as integer if possible @@ -1983,7 +1869,7 @@ void Item_func_neg::fix_length_and_dec() Use val() to get value as arg_type doesn't mean that item is Item_int or Item_float due to existence of Item_param. */ - if (Item_func_neg::result_type() == INT_RESULT && args[0]->const_item()) + if (args[0]->const_item()) { longlong val= args[0]->val_int(); if ((ulonglong) val >= (ulonglong) LONGLONG_MIN && @@ -1998,7 +1884,34 @@ void Item_func_neg::fix_length_and_dec() DBUG_PRINT("info", ("Type changed: DECIMAL_RESULT")); } } - unsigned_flag= 0; + unsigned_flag= false; +} + + +void Item_func_neg::fix_length_and_dec_double() +{ + set_handler(&type_handler_double); + decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC + max_length= args[0]->max_length + 1; + unsigned_flag= false; +} + + +void Item_func_neg::fix_length_and_dec_decimal() +{ + set_handler(&type_handler_newdecimal); + decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC + max_length= args[0]->max_length + 1; + unsigned_flag= false; +} + + +void Item_func_neg::fix_length_and_dec() +{ + DBUG_ENTER("Item_func_neg::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + args[0]->cast_to_int_type_handler()->Item_func_neg_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); DBUG_VOID_RETURN; } @@ -2039,13 +1952,42 @@ my_decimal *Item_func_abs::decimal_op(my_decimal *decimal_value) } -void Item_func_abs::fix_length_and_dec() +void Item_func_abs::fix_length_and_dec_int() +{ + max_length= args[0]->max_length; + unsigned_flag= args[0]->unsigned_flag; + set_handler(type_handler_long_or_longlong()); +} + + +void Item_func_abs::fix_length_and_dec_double() { - Item_func_num1::fix_length_and_dec(); + set_handler(&type_handler_double); + decimals= args[0]->decimals; // Preserve NOT_FIXED_DEC + max_length= float_length(decimals); unsigned_flag= args[0]->unsigned_flag; } +void Item_func_abs::fix_length_and_dec_decimal() +{ + set_handler(&type_handler_newdecimal); + decimals= args[0]->decimal_scale(); // Do not preserve NOT_FIXED_DEC + max_length= args[0]->max_length; + unsigned_flag= args[0]->unsigned_flag; +} + + +void Item_func_abs::fix_length_and_dec() +{ + DBUG_ENTER("Item_func_abs::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + args[0]->cast_to_int_type_handler()->Item_func_abs_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); + DBUG_VOID_RETURN; +} + + /** Gateway to natural LOG function. */ double Item_func_ln::val_real() { @@ -2274,54 +2216,47 @@ longlong Item_func_bit_neg::val_int() // Conversion functions -void Item_func_int_val::fix_length_and_dec() +void Item_func_int_val::fix_length_and_dec_int_or_decimal() { - DBUG_ENTER("Item_func_int_val::fix_length_and_dec"); - DBUG_PRINT("info", ("name %s", func_name())); - ulonglong tmp_max_length= (ulonglong ) args[0]->max_length - (args[0]->decimals ? args[0]->decimals + 1 : 0) + 2; - max_length= tmp_max_length > (ulonglong) 4294967295U ? - (uint32) 4294967295U : (uint32) tmp_max_length; + max_length= tmp_max_length > (ulonglong) UINT_MAX32 ? + (uint32) UINT_MAX32 : (uint32) tmp_max_length; uint tmp= float_length(decimals); set_if_smaller(max_length,tmp); decimals= 0; - // Note, cast_to_int_type() can return TIME_RESULT - switch (args[0]->cast_to_int_type()) + /* + -2 because in most high position can't be used any digit for longlong + and one position for increasing value during operation + */ + if (args[0]->max_length - args[0]->decimals >= DECIMAL_LONGLONG_DIGITS - 2) { - case STRING_RESULT: - case REAL_RESULT: - set_handler_by_result_type(REAL_RESULT); - max_length= float_length(decimals); - break; - case INT_RESULT: - case TIME_RESULT: - case DECIMAL_RESULT: - /* - -2 because in most high position can't be used any digit for longlong - and one position for increasing value during operation - */ - if ((args[0]->max_length - args[0]->decimals) >= - (DECIMAL_LONGLONG_DIGITS - 2)) - { - set_handler_by_result_type(DECIMAL_RESULT); - } - else - { - unsigned_flag= args[0]->unsigned_flag; - set_handler_by_result_type(INT_RESULT); - } - break; - case ROW_RESULT: - DBUG_ASSERT(0); + set_handler(&type_handler_newdecimal); + } + else + { + unsigned_flag= args[0]->unsigned_flag; + set_handler(type_handler_long_or_longlong()); } - DBUG_PRINT("info", ("Type: %s", - (result_type() == REAL_RESULT ? "REAL_RESULT" : - result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" : - result_type() == INT_RESULT ? "INT_RESULT" : - "--ILLEGAL!!!--"))); +} + + +void Item_func_int_val::fix_length_and_dec_double() +{ + set_handler(&type_handler_double); + max_length= float_length(0); + decimals= 0; +} + +void Item_func_int_val::fix_length_and_dec() +{ + DBUG_ENTER("Item_func_int_val::fix_length_and_dec"); + DBUG_PRINT("info", ("name %s", func_name())); + args[0]->cast_to_int_type_handler()-> + Item_func_int_val_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s", type_handler()->name().ptr())); DBUG_VOID_RETURN; } @@ -2420,85 +2355,92 @@ my_decimal *Item_func_floor::decimal_op(my_decimal *decimal_value) } -void Item_func_round::fix_length_and_dec() +void Item_func_round::fix_length_and_dec_decimal(uint decimals_to_set) { - int decimals_to_set; - longlong val1; - bool val1_unsigned; - + int decimals_delta= args[0]->decimals - decimals_to_set; + int length_increase= (decimals_delta <= 0 || truncate) ? 0 : 1; + int precision= args[0]->decimal_precision() + length_increase - + decimals_delta; + DBUG_ASSERT(decimals_to_set <= DECIMAL_MAX_SCALE); + set_handler(&type_handler_newdecimal); unsigned_flag= args[0]->unsigned_flag; - if (!args[1]->const_item()) + decimals= decimals_to_set; + max_length= my_decimal_precision_to_length_no_truncation(precision, + decimals, + unsigned_flag); +} + +void Item_func_round::fix_length_and_dec_double(uint decimals_to_set) +{ + set_handler(&type_handler_double); + unsigned_flag= args[0]->unsigned_flag; + decimals= decimals_to_set; + max_length= float_length(decimals_to_set); +} + + +void Item_func_round::fix_arg_decimal() +{ + if (args[1]->const_item()) { - decimals= args[0]->decimals; - max_length= float_length(decimals); - if (args[0]->result_type() == DECIMAL_RESULT) - { - max_length++; - set_handler_by_result_type(DECIMAL_RESULT); - } + uint dec= (uint) args[1]->val_uint_from_val_int(DECIMAL_MAX_SCALE); + if (args[1]->null_value) + fix_length_and_dec_double(NOT_FIXED_DEC); else - set_handler_by_result_type(REAL_RESULT); - return; + fix_length_and_dec_decimal(dec); } + else + { + set_handler(&type_handler_newdecimal); + unsigned_flag= args[0]->unsigned_flag; + decimals= args[0]->decimals; + max_length= float_length(args[0]->decimals) + 1; + } +} - val1= args[1]->val_int(); - if ((null_value= args[1]->null_value)) - return; - val1_unsigned= args[1]->unsigned_flag; - if (val1 < 0) - decimals_to_set= val1_unsigned ? INT_MAX : 0; +void Item_func_round::fix_arg_double() +{ + if (args[1]->const_item()) + { + uint dec= (uint) args[1]->val_uint_from_val_int(NOT_FIXED_DEC); + fix_length_and_dec_double(args[1]->null_value ? NOT_FIXED_DEC : dec); + } else - decimals_to_set= (val1 > INT_MAX) ? INT_MAX : (int) val1; + fix_length_and_dec_double(args[0]->decimals); +} + - if (args[0]->decimals == NOT_FIXED_DEC) +void Item_func_round::fix_arg_int() +{ + if (args[1]->const_item()) { - decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC); - max_length= float_length(decimals); - set_handler_by_result_type(REAL_RESULT); - return; - } - - switch (args[0]->result_type()) { - case REAL_RESULT: - case STRING_RESULT: - set_handler_by_result_type(REAL_RESULT); - decimals= MY_MIN(decimals_to_set, NOT_FIXED_DEC); - max_length= float_length(decimals); - break; - case INT_RESULT: - if ((!decimals_to_set && truncate) || (args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS)) + longlong val1= args[1]->val_int(); + bool val1_is_negative= val1 < 0 && !args[1]->unsigned_flag; + uint decimals_to_set= val1_is_negative ? + 0 : (uint) MY_MIN(val1, DECIMAL_MAX_SCALE); + if (args[1]->null_value) + fix_length_and_dec_double(NOT_FIXED_DEC); + else if ((!decimals_to_set && truncate) || + args[0]->decimal_precision() < DECIMAL_LONGLONG_DIGITS) { - int length_can_increase= MY_TEST(!truncate && (val1 < 0) && - !val1_unsigned); + // Length can increase in some cases: ROUND(9,-1) -> 10 + int length_can_increase= MY_TEST(!truncate && val1_is_negative); max_length= args[0]->max_length + length_can_increase; - /* Here we can keep INT_RESULT */ - set_handler_by_result_type(INT_RESULT); + // Here we can keep INT_RESULT + unsigned_flag= args[0]->unsigned_flag; decimals= 0; - break; + set_handler(type_handler_long_or_longlong()); } - /* fall through */ - case DECIMAL_RESULT: - { - set_handler_by_result_type(DECIMAL_RESULT); - decimals_to_set= MY_MIN(DECIMAL_MAX_SCALE, decimals_to_set); - int decimals_delta= args[0]->decimals - decimals_to_set; - int precision= args[0]->decimal_precision(); - int length_increase= ((decimals_delta <= 0) || truncate) ? 0:1; - - precision-= decimals_delta - length_increase; - decimals= MY_MIN(decimals_to_set, DECIMAL_MAX_SCALE); - max_length= my_decimal_precision_to_length_no_truncation(precision, - decimals, - unsigned_flag); - break; - } - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); /* This result type isn't handled */ + else + fix_length_and_dec_decimal(decimals_to_set); } + else + fix_length_and_dec_double(args[0]->decimals); + } + double my_double_round(double value, longlong dec, bool dec_unsigned, bool truncate) { @@ -2721,122 +2663,17 @@ double Item_func_units::val_real() } -void Item_func_min_max::fix_length_and_dec() +bool Item_func_min_max::fix_attributes(Item **items, uint nitems) { - uint unsigned_count= 0; - int max_int_part=0; - decimals=0; - max_length=0; - maybe_null=0; - Item_result tmp_cmp_type= args[0]->cmp_type(); - uint string_type_count= 0; - uint temporal_type_count= 0; - enum_field_types temporal_field_type= MYSQL_TYPE_DATETIME; - - for (uint i=0 ; i < arg_count ; i++) - { - set_if_bigger(max_length, args[i]->max_length); - set_if_bigger(decimals, args[i]->decimals); - set_if_bigger(max_int_part, args[i]->decimal_int_part()); - unsigned_count+= args[i]->unsigned_flag; - if (args[i]->maybe_null) - maybe_null= 1; - tmp_cmp_type= item_cmp_type(tmp_cmp_type, args[i]->cmp_type()); - string_type_count+= args[i]->cmp_type() == STRING_RESULT; - if (args[i]->cmp_type() == TIME_RESULT) - { - if (!temporal_type_count) - temporal_field_type= args[i]->field_type(); - else - temporal_field_type= Field::field_type_merge(temporal_field_type, - args[i]->field_type()); - temporal_type_count++; - } - } - unsigned_flag= unsigned_count == arg_count; // if all args are unsigned - - switch (tmp_cmp_type) { - case TIME_RESULT: - // At least one temporal argument was found. - collation.set_numeric(); - set_handler_by_field_type(temporal_field_type); - if (is_temporal_type_with_time(temporal_field_type)) - set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); - else - decimals= 0; - break; - - case STRING_RESULT: - /* - All arguments are of string-alike types: - CHAR, VARCHAR, TEXT, BINARY, VARBINARY, BLOB, SET, ENUM - No numeric and no temporal types were found. - */ - agg_arg_charsets_for_string_result_with_comparison(collation, - args, arg_count); - set_handler_by_field_type(agg_field_type(args, arg_count, false)); - break; - - case INT_RESULT: - /* - All arguments have INT-alike types: - TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT. - */ - collation.set_numeric(); - fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part + - decimals, - decimals, - unsigned_flag)); - if (unsigned_count != 0 && unsigned_count != arg_count) - { - /* - If all args are of INT-alike type, but have different unsigned_flag, - then change type to DECIMAL. - */ - set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL); - } - else - { - /* - There are only INT-alike arguments with equal unsigned_flag. - Aggregate types to get the best covering type. - Treat BIT as LONGLONG when aggregating to non-BIT types. - Possible final type: TINY, SHORT, LONG, LONGLONG, INT24, YEAR, BIT. - */ - set_handler_by_field_type(agg_field_type(args, arg_count, true)); - } - break; - - case DECIMAL_RESULT: - // All arguments are of DECIMAL type - collation.set_numeric(); - fix_char_length(my_decimal_precision_to_length_no_truncation(max_int_part + - decimals, - decimals, - unsigned_flag)); - set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL); - break; - - case ROW_RESULT: - DBUG_ASSERT(0); - // Pass through - case REAL_RESULT: - collation.set_numeric(); - fix_char_length(float_length(decimals)); - /* - Set type to DOUBLE, as Item_func::create_tmp_field() does not - distinguish between DOUBLE and FLOAT and always creates Field_double. - Perhaps we should eventually change this to use agg_field_type() here, - and fix Item_func::create_tmp_field() to create Field_float when possible. - */ - set_handler_by_field_type(MYSQL_TYPE_DOUBLE); - break; - } + bool rc= Item_func_min_max::type_handler()-> + Item_func_min_max_fix_attributes(current_thd, this, items, nitems); + DBUG_ASSERT(!rc || current_thd->is_error()); + return rc; } /* - Compare item arguments in the DATETIME context. + Compare item arguments using DATETIME/DATE/TIME representation. DESCRIPTION Compare item arguments as DATETIME values and return the index of the @@ -2849,21 +2686,11 @@ void Item_func_min_max::fix_length_and_dec() 0 Otherwise */ -bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) +bool Item_func_min_max::get_date_native(MYSQL_TIME *ltime, ulonglong fuzzy_date) { longlong UNINIT_VAR(min_max); DBUG_ASSERT(fixed == 1); - /* - just like ::val_int() method of a string item can be called, - for example, SELECT CONCAT("10", "12") + 1, - ::get_date() can be called for non-temporal values, - for example, SELECT MONTH(GREATEST("2011-11-21", "2010-10-09")) - - */ - if (Item_func_min_max::cmp_type() != TIME_RESULT) - return Item_func::get_date(ltime, fuzzy_date); - for (uint i=0; i < arg_count ; i++) { longlong res= args[i]->val_temporal_packed(Item_func_min_max::field_type()); @@ -2901,63 +2728,35 @@ bool Item_func_min_max::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date) } -String *Item_func_min_max::val_str(String *str) +String *Item_func_min_max::val_str_native(String *str) { - DBUG_ASSERT(fixed == 1); - if (Item_func_min_max::cmp_type() == TIME_RESULT) - return val_string_from_date(str); - switch (Item_func_min_max::result_type()) { - case INT_RESULT: - return val_string_from_int(str); - case DECIMAL_RESULT: - return val_string_from_decimal(str); - case REAL_RESULT: - return val_string_from_real(str); - case STRING_RESULT: + String *UNINIT_VAR(res); + for (uint i=0; i < arg_count ; i++) { - String *UNINIT_VAR(res); - for (uint i=0; i < arg_count ; i++) + if (i == 0) + res=args[i]->val_str(str); + else { - if (i == 0) - res=args[i]->val_str(str); - else + String *res2; + res2= args[i]->val_str(res == str ? &tmp_value : str); + if (res2) { - String *res2; - res2= args[i]->val_str(res == str ? &tmp_value : str); - if (res2) - { - int cmp= sortcmp(res,res2,collation.collation); - if ((cmp_sign < 0 ? cmp : -cmp) < 0) - res=res2; - } + int cmp= sortcmp(res,res2,collation.collation); + if ((cmp_sign < 0 ? cmp : -cmp) < 0) + res=res2; } - if ((null_value= args[i]->null_value)) - return 0; } - res->set_charset(collation.collation); - return res; - } - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); // This case should never be chosen - return 0; + if ((null_value= args[i]->null_value)) + return 0; } - return 0; // Keep compiler happy + res->set_charset(collation.collation); + return res; } -double Item_func_min_max::val_real() +double Item_func_min_max::val_real_native() { - DBUG_ASSERT(fixed == 1); double value=0.0; - if (Item_func_min_max::cmp_type() == TIME_RESULT) - { - MYSQL_TIME ltime; - if (get_date(<ime, 0)) - return 0; - - return TIME_to_double(<ime); - } for (uint i=0; i < arg_count ; i++) { if (i == 0) @@ -2975,18 +2774,10 @@ double Item_func_min_max::val_real() } -longlong Item_func_min_max::val_int() +longlong Item_func_min_max::val_int_native() { DBUG_ASSERT(fixed == 1); longlong value=0; - if (Item_func_min_max::cmp_type() == TIME_RESULT) - { - MYSQL_TIME ltime; - if (get_date(<ime, 0)) - return 0; - - return TIME_to_ulonglong(<ime); - } for (uint i=0; i < arg_count ; i++) { if (i == 0) @@ -3004,19 +2795,11 @@ longlong Item_func_min_max::val_int() } -my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) +my_decimal *Item_func_min_max::val_decimal_native(my_decimal *dec) { DBUG_ASSERT(fixed == 1); my_decimal tmp_buf, *tmp, *UNINIT_VAR(res); - if (Item_func_min_max::cmp_type() == TIME_RESULT) - { - MYSQL_TIME ltime; - if (get_date(<ime, 0)) - return 0; - - return date2my_decimal(<ime, dec); - } for (uint i=0; i < arg_count ; i++) { if (i == 0) @@ -3046,7 +2829,15 @@ my_decimal *Item_func_min_max::val_decimal(my_decimal *dec) } -longlong Item_func_length::val_int() +longlong Item_func_bit_length::val_int() +{ + DBUG_ASSERT(fixed == 1); + String *res= args[0]->val_str(&value); + return (null_value= !res) ? 0 : (longlong) res->length() * 8; +} + + +longlong Item_func_octet_length::val_int() { DBUG_ASSERT(fixed == 1); String *res=args[0]->val_str(&value); @@ -3082,13 +2873,6 @@ longlong Item_func_coercibility::val_int() } -void Item_func_locate::fix_length_and_dec() -{ - max_length= MY_INT32_NUM_DECIMAL_DIGITS; - agg_arg_charsets_for_comparison(cmp_collation, args, 2); -} - - longlong Item_func_locate::val_int() { DBUG_ASSERT(fixed == 1); @@ -3474,7 +3258,7 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, !(f_args.maybe_null= (char*) thd->alloc(arg_count * sizeof(char))) || !(num_buffer= (char*) thd->alloc(arg_count * ALIGN_SIZE(sizeof(double)))) || - !(f_args.attributes= (char**) thd->alloc(arg_count * + !(f_args.attributes= (const char**) thd->alloc(arg_count * sizeof(char *))) || !(f_args.attribute_lengths= (ulong*) thd->alloc(arg_count * sizeof(long)))) @@ -3504,8 +3288,8 @@ udf_handler::fix_fields(THD *thd, Item_func_or_sum *func, f_args.lengths[i]= arguments[i]->max_length; 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; + f_args.attributes[i]= arguments[i]->name.str; + f_args.attribute_lengths[i]= arguments[i]->name.length; if (arguments[i]->const_item()) { @@ -3873,7 +3657,7 @@ longlong Item_master_pos_wait::val_int() longlong pos = (ulong)args[1]->val_int(); longlong timeout = (arg_count>=3) ? args[2]->val_int() : 0 ; String connection_name_buff; - LEX_STRING connection_name; + LEX_CSTRING connection_name; Master_info *mi= NULL; if (arg_count >= 4) { @@ -3881,7 +3665,7 @@ longlong Item_master_pos_wait::val_int() if (!(con= args[3]->val_str(&connection_name_buff))) goto err; - connection_name.str= (char*) con->ptr(); + connection_name.str= con->ptr(); connection_name.length= con->length(); if (check_master_connection_name(&connection_name)) { @@ -4581,16 +4365,16 @@ bool Item_func_user_var::check_vcol_func_processor(void *arg) #define extra_size sizeof(double) -user_var_entry *get_variable(HASH *hash, LEX_STRING &name, - bool create_if_not_exists) +user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name, + bool create_if_not_exists) { user_var_entry *entry; - if (!(entry = (user_var_entry*) my_hash_search(hash, (uchar*) name.str, - name.length)) && + if (!(entry = (user_var_entry*) my_hash_search(hash, (uchar*) name->str, + name->length)) && create_if_not_exists) { - uint size=ALIGN_SIZE(sizeof(user_var_entry))+name.length+1+extra_size; + uint size=ALIGN_SIZE(sizeof(user_var_entry))+name->length+1+extra_size; if (!my_hash_inited(hash)) return 0; if (!(entry = (user_var_entry*) my_malloc(size, @@ -4599,7 +4383,7 @@ user_var_entry *get_variable(HASH *hash, LEX_STRING &name, return 0; entry->name.str=(char*) entry+ ALIGN_SIZE(sizeof(user_var_entry))+ extra_size; - entry->name.length=name.length; + entry->name.length=name->length; entry->value=0; entry->length=0; entry->update_query_id=0; @@ -4617,7 +4401,7 @@ user_var_entry *get_variable(HASH *hash, LEX_STRING &name, */ entry->used_query_id=current_thd->query_id; entry->type=STRING_RESULT; - memcpy(entry->name.str, name.str, name.length+1); + memcpy((char*) entry->name.str, name->str, name->length+1); if (my_hash_insert(hash,(uchar*) entry)) { my_free(entry); @@ -4639,7 +4423,7 @@ bool Item_func_set_user_var::set_entry(THD *thd, bool create_if_not_exists) { if (m_var_entry && thd->thread_id == entry_thread_id) goto end; // update entry->update_query_id for PS - if (!(m_var_entry= get_variable(&thd->user_vars, name, create_if_not_exists))) + if (!(m_var_entry= get_variable(&thd->user_vars, &name, create_if_not_exists))) { entry_thread_id= 0; return TRUE; @@ -4687,8 +4471,27 @@ bool Item_func_set_user_var::fix_fields(THD *thd, Item **ref) m_var_entry->set_charset(args[0]->collation.derivation == DERIVATION_NUMERIC ? default_charset() : args[0]->collation.collation); collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT); - set_handler_by_result_type(args[0]->result_type(), - max_length, collation.collation); + switch (args[0]->result_type()) { + case STRING_RESULT: + case TIME_RESULT: + set_handler(type_handler_long_blob. + type_handler_adjusted_to_max_octet_length(max_length, + collation.collation)); + break; + case REAL_RESULT: + set_handler(&type_handler_double); + break; + case INT_RESULT: + set_handler(Type_handler::type_handler_long_or_longlong(max_char_length())); + break; + case DECIMAL_RESULT: + set_handler(&type_handler_newdecimal); + break; + case ROW_RESULT: + DBUG_ASSERT(0); + set_handler(&type_handler_row); + break; + } if (thd->lex->current_select) { /* @@ -5021,7 +4824,7 @@ Item_func_set_user_var::check(bool use_result_field) if (use_result_field && !result_field) use_result_field= FALSE; - switch (Item_func_set_user_var::result_type()) { + switch (result_type()) { case REAL_RESULT: { save_result.vreal= use_result_field ? result_field->val_real() : @@ -5114,7 +4917,7 @@ Item_func_set_user_var::update() bool res= 0; DBUG_ENTER("Item_func_set_user_var::update"); - switch (Item_func_set_user_var::result_type()) { + switch (result_type()) { case REAL_RESULT: { res= update_hash((void*) &save_result.vreal,sizeof(save_result.vreal), @@ -5259,7 +5062,7 @@ void Item_func_set_user_var::print_as_stmt(String *str, args[0]->print_parenthesised(str, query_type, precedence()); } -bool Item_func_set_user_var::send(Protocol *protocol, String *str_arg) +bool Item_func_set_user_var::send(Protocol *protocol, st_value *buffer) { if (result_field) { @@ -5267,7 +5070,7 @@ bool Item_func_set_user_var::send(Protocol *protocol, String *str_arg) update(); return protocol->store(result_field); } - return Item::send(protocol, str_arg); + return Item::send(protocol, buffer); } void Item_func_set_user_var::make_field(THD *thd, Send_field *tmp_field) @@ -5276,8 +5079,8 @@ void Item_func_set_user_var::make_field(THD *thd, Send_field *tmp_field) { result_field->make_field(tmp_field); DBUG_ASSERT(tmp_field->table_name != 0); - if (Item::name) - tmp_field->col_name=Item::name; // Use user supplied name + if (Item::name.str) + tmp_field->col_name= Item::name; // Use user supplied name } else Item::make_field(thd, tmp_field); @@ -5445,7 +5248,7 @@ longlong Item_func_get_user_var::val_int() static int get_var_with_binlog(THD *thd, enum_sql_command sql_command, - LEX_STRING &name, user_var_entry **out_entry) + LEX_CSTRING *name, user_var_entry **out_entry) { BINLOG_USER_VAR_EVENT *user_var_event; user_var_entry *var_entry; @@ -5571,7 +5374,7 @@ void Item_func_get_user_var::fix_length_and_dec() decimals=NOT_FIXED_DEC; max_length=MAX_BLOB_WIDTH; - error= get_var_with_binlog(thd, thd->lex->sql_command, name, &m_var_entry); + error= get_var_with_binlog(thd, thd->lex->sql_command, &name, &m_var_entry); /* If the variable didn't exist it has been created as a STRING-type. @@ -5584,7 +5387,7 @@ void Item_func_get_user_var::fix_length_and_dec() max_length= m_var_entry->length; collation.set(m_var_entry->charset(), DERIVATION_IMPLICIT); set_handler_by_result_type(m_var_entry->type); - switch (Item_func_get_user_var::result_type()) { + switch (result_type()) { case REAL_RESULT: fix_char_length(DBL_DIG + 8); break; @@ -5594,7 +5397,6 @@ void Item_func_get_user_var::fix_length_and_dec() break; case STRING_RESULT: max_length= MAX_BLOB_WIDTH - 1; - set_handler_by_field_type(MYSQL_TYPE_MEDIUM_BLOB); break; case DECIMAL_RESULT: fix_char_length(DECIMAL_MAX_STR_LENGTH); @@ -5610,7 +5412,7 @@ void Item_func_get_user_var::fix_length_and_dec() { collation.set(&my_charset_bin, DERIVATION_IMPLICIT); null_value= 1; - set_handler_by_field_type(MYSQL_TYPE_LONG_BLOB); + set_handler(&type_handler_long_blob); max_length= MAX_BLOB_WIDTH; } } @@ -5648,7 +5450,8 @@ bool Item_func_get_user_var::eq(const Item *item, bool binary_cmp) const bool Item_func_get_user_var::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it) { - Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, get_name(), *it); + LEX_CSTRING tmp_name= get_name(); + Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, &tmp_name, *it); /* Item_func_set_user_var is not fixed after construction, call fix_fields(). @@ -5662,7 +5465,7 @@ bool Item_user_var_as_out_param::fix_fields(THD *thd, Item **ref) DBUG_ASSERT(fixed == 0); DBUG_ASSERT(thd->lex->exchange); if (Item::fix_fields(thd, ref) || - !(entry= get_variable(&thd->user_vars, name, 1))) + !(entry= get_variable(&thd->user_vars, &name, 1))) return TRUE; entry->type= STRING_RESULT; /* @@ -5729,7 +5532,7 @@ void Item_user_var_as_out_param::print_for_load(THD *thd, String *str) Item_func_get_system_var:: Item_func_get_system_var(THD *thd, sys_var *var_arg, enum_var_type var_type_arg, - LEX_STRING *component_arg, const char *name_arg, + LEX_CSTRING *component_arg, const char *name_arg, size_t name_len_arg): Item_func(thd), var(var_arg), var_type(var_type_arg), orig_var_type(var_type_arg), component(*component_arg), cache_present(0) @@ -5836,8 +5639,8 @@ void Item_func_get_system_var::fix_length_and_dec() void Item_func_get_system_var::print(String *str, enum_query_type query_type) { - if (name_length) - str->append(name, name_length); + if (name.length) + str->append(name.str, name.length); else { str->append(STRING_WITH_LEN("@@")); @@ -5859,34 +5662,8 @@ bool Item_func_get_system_var::check_vcol_func_processor(void *arg) return mark_unsupported_function("@@", var->name.str, arg, VCOL_SESSION_FUNC); } -enum Item_result Item_func_get_system_var::result_type() const -{ - switch (var->show_type()) - { - case SHOW_BOOL: - case SHOW_MY_BOOL: - case SHOW_SINT: - case SHOW_SLONG: - case SHOW_SLONGLONG: - case SHOW_UINT: - case SHOW_ULONG: - case SHOW_ULONGLONG: - case SHOW_HA_ROWS: - return INT_RESULT; - case SHOW_CHAR: - case SHOW_CHAR_PTR: - case SHOW_LEX_STRING: - return STRING_RESULT; - case SHOW_DOUBLE: - return REAL_RESULT; - default: - my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str); - return STRING_RESULT; // keep the compiler happy - } -} - -enum_field_types Item_func_get_system_var::field_type() const +const Type_handler *Item_func_get_system_var::type_handler() const { switch (var->show_type()) { @@ -5899,16 +5676,16 @@ enum_field_types Item_func_get_system_var::field_type() const case SHOW_ULONG: case SHOW_ULONGLONG: case SHOW_HA_ROWS: - return MYSQL_TYPE_LONGLONG; + return &type_handler_longlong; case SHOW_CHAR: case SHOW_CHAR_PTR: case SHOW_LEX_STRING: - return MYSQL_TYPE_VARCHAR; + return &type_handler_varchar; case SHOW_DOUBLE: - return MYSQL_TYPE_DOUBLE; + return &type_handler_double; default: my_error(ER_VAR_CANT_BE_READ, MYF(0), var->name.str); - return MYSQL_TYPE_VARCHAR; // keep the compiler happy + return &type_handler_varchar; // keep the compiler happy } } @@ -6397,11 +6174,11 @@ longlong Item_func_bit_xor::val_int() */ -Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, - LEX_STRING component) +Item *get_system_var(THD *thd, enum_var_type var_type, LEX_CSTRING name, + LEX_CSTRING component) { sys_var *var; - LEX_STRING *base_name, *component_name; + LEX_CSTRING *base_name, *component_name; if (component.str) { @@ -6449,7 +6226,6 @@ Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg, Item_func(thd), context(context_arg), m_name(name), m_sp(NULL), sp_result_field(NULL) { maybe_null= 1; - m_name->init_qname(thd); dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE)); dummy_table->s= (TABLE_SHARE*) (dummy_table+1); } @@ -6461,7 +6237,6 @@ Item_func_sp::Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_result_field(NULL) { maybe_null= 1; - m_name->init_qname(thd); dummy_table= (TABLE*) thd->calloc(sizeof(TABLE)+ sizeof(TABLE_SHARE)); dummy_table->s= (TABLE_SHARE*) (dummy_table+1); } @@ -6506,7 +6281,7 @@ Item_func_sp::func_name() const } -void my_missing_function_error(const LEX_STRING &token, const char *func_name) +void my_missing_function_error(const LEX_CSTRING &token, const char *func_name) { if (token.length && is_lex_native_function (&token)) my_error(ER_FUNC_INEXISTENT_NAME_COLLISION, MYF(0), func_name); @@ -6534,19 +6309,17 @@ void my_missing_function_error(const LEX_STRING &token, const char *func_name) */ bool -Item_func_sp::init_result_field(THD *thd) +Item_func_sp::init_result_field(THD *thd, sp_head *sp) { - LEX_STRING empty_name= { C_STRING_WITH_LEN("") }; TABLE_SHARE *share; DBUG_ENTER("Item_func_sp::init_result_field"); DBUG_ASSERT(m_sp == NULL); DBUG_ASSERT(sp_result_field == NULL); - if (!(m_sp= sp_find_routine(thd, TYPE_ENUM_FUNCTION, m_name, - &thd->sp_func_cache, TRUE))) + if (!(m_sp= sp)) { - my_missing_function_error (m_name->m_name, m_name->m_qname.str); + my_missing_function_error (m_name->m_name, ErrConvDQName(m_name).ptr()); context->process_error(thd); DBUG_RETURN(TRUE); } @@ -6562,11 +6335,10 @@ Item_func_sp::init_result_field(THD *thd) dummy_table->maybe_null = maybe_null; dummy_table->in_use= thd; dummy_table->copy_blobs= TRUE; - share->table_cache_key = empty_name; - share->table_name = empty_name; + share->table_cache_key= empty_clex_str; + share->table_name= empty_clex_str; - if (!(sp_result_field= m_sp->create_result_field(max_length, name, - dummy_table))) + if (!(sp_result_field= m_sp->create_result_field(max_length, &name, dummy_table))) { DBUG_RETURN(TRUE); } @@ -6596,7 +6368,7 @@ Item_func_sp::init_result_field(THD *thd) bool Item_func_sp::is_expensive() { - return !m_sp->m_chistics->detistic || + return !m_sp->detistic() || current_thd->locked_tables_mode < LTM_LOCK_TABLES; } @@ -6612,7 +6384,9 @@ void Item_func_sp::fix_length_and_dec() DBUG_ENTER("Item_func_sp::fix_length_and_dec"); DBUG_ASSERT(sp_result_field); - Type_std_attributes::set(sp_result_field); + Type_std_attributes::set(sp_result_field->type_std_attributes()); + // There is a bug in the line below. See MDEV-11292 for details. + collation.derivation= DERIVATION_COERCIBLE; maybe_null= 1; DBUG_VOID_RETURN; @@ -6668,8 +6442,8 @@ Item_func_sp::execute_impl(THD *thd) Sub_statement_state statement_state; Security_context *save_security_ctx= thd->security_ctx; enum enum_sp_data_access access= - (m_sp->m_chistics->daccess == SP_DEFAULT_ACCESS) ? - SP_DEFAULT_ACCESS_MAPPING : m_sp->m_chistics->daccess; + (m_sp->daccess() == SP_DEFAULT_ACCESS) ? + SP_DEFAULT_ACCESS_MAPPING : m_sp->daccess(); DBUG_ENTER("Item_func_sp::execute_impl"); @@ -6686,7 +6460,7 @@ Item_func_sp::execute_impl(THD *thd) statement-based replication (SBR) is active. */ - if (!m_sp->m_chistics->detistic && !trust_function_creators && + 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)) @@ -6717,29 +6491,26 @@ Item_func_sp::make_field(THD *thd, Send_field *tmp_field) DBUG_ENTER("Item_func_sp::make_field"); DBUG_ASSERT(sp_result_field); sp_result_field->make_field(tmp_field); - if (name) + if (name.str) + { + DBUG_ASSERT(name.length == strlen(name.str)); tmp_field->col_name= name; + } DBUG_VOID_RETURN; } -enum enum_field_types -Item_func_sp::field_type() const +const Type_handler *Item_func_sp::type_handler() const { - DBUG_ENTER("Item_func_sp::field_type"); - DBUG_ASSERT(sp_result_field); - DBUG_RETURN(sp_result_field->type()); -} - -Item_result -Item_func_sp::result_type() const -{ - DBUG_ENTER("Item_func_sp::result_type"); + DBUG_ENTER("Item_func_sp::type_handler"); DBUG_PRINT("info", ("m_sp = %p", (void *) m_sp)); DBUG_ASSERT(sp_result_field); - DBUG_RETURN(sp_result_field->result_type()); + // This converts ENUM/SET to STRING + const Type_handler *handler= sp_result_field->type_handler(); + DBUG_RETURN(handler->type_handler_for_item_field()); } + longlong Item_func_found_rows::val_int() { DBUG_ASSERT(fixed == 1); @@ -6747,6 +6518,34 @@ longlong Item_func_found_rows::val_int() } +longlong Item_func_oracle_sql_rowcount::val_int() +{ + DBUG_ASSERT(fixed == 1); + THD *thd= current_thd; + /* + In case when a query like this: + INSERT a INTO @va FROM t1; + returns multiple rows, SQL%ROWCOUNT should report 1 rather than -1. + */ + longlong rows= thd->get_row_count_func(); + return rows != -1 ? rows : // ROW_COUNT() + thd->found_rows(); // FOUND_ROWS() +} + + +longlong Item_func_sqlcode::val_int() +{ + DBUG_ASSERT(fixed); + DBUG_ASSERT(!null_value); + Diagnostics_area::Sql_condition_iterator it= + current_thd->get_stmt_da()->sql_conditions(); + const Sql_condition *err; + if ((err= it++)) + return err->get_sql_errno(); + return 0; +} + + /** @brief Checks if requested access to function can be granted to user. If function isn't found yet, it searches function first. @@ -6766,11 +6565,7 @@ Item_func_sp::sp_check_access(THD *thd) { DBUG_ENTER("Item_func_sp::sp_check_access"); DBUG_ASSERT(m_sp); - if (check_routine_access(thd, EXECUTE_ACL, - m_sp->m_db.str, m_sp->m_name.str, 0, FALSE)) - DBUG_RETURN(TRUE); - - DBUG_RETURN(FALSE); + DBUG_RETURN(m_sp->check_execute_access(thd)); } @@ -6780,6 +6575,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) bool res; DBUG_ENTER("Item_func_sp::fix_fields"); DBUG_ASSERT(fixed == 0); + sp_head *sp= sp_handler_function.sp_find_routine(thd, m_name, true); /* Checking privileges to execute the function while creating view and @@ -6792,8 +6588,14 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) if (context->security_ctx) thd->security_ctx= context->security_ctx; - res= check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str, - m_name->m_name.str, 0, FALSE); + /* + If the routine is not found, let's still check EXECUTE_ACL to decide + whether to return "Access denied" or "Routine does not exist". + */ + res= sp ? sp->check_execute_access(thd) : + check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str, + m_name->m_name.str, + &sp_handler_function, false); thd->security_ctx= save_security_ctx; if (res) @@ -6808,7 +6610,7 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) to make m_sp and result_field members available to fix_length_and_dec(), which is called from Item_func::fix_fields(). */ - res= init_result_field(thd); + res= init_result_field(thd, sp); if (res) DBUG_RETURN(res); @@ -6836,14 +6638,14 @@ Item_func_sp::fix_fields(THD *thd, Item **ref) Try to set and restore the security context to see whether it's valid */ Security_context *save_secutiry_ctx; - res= set_routine_security_ctx(thd, m_sp, false, &save_secutiry_ctx); + res= set_routine_security_ctx(thd, m_sp, &save_secutiry_ctx); if (!res) m_sp->m_security_ctx.restore_security_context(thd, save_secutiry_ctx); #endif /* ! NO_EMBEDDED_ACCESS_CHECKS */ } - if (!m_sp->m_chistics->detistic) + if (!m_sp->detistic()) { used_tables_cache |= RAND_TABLE_BIT; const_item_cache= FALSE; @@ -6857,7 +6659,7 @@ void Item_func_sp::update_used_tables() { Item_func::update_used_tables(); - if (!m_sp->m_chistics->detistic) + if (!m_sp->detistic()) { used_tables_cache |= RAND_TABLE_BIT; const_item_cache= FALSE; @@ -6962,4 +6764,269 @@ void Item_func_last_value::fix_length_and_dec() } +void Cursor_ref::print_func(String *str, const char *func_name) +{ + append_identifier(current_thd, str, m_cursor_name.str, m_cursor_name.length); + str->append(func_name); +} + + +sp_cursor *Cursor_ref::get_open_cursor_or_error() +{ + THD *thd= current_thd; + sp_cursor *c= thd->spcont->get_cursor(m_cursor_offset); + DBUG_ASSERT(c); + if (!c/*safety*/ || !c->is_open()) + { + my_message(ER_SP_CURSOR_NOT_OPEN, ER_THD(thd, ER_SP_CURSOR_NOT_OPEN), + MYF(0)); + return NULL; + } + return c; +} + + +longlong Item_func_cursor_isopen::val_int() +{ + sp_cursor *c= current_thd->spcont->get_cursor(m_cursor_offset); + DBUG_ASSERT(c != NULL); + return c ? c->is_open() : 0; +} + +longlong Item_func_cursor_found::val_int() +{ + sp_cursor *c= get_open_cursor_or_error(); + return !(null_value= (!c || c->fetch_count() == 0)) && c->found(); +} + + +longlong Item_func_cursor_notfound::val_int() +{ + sp_cursor *c= get_open_cursor_or_error(); + return !(null_value= (!c || c->fetch_count() == 0)) && !c->found(); +} + + +longlong Item_func_cursor_rowcount::val_int() +{ + sp_cursor *c= get_open_cursor_or_error(); + return !(null_value= !c) ? c->row_count() : 0; +} + +/***************************************************************************** + SEQUENCE functions +*****************************************************************************/ + +longlong Item_func_nextval::val_int() +{ + longlong value; + int error; + const char *key; + TABLE *table= table_list->table; + uint length= get_table_def_key(table_list, &key); + THD *thd= table->in_use; + SEQUENCE_LAST_VALUE *entry; + char buff[80]; + String key_buff(buff,sizeof(buff), &my_charset_bin); + DBUG_ASSERT(table && table->s->sequence); + DBUG_ENTER("Item_func_nextval::val_int"); + + if (table->s->tmp_table != NO_TMP_TABLE) + { + /* + Temporary tables has an extra \0 at end to distinguish it from + normal tables + */ + key_buff.copy(key, length, &my_charset_bin); + key_buff.append((char) 0); + key= key_buff.ptr(); + length++; + } + + if (!(entry= ((SEQUENCE_LAST_VALUE*) + my_hash_search(&thd->sequences, (uchar*) key, length)))) + { + if (!(key= (char*) my_memdup(key, length, MYF(MY_WME))) || + !(entry= new SEQUENCE_LAST_VALUE((uchar*) key, length))) + { + /* EOM, error given */ + my_free((char*) key); + delete entry; + null_value= 1; + DBUG_RETURN(0); + } + if (my_hash_insert(&thd->sequences, (uchar*) entry)) + { + /* EOM, error given */ + delete entry; + null_value= 1; + DBUG_RETURN(0); + } + } + entry->null_value= null_value= 0; + value= table->s->sequence->next_value(table, 0, &error); + entry->value= value; + entry->set_version(table); + + if (error) // Warning already printed + entry->null_value= null_value= 1; // For not strict mode + DBUG_RETURN(value); +} + + +/* Print for nextval and lastval */ + +void Item_func_nextval::print(String *str, enum_query_type query_type) +{ + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + const char *d_name= table_list->db, *t_name= table_list->table_name; + bool use_db_name= d_name && d_name[0]; + THD *thd= current_thd; + + str->append(func_name()); + str->append('('); + + /* + for next_val we assume that table_list has been updated to contain + the current db. + */ + + if (lower_case_table_names > 0) + { + strmake(t_name_buff, t_name, MAX_ALIAS_NAME-1); + my_casedn_str(files_charset_info, t_name_buff); + t_name= t_name_buff; + if (use_db_name) + { + strmake(d_name_buff, d_name, MAX_ALIAS_NAME-1); + my_casedn_str(files_charset_info, d_name_buff); + d_name= d_name_buff; + } + } + + if (use_db_name) + { + append_identifier(thd, str, d_name, (uint)strlen(d_name)); + str->append('.'); + } + append_identifier(thd, str, t_name, (uint) strlen(t_name)); + str->append(')'); +} + + +/* Return last used value for sequence or NULL if sequence hasn't been used */ + +longlong Item_func_lastval::val_int() +{ + const char *key; + SEQUENCE_LAST_VALUE *entry; + uint length= get_table_def_key(table_list, &key); + THD *thd= table_list->table->in_use; + char buff[80]; + String key_buff(buff,sizeof(buff), &my_charset_bin); + DBUG_ENTER("Item_func_lastval::val_int"); + + if (table_list->table->s->tmp_table != NO_TMP_TABLE) + { + /* + Temporary tables has an extra \0 at end to distinguish it from + normal tables + */ + key_buff.copy(key, length, &my_charset_bin); + key_buff.append((char) 0); + key= key_buff.ptr(); + length++; + } + + if (!(entry= ((SEQUENCE_LAST_VALUE*) + my_hash_search(&thd->sequences, (uchar*) key, length)))) + { + /* Sequence not used */ + null_value= 1; + DBUG_RETURN(0); + } + if (entry->check_version(table_list->table)) + { + /* Table droped and re-created, remove current version */ + my_hash_delete(&thd->sequences, (uchar*) entry); + null_value= 1; + DBUG_RETURN(0); + } + + null_value= entry->null_value; + DBUG_RETURN(entry->value); +} + + +/* + Sets next value to be returned from sequences + + SELECT setval('foo', 42, 0); Next nextval will return 43 + SELECT setval('foo', 42, 0, true); Same as above + SELECT setval('foo', 42, 0, false); Next nextval will return 42 +*/ + +longlong Item_func_setval::val_int() +{ + longlong value; + int error; + TABLE *table= table_list->table; + DBUG_ASSERT(table && table->s->sequence); + DBUG_ENTER("Item_func_setval::val_int"); + + value= nextval; + error= table->s->sequence->set_value(table, nextval, round, is_used); + if (error) + { + null_value= 1; + value= 0; + } + DBUG_RETURN(value); +} + + +/* Print for setval */ + +void Item_func_setval::print(String *str, enum_query_type query_type) +{ + char d_name_buff[MAX_ALIAS_NAME], t_name_buff[MAX_ALIAS_NAME]; + const char *d_name= table_list->db, *t_name= table_list->table_name; + bool use_db_name= d_name && d_name[0]; + THD *thd= table_list->table->in_use; + + str->append(func_name()); + str->append('('); + + /* + for next_val we assume that table_list has been updated to contain + the current db. + */ + + if (lower_case_table_names > 0) + { + strmake(t_name_buff, t_name, MAX_ALIAS_NAME-1); + my_casedn_str(files_charset_info, t_name_buff); + t_name= t_name_buff; + if (use_db_name) + { + strmake(d_name_buff, d_name, MAX_ALIAS_NAME-1); + my_casedn_str(files_charset_info, d_name_buff); + d_name= d_name_buff; + } + } + + if (use_db_name) + { + append_identifier(thd, str, d_name, (uint)strlen(d_name)); + str->append('.'); + } + append_identifier(thd, str, t_name, (uint) strlen(t_name)); + str->append(','); + str->append_longlong(nextval); + str->append(','); + str->append_longlong(is_used); + str->append(','); + str->append_ulonglong(round); + str->append(')'); +} diff --git a/sql/item_func.h b/sql/item_func.h index bee6dc4e524..1d96c0d024e 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -35,21 +35,25 @@ class Item_func :public Item_func_or_sum { void sync_with_sum_func_and_with_field(List<Item> &list); protected: - /* - Allowed numbers of columns in result (usually 1, which means scalar value) - 0 means get this number from first argument - */ - uint allowed_arg_cols; String *val_str_from_val_str_ascii(String *str, String *str2); - void count_only_length(Item **item, uint nitems); - void count_real_length(Item **item, uint nitems); - void count_decimal_length(Item **item, uint nitems); - void count_datetime_length(enum_field_types field_type, - Item **item, uint nitems); - bool count_string_result_length(enum_field_types field_type, - Item **item, uint nitems); + virtual bool check_arguments() const + { + return check_argument_types_scalar(0, arg_count); + } + bool check_argument_types_like_args0() const; + bool check_argument_types_scalar(uint start, uint end) const; + bool check_argument_types_traditional_scalar(uint start, uint end) const; + bool check_argument_types_or_binary(const Type_handler *handler, + uint start, uint end) const; + bool check_argument_types_can_return_int(uint start, uint end) const; + bool check_argument_types_can_return_real(uint start, uint end) const; + bool check_argument_types_can_return_str(uint start, uint end) const; + bool check_argument_types_can_return_text(uint start, uint end) const; + bool check_argument_types_can_return_date(uint start, uint end) const; + bool check_argument_types_can_return_time(uint start, uint end) const; public: + table_map not_null_tables_cache; enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC, @@ -70,30 +74,30 @@ public: NEG_FUNC, GSYSVAR_FUNC, DYNCOL_FUNC, JSON_EXTRACT_FUNC }; enum Type type() const { return FUNC_ITEM; } virtual enum Functype functype() const { return UNKNOWN_FUNC; } - Item_func(THD *thd): Item_func_or_sum(thd), allowed_arg_cols(1) + Item_func(THD *thd): Item_func_or_sum(thd) { with_sum_func= 0; with_field= 0; } - Item_func(THD *thd, Item *a): Item_func_or_sum(thd, a), allowed_arg_cols(1) + Item_func(THD *thd, Item *a): Item_func_or_sum(thd, a) { with_sum_func= a->with_sum_func; with_field= a->with_field; } Item_func(THD *thd, Item *a, Item *b): - Item_func_or_sum(thd, a, b), allowed_arg_cols(1) + Item_func_or_sum(thd, a, b) { with_sum_func= a->with_sum_func || b->with_sum_func; with_field= a->with_field || b->with_field; } Item_func(THD *thd, Item *a, Item *b, Item *c): - Item_func_or_sum(thd, a, b, c), allowed_arg_cols(1) + Item_func_or_sum(thd, a, b, c) { with_sum_func= a->with_sum_func || b->with_sum_func || c->with_sum_func; with_field= a->with_field || b->with_field || c->with_field; } Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d): - Item_func_or_sum(thd, a, b, c, d), allowed_arg_cols(1) + Item_func_or_sum(thd, a, b, c, d) { with_sum_func= a->with_sum_func || b->with_sum_func || c->with_sum_func || d->with_sum_func; @@ -101,7 +105,7 @@ public: c->with_field || d->with_field; } Item_func(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e): - Item_func_or_sum(thd, a, b, c, d, e), allowed_arg_cols(1) + Item_func_or_sum(thd, a, b, c, d, e) { with_sum_func= a->with_sum_func || b->with_sum_func || c->with_sum_func || d->with_sum_func || e->with_sum_func; @@ -109,14 +113,13 @@ public: c->with_field || d->with_field || e->with_field; } Item_func(THD *thd, List<Item> &list): - Item_func_or_sum(thd, list), allowed_arg_cols(1) + Item_func_or_sum(thd, list) { set_arguments(thd, list); } // Constructor used for Item_cond_and/or (see Item comment) Item_func(THD *thd, Item_func *item): Item_func_or_sum(thd, item), - allowed_arg_cols(item->allowed_arg_cols), not_null_tables_cache(item->not_null_tables_cache) { } @@ -146,7 +149,6 @@ public: virtual Item *key_item() const { return args[0]; } void set_arguments(THD *thd, List<Item> &list) { - allowed_arg_cols= 1; Item_args::set_arguments(thd, list); sync_with_sum_func_and_with_field(list); list.empty(); // Fields are used @@ -174,11 +176,7 @@ public: void signal_divide_by_null(); friend class udf_handler; Field *create_field_for_create_select(TABLE *table) - { - return result_type() != STRING_RESULT ? - create_tmp_field(false, table, MY_INT32_NUM_DECIMAL_DIGITS) : - tmp_table_field_from_field_type(table, false, false); - } + { return tmp_table_field_from_field_type(table); } Item *get_tmp_table_item(THD *thd); my_decimal *val_decimal(my_decimal *); @@ -388,8 +386,7 @@ public: my_decimal *val_decimal(my_decimal *decimal_value); longlong val_int() { DBUG_ASSERT(fixed == 1); return (longlong) rint(val_real()); } - enum Item_result result_type () const { return REAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + const Type_handler *type_handler() const { return &type_handler_double; } void fix_length_and_dec() { decimals= NOT_FIXED_DEC; max_length= float_length(decimals); } }; @@ -399,10 +396,11 @@ public: Functions whose returned field type is determined at fix_fields() time. */ class Item_hybrid_func: public Item_func, - public Type_handler_hybrid_field_type + public Type_handler_hybrid_field_type, + public Type_geometry_attributes { protected: - void fix_attributes(Item **item, uint nitems); + bool fix_attributes(Item **item, uint nitems); public: Item_hybrid_func(THD *thd): Item_func(thd) { } Item_hybrid_func(THD *thd, Item *a): Item_func(thd, a) { } @@ -412,12 +410,14 @@ public: Item_hybrid_func(THD *thd, List<Item> &list): Item_func(thd, list) { } Item_hybrid_func(THD *thd, Item_hybrid_func *item) :Item_func(thd, item), Type_handler_hybrid_field_type(item) { } - enum_field_types field_type() const - { return Type_handler_hybrid_field_type::field_type(); } - enum Item_result result_type () const - { return Type_handler_hybrid_field_type::result_type(); } - enum Item_result cmp_type () const - { return Type_handler_hybrid_field_type::cmp_type(); } + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } + Field::geometry_type get_geometry_type() const + { return Type_geometry_attributes::get_geometry_type(); }; + void set_geometry_type(uint type) + { + Type_geometry_attributes::set_geometry_type(type); + } }; @@ -462,6 +462,62 @@ class Item_func_hybrid_field_type: public Item_hybrid_func DBUG_ASSERT((res != NULL) ^ null_value); return res; } + bool make_zero_mysql_time(MYSQL_TIME *ltime, ulonglong fuzzydate) + { + bzero(ltime, sizeof(*ltime)); + return null_value|= !(fuzzydate & TIME_FUZZY_DATES); + } + +public: + // Value methods that involve no conversion + String *val_str_from_str_op(String *str) + { + return str_op_with_null_check(&str_value); + } + my_decimal *val_decimal_from_decimal_op(my_decimal *dec) + { + return decimal_op_with_null_check(dec); + } + longlong val_int_from_int_op() + { + return int_op(); + } + double val_real_from_real_op() + { + return real_op(); + } + bool get_date_from_date_op(MYSQL_TIME *ltime, ulonglong fuzzydate) + { + return date_op(ltime, + fuzzydate | + (field_type() == MYSQL_TYPE_TIME ? TIME_TIME_ONLY : 0)); + } + + // Value methods that involve conversion + String *val_str_from_decimal_op(String *str); + String *val_str_from_real_op(String *str); + String *val_str_from_int_op(String *str); + String *val_str_from_date_op(String *str); + + my_decimal *val_decimal_from_str_op(my_decimal *dec); + my_decimal *val_decimal_from_real_op(my_decimal *dec); + my_decimal *val_decimal_from_int_op(my_decimal *dec); + my_decimal *val_decimal_from_date_op(my_decimal *dec); + + longlong val_int_from_str_op(); + longlong val_int_from_real_op(); + longlong val_int_from_decimal_op(); + longlong val_int_from_date_op(); + + double val_real_from_str_op(); + double val_real_from_decimal_op(); + double val_real_from_date_op(); + double val_real_from_int_op(); + + bool get_date_from_str_op(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date_from_real_op(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date_from_decimal_op(MYSQL_TIME *ltime, ulonglong fuzzydate); + bool get_date_from_int_op(MYSQL_TIME *ltime, ulonglong fuzzydate); public: Item_func_hybrid_field_type(THD *thd): @@ -480,11 +536,38 @@ public: Item_hybrid_func(thd, list) { collation.set_numeric(); } - double val_real(); - longlong val_int(); - my_decimal *val_decimal(my_decimal *); - String *val_str(String*str); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); + double val_real() + { + DBUG_ASSERT(fixed); + return Item_func_hybrid_field_type::type_handler()-> + Item_func_hybrid_field_type_val_real(this); + } + longlong val_int() + { + DBUG_ASSERT(fixed); + return Item_func_hybrid_field_type::type_handler()-> + Item_func_hybrid_field_type_val_int(this); + } + my_decimal *val_decimal(my_decimal *dec) + { + DBUG_ASSERT(fixed); + return Item_func_hybrid_field_type::type_handler()-> + Item_func_hybrid_field_type_val_decimal(this, dec); + } + String *val_str(String*str) + { + DBUG_ASSERT(fixed); + String *res= Item_func_hybrid_field_type::type_handler()-> + Item_func_hybrid_field_type_val_str(this, str); + DBUG_ASSERT(null_value == (res == NULL)); + return res; + } + bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) + { + DBUG_ASSERT(fixed); + return Item_func_hybrid_field_type::type_handler()-> + Item_func_hybrid_field_type_get_date(this, res, fuzzy_date); + } /** @brief Performs the operation that this functions implements when the @@ -532,6 +615,34 @@ public: }; +/* + This class resembles SQL standard CASE-alike expressions: + CASE and its abbreviations COALESCE, NULLIF, IFNULL, IF. + + <case expression> ::= <case abbreviation> + | <case specification> +*/ +class Item_func_case_expression: public Item_func_hybrid_field_type +{ +public: + Item_func_case_expression(THD *thd) + :Item_func_hybrid_field_type(thd) + { } + Item_func_case_expression(THD *thd, Item *a) + :Item_func_hybrid_field_type(thd, a) + { } + Item_func_case_expression(THD *thd, Item *a, Item *b) + :Item_func_hybrid_field_type(thd, a, b) + { } + Item_func_case_expression(THD *thd, Item *a, Item *b, Item *c) + :Item_func_hybrid_field_type(thd, a, b, c) + { } + Item_func_case_expression(THD *thd, List<Item> &list): + Item_func_hybrid_field_type(thd, list) + { } +}; + + class Item_func_numhybrid: public Item_func_hybrid_field_type { protected: @@ -568,7 +679,8 @@ class Item_func_num1: public Item_func_numhybrid public: Item_func_num1(THD *thd, Item *a): Item_func_numhybrid(thd, a) {} Item_func_num1(THD *thd, Item *a, Item *b): Item_func_numhybrid(thd, a, b) {} - void fix_length_and_dec(); + bool check_partition_func_processor(void *int_arg) { return FALSE; } + bool check_vcol_func_processor(void *arg) { return FALSE; } }; @@ -583,7 +695,32 @@ class Item_num_op :public Item_func_numhybrid { print_op(str, query_type); } - void fix_length_and_dec(); + bool fix_type_handler(const Type_aggregator *aggregator); + void fix_length_and_dec_double() + { + count_real_length(args, arg_count); + max_length= float_length(decimals); + } + void fix_length_and_dec_decimal() + { + unsigned_flag= args[0]->unsigned_flag & args[1]->unsigned_flag; + result_precision(); + fix_decimals(); + } + void fix_length_and_dec_int() + { + unsigned_flag= args[0]->unsigned_flag | args[1]->unsigned_flag; + result_precision(); + decimals= 0; + set_handler(type_handler_long_or_longlong()); + } + void fix_length_and_dec_temporal() + { + set_handler(&type_handler_newdecimal); + fix_length_and_dec_decimal(); + if (decimals == 0) + set_handler(type_handler_long_or_longlong()); + } bool need_parentheses_in_default() { return true; } }; @@ -591,6 +728,12 @@ class Item_num_op :public Item_func_numhybrid class Item_int_func :public Item_func { public: + /* + QQ: shouldn't 20 characters be enough: + Max unsigned = 18,446,744,073,709,551,615 = 20 digits, 20 characters + Max signed = 9,223,372,036,854,775,807 = 19 digits, 19 characters + Min signed = -9,223,372,036,854,775,808 = 19 digits, 20 characters + */ Item_int_func(THD *thd): Item_func(thd) { collation.set_numeric(); fix_char_length(21); } Item_int_func(THD *thd, Item *a): Item_func(thd, a) @@ -608,18 +751,82 @@ public: { collation.set_numeric(); } double val_real(); String *val_str(String*str); - enum Item_result result_type () const { return INT_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const= 0; void fix_length_and_dec() {} }; -class Item_func_connection_id :public Item_int_func +class Item_long_func: public Item_int_func +{ +public: + Item_long_func(THD *thd): Item_int_func(thd) { } + Item_long_func(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_long_func(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} + Item_long_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {} + Item_long_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { } + Item_long_func(THD *thd, Item_long_func *item) :Item_int_func(thd, item) {} + const Type_handler *type_handler() const { return &type_handler_long; } + void fix_length_and_dec() { max_length= 11; } +}; + + +class Item_longlong_func: public Item_int_func +{ +public: + Item_longlong_func(THD *thd): Item_int_func(thd) { } + Item_longlong_func(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_longlong_func(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} + Item_longlong_func(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {} + Item_longlong_func(THD *thd, Item *a, Item *b, Item *c, Item *d): + Item_int_func(thd, a, b, c, d) {} + Item_longlong_func(THD *thd, List<Item> &list): Item_int_func(thd, list) { } + Item_longlong_func(THD *thd, Item_longlong_func *item) :Item_int_func(thd, item) {} + const Type_handler *type_handler() const { return &type_handler_longlong; } +}; + + +class Cursor_ref +{ +protected: + LEX_CSTRING m_cursor_name; + uint m_cursor_offset; + class sp_cursor *get_open_cursor_or_error(); + Cursor_ref(const LEX_CSTRING *name, uint offset) + :m_cursor_name(*name), m_cursor_offset(offset) + { } + void print_func(String *str, const char *func_name); +}; + + + +class Item_func_cursor_rowcount: public Item_longlong_func, + public Cursor_ref +{ +public: + Item_func_cursor_rowcount(THD *thd, const LEX_CSTRING *name, uint offset) + :Item_longlong_func(thd), Cursor_ref(name, offset) { maybe_null= true; } + const char *func_name() const { return "%ROWCOUNT"; } + longlong val_int(); + bool check_vcol_func_processor(void *arg) + { + return mark_unsupported_function(func_name(), arg, VCOL_SESSION_FUNC); + } + void print(String *str, enum_query_type query_type) + { + return Cursor_ref::print_func(str, func_name()); + } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_cursor_rowcount>(thd, mem_root, this); } +}; + + + +class Item_func_connection_id :public Item_long_func { longlong value; public: - Item_func_connection_id(THD *thd): Item_int_func(thd) {} + Item_func_connection_id(THD *thd): Item_long_func(thd) {} const char *func_name() const { return "connection_id"; } void fix_length_and_dec(); bool fix_fields(THD *thd, Item **ref); @@ -641,13 +848,19 @@ public: unsigned_flag= 0; } const char *func_name() const { return "cast_as_signed"; } + const Type_handler *type_handler() const + { return type_handler_long_or_longlong(); } longlong val_int() { longlong value= args[0]->val_int_signed_typecast(); null_value= args[0]->null_value; return value; } - void fix_length_and_dec() + void fix_length_and_dec_double() + { + fix_char_length(MAX_BIGINT_WIDTH); + } + void fix_length_and_dec_generic() { uint32 char_length= MY_MIN(args[0]->max_char_length(), MY_INT64_NUM_DECIMAL_DIGITS); @@ -659,6 +872,23 @@ public: set_if_bigger(char_length, 1U + (unsigned_flag ? 0 : 1)); fix_char_length(char_length); } + void fix_length_and_dec_string() + { + /* + For strings, use decimal_int_part() instead of max_char_length(). + This is important for Item_hex_hybrid: + SELECT CAST(0x1FFFFFFFF AS SIGNED); + Length is 5, decimal_int_part() is 13. + */ + uint32 char_length= MY_MIN(args[0]->decimal_int_part(), + MY_INT64_NUM_DECIMAL_DIGITS); + set_if_bigger(char_length, 1U + (unsigned_flag ? 0 : 1)); + fix_char_length(char_length); + } + void fix_length_and_dec() + { + args[0]->type_handler()->Item_func_signed_fix_length_and_dec(this); + } virtual void print(String *str, enum_query_type query_type); uint decimal_precision() const { return args[0]->decimal_precision(); } bool need_parentheses_in_default() { return true; } @@ -675,12 +905,23 @@ public: unsigned_flag= 1; } const char *func_name() const { return "cast_as_unsigned"; } + const Type_handler *type_handler() const + { + if (max_char_length() <= MY_INT32_NUM_DECIMAL_DIGITS - 1) + return &type_handler_long; + return &type_handler_longlong; + } longlong val_int() { longlong value= args[0]->val_int_unsigned_typecast(); null_value= args[0]->null_value; return value; } + void fix_length_and_dec() + { + args[0]->type_handler()->Item_func_unsigned_fix_length_and_dec(this); + } + uint decimal_precision() const { return max_length; } virtual void print(String *str, enum_query_type query_type); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_unsigned>(thd, mem_root, this); } @@ -691,7 +932,8 @@ class Item_decimal_typecast :public Item_func { my_decimal decimal_value; public: - Item_decimal_typecast(THD *thd, Item *a, int len, int dec): Item_func(thd, a) + Item_decimal_typecast(THD *thd, Item *a, uint len, uint dec) + :Item_func(thd, a) { decimals= (uint8) dec; collation.set_numeric(); @@ -702,9 +944,12 @@ public: double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal*); - enum Item_result result_type () const { return DECIMAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; } - void fix_length_and_dec() {} + const Type_handler *type_handler() const { return &type_handler_newdecimal; } + void fix_length_and_dec_generic() {} + void fix_length_and_dec() + { + args[0]->type_handler()->Item_decimal_typecast_fix_length_and_dec(this); + } const char *func_name() const { return "decimal_typecast"; } virtual void print(String *str, enum_query_type query_type); bool need_parentheses_in_default() { return true; } @@ -716,15 +961,18 @@ public: class Item_double_typecast :public Item_real_func { public: - Item_double_typecast(THD *thd, Item *a, int len, int dec): + Item_double_typecast(THD *thd, Item *a, uint len, uint dec): Item_real_func(thd, a) { decimals= (uint8) dec; max_length= (uint32) len; } double val_real(); - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } - void fix_length_and_dec() { maybe_null= 1; } + void fix_length_and_dec_generic() { maybe_null= 1; } + void fix_length_and_dec() + { + args[0]->type_handler()->Item_double_typecast_fix_length_and_dec(this); + } const char *func_name() const { return "double_typecast"; } virtual void print(String *str, enum_query_type query_type); bool need_parentheses_in_default() { return true; } @@ -751,6 +999,7 @@ public: Item_func_additive_op(thd, a, b) {} const char *func_name() const { return "+"; } enum precedence precedence() const { return ADD_PRECEDENCE; } + void fix_length_and_dec(); longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); @@ -769,6 +1018,22 @@ public: double real_op(); my_decimal *decimal_op(my_decimal *); void fix_length_and_dec(); + void fix_unsigned_flag(); + void fix_length_and_dec_double() + { + Item_func_additive_op::fix_length_and_dec_double(); + fix_unsigned_flag(); + } + void fix_length_and_dec_decimal() + { + Item_func_additive_op::fix_length_and_dec_decimal(); + fix_unsigned_flag(); + } + void fix_length_and_dec_int() + { + Item_func_additive_op::fix_length_and_dec_int(); + fix_unsigned_flag(); + } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_minus>(thd, mem_root, this); } }; @@ -785,6 +1050,7 @@ public: double real_op(); my_decimal *decimal_op(my_decimal *); void result_precision(); + void fix_length_and_dec(); bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -803,6 +1069,8 @@ public: const char *func_name() const { return "/"; } enum precedence precedence() const { return MUL_PRECEDENCE; } void fix_length_and_dec(); + void fix_length_and_dec_double(); + void fix_length_and_dec_int(); void result_precision(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_div>(thd, mem_root, this); } @@ -817,6 +1085,8 @@ public: longlong val_int(); const char *func_name() const { return "DIV"; } enum precedence precedence() const { return MUL_PRECEDENCE; } + const Type_handler *type_handler() const + { return type_handler_long_or_longlong(); } void fix_length_and_dec(); void print(String *str, enum_query_type query_type) { @@ -838,10 +1108,27 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); - const char *func_name() const { return "%"; } + const char *func_name() const { return "MOD"; } enum precedence precedence() const { return MUL_PRECEDENCE; } void result_precision(); void fix_length_and_dec(); + void fix_length_and_dec_double() + { + Item_num_op::fix_length_and_dec_double(); + unsigned_flag= args[0]->unsigned_flag; + } + void fix_length_and_dec_decimal() + { + Item_num_op::fix_length_and_dec_decimal(); + unsigned_flag= args[0]->unsigned_flag; + } + void fix_length_and_dec_int() + { + max_length= MY_MAX(args[0]->max_length, args[1]->max_length); + decimals= 0; + unsigned_flag= args[0]->unsigned_flag; + set_handler(type_handler_long_or_longlong()); + } bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -864,10 +1151,11 @@ public: str->append(func_name()); args[0]->print_parenthesised(str, query_type, precedence()); } + void fix_length_and_dec_int(); + void fix_length_and_dec_double(); + void fix_length_and_dec_decimal(); void fix_length_and_dec(); uint decimal_precision() const { return args[0]->decimal_precision(); } - bool check_partition_func_processor(void *int_arg) {return FALSE;} - bool check_vcol_func_processor(void *arg) { return FALSE;} bool need_parentheses_in_default() { return true; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_neg>(thd, mem_root, this); } @@ -882,9 +1170,10 @@ public: longlong int_op(); my_decimal *decimal_op(my_decimal *); const char *func_name() const { return "abs"; } + void fix_length_and_dec_int(); + void fix_length_and_dec_double(); + void fix_length_and_dec_decimal(); void fix_length_and_dec(); - bool check_partition_func_processor(void *int_arg) {return FALSE;} - bool check_vcol_func_processor(void *arg) { return FALSE;} Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_abs>(thd, mem_root, this); } }; @@ -893,6 +1182,8 @@ public: class Item_dec_func :public Item_real_func { + bool check_arguments() const + { return check_argument_types_can_return_real(0, arg_count); } public: Item_dec_func(THD *thd, Item *a): Item_real_func(thd, a) {} Item_dec_func(THD *thd, Item *a, Item *b): Item_real_func(thd, a, b) {} @@ -1057,6 +1348,8 @@ class Item_func_int_val :public Item_func_num1 { public: Item_func_int_val(THD *thd, Item *a): Item_func_num1(thd, a) {} + void fix_length_and_dec_double(); + void fix_length_and_dec_int_or_decimal(); void fix_length_and_dec(); }; @@ -1069,8 +1362,6 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); - bool check_partition_func_processor(void *int_arg) {return FALSE;} - bool check_vcol_func_processor(void *arg) { return FALSE;} Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_ceiling>(thd, mem_root, this); } }; @@ -1084,25 +1375,31 @@ public: longlong int_op(); double real_op(); my_decimal *decimal_op(my_decimal *); - bool check_partition_func_processor(void *int_arg) {return FALSE;} - bool check_vcol_func_processor(void *arg) { return FALSE;} Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_floor>(thd, mem_root, this); } }; /* This handles round and truncate */ -class Item_func_round :public Item_func_num1 +class Item_func_round :public Item_func_numhybrid { bool truncate; + void fix_length_and_dec_decimal(uint decimals_to_set); + void fix_length_and_dec_double(uint decimals_to_set); public: Item_func_round(THD *thd, Item *a, Item *b, bool trunc_arg) - :Item_func_num1(thd, a, b), truncate(trunc_arg) {} + :Item_func_numhybrid(thd, a, b), truncate(trunc_arg) {} const char *func_name() const { return truncate ? "truncate" : "round"; } double real_op(); longlong int_op(); my_decimal *decimal_op(my_decimal *); - void fix_length_and_dec(); + void fix_arg_decimal(); + void fix_arg_int(); + void fix_arg_double(); + void fix_length_and_dec() + { + args[0]->type_handler()->Item_func_round_fix_length_and_dec(this); + } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_round>(thd, mem_root, this); } }; @@ -1112,6 +1409,8 @@ class Item_func_rand :public Item_real_func { struct my_rnd_struct *rand; bool first_eval; // TRUE if val_real() is called 1st time + bool check_arguments() const + { return check_argument_types_can_return_int(0, arg_count); } public: Item_func_rand(THD *thd, Item *a): Item_real_func(thd, a), rand(0), first_eval(TRUE) {} @@ -1133,11 +1432,15 @@ private: }; -class Item_func_sign :public Item_int_func +class Item_func_sign :public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_can_return_real(func_name()); } public: - Item_func_sign(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_sign(THD *thd, Item *a): Item_long_func(thd, a) {} const char *func_name() const { return "sign"; } + uint decimal_precision() const { return 1; } + void fix_length_and_dec() { fix_char_length(2); } longlong val_int(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_sign>(thd, mem_root, this); } @@ -1148,6 +1451,8 @@ class Item_func_units :public Item_real_func { char *name; double mul,add; + bool check_arguments() const + { return check_argument_types_can_return_real(0, arg_count); } public: Item_func_units(THD *thd, char *name_arg, Item *a, double mul_arg, double add_arg): @@ -1176,16 +1481,74 @@ class Item_func_min_max :public Item_hybrid_func { String tmp_value; int cmp_sign; +protected: + bool fix_attributes(Item **item, uint nitems); public: Item_func_min_max(THD *thd, List<Item> &list, int cmp_sign_arg): Item_hybrid_func(thd, list), cmp_sign(cmp_sign_arg) {} - double val_real(); - longlong val_int(); - String *val_str(String *); - my_decimal *val_decimal(my_decimal *); - bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); - void fix_length_and_dec(); + String *val_str_native(String *str); + double val_real_native(); + longlong val_int_native(); + my_decimal *val_decimal_native(my_decimal *); + bool get_date_native(MYSQL_TIME *res, ulonglong fuzzydate); + + double val_real() + { + DBUG_ASSERT(fixed); + return Item_func_min_max::type_handler()-> + Item_func_min_max_val_real(this); + } + longlong val_int() + { + DBUG_ASSERT(fixed); + return Item_func_min_max::type_handler()-> + Item_func_min_max_val_int(this); + } + String *val_str(String *str) + { + DBUG_ASSERT(fixed); + return Item_func_min_max::type_handler()-> + Item_func_min_max_val_str(this, str); + } + my_decimal *val_decimal(my_decimal *dec) + { + DBUG_ASSERT(fixed); + return Item_func_min_max::type_handler()-> + Item_func_min_max_val_decimal(this, dec); + } + bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) + { + DBUG_ASSERT(fixed); + return Item_func_min_max::type_handler()-> + Item_func_min_max_get_date(this, res, fuzzy_date); + } + void aggregate_attributes_real(Item **items, uint nitems) + { + /* + Aggregating attributes for the double data type for LEAST/GREATEST + is almost the same with aggregating for CASE-alike hybrid functions, + (CASE..THEN, COALESCE, IF, etc). + There is one notable difference though, when a numeric argument is mixed + with a string argument: + - CASE-alike functions return a string data type in such cases + COALESCE(10,'x') -> VARCHAR(2) = '10' + - LEAST/GREATEST returns double: + GREATEST(10,'10e4') -> DOUBLE = 100000 + As the string argument can represent a number in the scientific notation, + like in the example above, max_length of the result can be longer than + max_length of the arguments. To handle this properly, max_length is + additionally assigned to the result of float_length(decimals). + */ + Item_func::aggregate_attributes_real(items, nitems); + max_length= float_length(decimals); + } + void fix_length_and_dec() + { + if (aggregate_for_min_max(func_name(), args, arg_count)) + return; + fix_attributes(args, arg_count); + } }; class Item_func_min :public Item_func_min_max @@ -1218,7 +1581,6 @@ public: Item_func_rollup_const(THD *thd, Item *a): Item_func(thd, a) { name= a->name; - name_length= a->name_length; } double val_real() { return args[0]->val_real(); } longlong val_int() { return args[0]->val_int(); } @@ -1226,8 +1588,7 @@ public: my_decimal *val_decimal(my_decimal *dec) { return args[0]->val_decimal(dec); } const char *func_name() const { return "rollup_const"; } bool const_item() const { return 0; } - Item_result result_type() const { return args[0]->result_type(); } - enum_field_types field_type() const { return args[0]->field_type(); } + const Type_handler *type_handler() const { return args[0]->type_handler(); } void fix_length_and_dec() { collation= args[0]->collation; @@ -1241,45 +1602,59 @@ public: }; -class Item_func_length :public Item_int_func +class Item_long_func_length: public Item_long_func +{ + bool check_arguments() const + { return args[0]->check_type_can_return_str(func_name()); } +public: + Item_long_func_length(THD *thd, Item *a): Item_long_func(thd, a) {} + void fix_length_and_dec() { max_length=10; } +}; + + +class Item_func_octet_length :public Item_long_func_length { String value; public: - Item_func_length(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_octet_length(THD *thd, Item *a): Item_long_func_length(thd, a) {} longlong val_int(); - const char *func_name() const { return "length"; } - void fix_length_and_dec() { max_length=10; } + const char *func_name() const { return "octet_length"; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_length>(thd, mem_root, this); } + { return get_item_copy<Item_func_octet_length>(thd, mem_root, this); } }; -class Item_func_bit_length :public Item_func_length +class Item_func_bit_length :public Item_longlong_func { + String value; public: - Item_func_bit_length(THD *thd, Item *a): Item_func_length(thd, a) {} - longlong val_int() - { DBUG_ASSERT(fixed == 1); return Item_func_length::val_int()*8; } + Item_func_bit_length(THD *thd, Item *a): Item_longlong_func(thd, a) {} + void fix_length_and_dec() + { + max_length= 11; // 0x100000000*8 = 34,359,738,368 + } + longlong val_int(); const char *func_name() const { return "bit_length"; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_bit_length>(thd, mem_root, this); } }; -class Item_func_char_length :public Item_int_func +class Item_func_char_length :public Item_long_func_length { String value; public: - Item_func_char_length(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_char_length(THD *thd, Item *a): Item_long_func_length(thd, a) {} longlong val_int(); const char *func_name() const { return "char_length"; } - void fix_length_and_dec() { max_length=10; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_char_length>(thd, mem_root, this); } }; -class Item_func_coercibility :public Item_int_func +class Item_func_coercibility :public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_can_return_str(func_name()); } public: - Item_func_coercibility(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_coercibility(THD *thd, Item *a): Item_long_func(thd, a) {} longlong val_int(); const char *func_name() const { return "coercibility"; } void fix_length_and_dec() { max_length=10; maybe_null= 0; } @@ -1295,29 +1670,47 @@ public: { return get_item_copy<Item_func_coercibility>(thd, mem_root, this); } }; -class Item_func_locate :public Item_int_func + +/* + In the corner case LOCATE could return (4,294,967,296 + 1), + which would not fit into Item_long_func range. + But string lengths are limited with max_allowed_packet, + which cannot be bigger than 1024*1024*1024. +*/ +class Item_func_locate :public Item_long_func { + bool check_arguments() const + { + return check_argument_types_can_return_str(0, 2) || + (arg_count > 2 && args[2]->check_type_can_return_int(func_name())); + } String value1,value2; DTCollation cmp_collation; public: - Item_func_locate(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} - Item_func_locate(THD *thd, Item *a, Item *b, Item *c): Item_int_func(thd, a, b, c) {} + Item_func_locate(THD *thd, Item *a, Item *b) + :Item_long_func(thd, a, b) {} + Item_func_locate(THD *thd, Item *a, Item *b, Item *c) + :Item_long_func(thd, a, b, c) {} const char *func_name() const { return "locate"; } longlong val_int(); - void fix_length_and_dec(); + void fix_length_and_dec() + { + max_length= MY_INT32_NUM_DECIMAL_DIGITS; + agg_arg_charsets_for_comparison(cmp_collation, args, 2); + } virtual void print(String *str, enum_query_type query_type); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_locate>(thd, mem_root, this); } }; -class Item_func_field :public Item_int_func +class Item_func_field :public Item_long_func { String value,tmp; Item_result cmp_type; DTCollation cmp_collation; public: - Item_func_field(THD *thd, List<Item> &list): Item_int_func(thd, list) {} + Item_func_field(THD *thd, List<Item> &list): Item_long_func(thd, list) {} longlong val_int(); const char *func_name() const { return "field"; } void fix_length_and_dec(); @@ -1326,11 +1719,13 @@ public: }; -class Item_func_ascii :public Item_int_func +class Item_func_ascii :public Item_long_func { + bool check_arguments() const + { return check_argument_types_can_return_str(0, arg_count); } String value; public: - Item_func_ascii(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_ascii(THD *thd, Item *a): Item_long_func(thd, a) {} longlong val_int(); const char *func_name() const { return "ascii"; } void fix_length_and_dec() { max_length=3; } @@ -1338,26 +1733,31 @@ public: { return get_item_copy<Item_func_ascii>(thd, mem_root, this); } }; -class Item_func_ord :public Item_int_func +class Item_func_ord :public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_can_return_str(func_name()); } String value; public: - Item_func_ord(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_ord(THD *thd, Item *a): Item_long_func(thd, a) {} + void fix_length_and_dec() { fix_char_length(7); } longlong val_int(); const char *func_name() const { return "ord"; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_ord>(thd, mem_root, this); } }; -class Item_func_find_in_set :public Item_int_func +class Item_func_find_in_set :public Item_long_func { + bool check_arguments() const + { return check_argument_types_can_return_str(0, 2); } String value,value2; uint enum_value; ulonglong enum_bit; DTCollation cmp_collation; public: Item_func_find_in_set(THD *thd, Item *a, Item *b): - Item_int_func(thd, a, b), enum_value(0) {} + Item_long_func(thd, a, b), enum_value(0) {} longlong val_int(); const char *func_name() const { return "find_in_set"; } void fix_length_and_dec(); @@ -1367,11 +1767,13 @@ public: /* Base class for all bit functions: '~', '|', '^', '&', '>>', '<<' */ -class Item_func_bit: public Item_int_func +class Item_func_bit: public Item_longlong_func { + bool check_arguments() const + { return check_argument_types_can_return_int(0, arg_count); } public: - Item_func_bit(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} - Item_func_bit(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_bit(THD *thd, Item *a, Item *b): Item_longlong_func(thd, a, b) {} + Item_func_bit(THD *thd, Item *a): Item_longlong_func(thd, a) {} void fix_length_and_dec() { unsigned_flag= 1; } virtual inline void print(String *str, enum_query_type query_type) @@ -1403,10 +1805,12 @@ public: { return get_item_copy<Item_func_bit_and>(thd, mem_root, this); } }; -class Item_func_bit_count :public Item_int_func +class Item_func_bit_count :public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_can_return_int(func_name()); } public: - Item_func_bit_count(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_bit_count(THD *thd, Item *a): Item_long_func(thd, a) {} longlong val_int(); const char *func_name() const { return "bit_count"; } void fix_length_and_dec() { max_length=2; } @@ -1453,19 +1857,20 @@ public: }; -class Item_func_last_insert_id :public Item_int_func +class Item_func_last_insert_id :public Item_longlong_func { + bool check_arguments() const + { return check_argument_types_can_return_int(0, arg_count); } public: - Item_func_last_insert_id(THD *thd): Item_int_func(thd) {} - Item_func_last_insert_id(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_last_insert_id(THD *thd): Item_longlong_func(thd) {} + Item_func_last_insert_id(THD *thd, Item *a): Item_longlong_func(thd, a) {} longlong val_int(); const char *func_name() const { return "last_insert_id"; } void fix_length_and_dec() { - unsigned_flag= TRUE; + unsigned_flag= true; if (arg_count) max_length= args[0]->max_length; - unsigned_flag=1; } bool fix_fields(THD *thd, Item **ref); bool check_vcol_func_processor(void *arg) @@ -1477,11 +1882,16 @@ public: }; -class Item_func_benchmark :public Item_int_func +class Item_func_benchmark :public Item_long_func { + bool check_arguments() const + { + return args[0]->check_type_can_return_int(func_name()) || + args[1]->check_type_scalar(func_name()); + } public: Item_func_benchmark(THD *thd, Item *count_expr, Item *expr): - Item_int_func(thd, count_expr, expr) + Item_long_func(thd, count_expr, expr) {} longlong val_int(); const char *func_name() const { return "benchmark"; } @@ -1499,10 +1909,13 @@ public: void item_func_sleep_init(void); void item_func_sleep_free(void); -class Item_func_sleep :public Item_int_func +class Item_func_sleep :public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_can_return_real(func_name()); } public: - Item_func_sleep(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_sleep(THD *thd, Item *a): Item_long_func(thd, a) {} + void fix_length_and_dec() { fix_char_length(1); } bool const_item() const { return 0; } const char *func_name() const { return "sleep"; } table_map used_tables() const @@ -1604,7 +2017,6 @@ public: } } void cleanup(); - Item_result result_type () const { return udf.result_type(); } bool eval_not_null_tables(void *opt_arg) { not_null_tables_cache= 0; @@ -1642,7 +2054,7 @@ class Item_func_udf_float :public Item_udf_func } double val_real(); String *val_str(String *str); - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + const Type_handler *type_handler() const { return &type_handler_double; } void fix_length_and_dec() { fix_num_length_and_dec(); } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_udf_float>(thd, mem_root, this); } @@ -1660,8 +2072,7 @@ public: longlong val_int(); double val_real() { return (double) Item_func_udf_int::val_int(); } String *val_str(String *str); - enum Item_result result_type () const { return INT_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const { return &type_handler_longlong; } void fix_length_and_dec() { decimals= 0; max_length= 21; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_udf_int>(thd, mem_root, this); } @@ -1679,8 +2090,7 @@ public: double val_real(); my_decimal *val_decimal(my_decimal *); String *val_str(String *str); - enum Item_result result_type () const { return DECIMAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; } + const Type_handler *type_handler() const { return &type_handler_newdecimal; } void fix_length_and_dec() { fix_num_length_and_dec(); } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_udf_decimal>(thd, mem_root, this); } @@ -1719,8 +2129,7 @@ public: string2my_decimal(E_DEC_FATAL_ERROR, res, dec_buf); return dec_buf; } - enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return string_field_type(); } + const Type_handler *type_handler() const { return string_type_handler(); } void fix_length_and_dec(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_udf_str>(thd, mem_root, this); } @@ -1746,6 +2155,7 @@ public: Item_int_func(thd) {} Item_func_udf_int(THD *thd, udf_func *udf_arg, List<Item> &list): Item_int_func(thd, list) {} + const Type_handler *type_handler() const { return &type_handler_longlong; } longlong val_int() { DBUG_ASSERT(fixed == 1); return 0; } }; @@ -1757,6 +2167,7 @@ public: Item_int_func(thd) {} Item_func_udf_decimal(THD *thd, udf_func *udf_arg, List<Item> &list): Item_int_func(thd, list) {} + const Type_handler *type_handler() const { return &type_handler_longlong; } my_decimal *val_decimal(my_decimal *) { DBUG_ASSERT(fixed == 1); return 0; } }; @@ -1772,7 +2183,6 @@ public: { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } double val_real() { DBUG_ASSERT(fixed == 1); null_value= 1; return 0.0; } longlong val_int() { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } - enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec() { maybe_null=1; max_length=0; } }; @@ -1781,11 +2191,16 @@ public: void mysql_ull_cleanup(THD *thd); void mysql_ull_set_explicit_lock_duration(THD *thd); -class Item_func_get_lock :public Item_int_func +class Item_func_get_lock :public Item_long_func { + bool check_arguments() const + { + return args[0]->check_type_general_purpose_string(func_name()) || + args[1]->check_type_can_return_real(func_name()); + } String value; public: - Item_func_get_lock(THD *thd, Item *a, Item *b) :Item_int_func(thd, a, b) {} + Item_func_get_lock(THD *thd, Item *a, Item *b) :Item_long_func(thd, a, b) {} longlong val_int(); const char *func_name() const { return "get_lock"; } void fix_length_and_dec() { max_length=1; maybe_null=1;} @@ -1803,11 +2218,13 @@ class Item_func_get_lock :public Item_int_func { return get_item_copy<Item_func_get_lock>(thd, mem_root, this); } }; -class Item_func_release_lock :public Item_int_func +class Item_func_release_lock :public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_general_purpose_string(func_name()); } String value; public: - Item_func_release_lock(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_release_lock(THD *thd, Item *a): Item_long_func(thd, a) {} longlong val_int(); const char *func_name() const { return "release_lock"; } void fix_length_and_dec() { max_length= 1; maybe_null= 1;} @@ -1827,15 +2244,24 @@ public: /* replication functions */ -class Item_master_pos_wait :public Item_int_func +class Item_master_pos_wait :public Item_longlong_func { + bool check_arguments() const + { + return + args[0]->check_type_general_purpose_string(func_name()) || + args[1]->check_type_can_return_int(func_name()) || + (arg_count > 2 && args[2]->check_type_can_return_int(func_name())) || + (arg_count > 3 && args[3]->check_type_general_purpose_string(func_name())); + } String value; public: - Item_master_pos_wait(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} + Item_master_pos_wait(THD *thd, Item *a, Item *b) + :Item_longlong_func(thd, a, b) {} Item_master_pos_wait(THD *thd, Item *a, Item *b, Item *c): - Item_int_func(thd, a, b, c) {} + Item_longlong_func(thd, a, b, c) {} Item_master_pos_wait(THD *thd, Item *a, Item *b, Item *c, Item *d): - Item_int_func(thd, a, b, c, d) {} + Item_longlong_func(thd, a, b, c, d) {} longlong val_int(); const char *func_name() const { return "master_pos_wait"; } void fix_length_and_dec() { max_length=21; maybe_null=1;} @@ -1848,12 +2274,19 @@ public: }; -class Item_master_gtid_wait :public Item_int_func +class Item_master_gtid_wait :public Item_long_func { + bool check_arguments() const + { + return args[0]->check_type_general_purpose_string(func_name()) || + (arg_count > 1 && args[1]->check_type_can_return_real(func_name())); + } String value; public: - Item_master_gtid_wait(THD *thd, Item *a): Item_int_func(thd, a) {} - Item_master_gtid_wait(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} + Item_master_gtid_wait(THD *thd, Item *a) + :Item_long_func(thd, a) {} + Item_master_gtid_wait(THD *thd, Item *a, Item *b) + :Item_long_func(thd, a, b) {} longlong val_int(); const char *func_name() const { return "master_gtid_wait"; } void fix_length_and_dec() { max_length=2; } @@ -1879,14 +2312,18 @@ class Item_func_user_var :public Item_hybrid_func protected: user_var_entry *m_var_entry; public: - LEX_STRING name; // keep it public - Item_func_user_var(THD *thd, LEX_STRING a) - :Item_hybrid_func(thd), m_var_entry(NULL), name(a) { } - Item_func_user_var(THD *thd, LEX_STRING a, Item *b) - :Item_hybrid_func(thd, b), m_var_entry(NULL), name(a) { } + LEX_CSTRING name; // keep it public + Item_func_user_var(THD *thd, const LEX_CSTRING *a) + :Item_hybrid_func(thd), m_var_entry(NULL), name(*a) { } + Item_func_user_var(THD *thd, const LEX_CSTRING *a, Item *b) + :Item_hybrid_func(thd, b), m_var_entry(NULL), name(*a) { } Item_func_user_var(THD *thd, Item_func_user_var *item) :Item_hybrid_func(thd, item), m_var_entry(item->m_var_entry), name(item->name) { } + Field *create_tmp_field(bool group, TABLE *table) + { return create_table_field_from_handler(table); } + Field *create_field_for_create_select(TABLE *table) + { return create_table_field_from_handler(table); } bool check_vcol_func_processor(void *arg); }; @@ -1917,7 +2354,7 @@ class Item_func_set_user_var :public Item_func_user_var } save_result; public: - Item_func_set_user_var(THD *thd, LEX_STRING a, Item *b): + Item_func_set_user_var(THD *thd, const LEX_CSTRING *a, Item *b): Item_func_user_var(thd, a, b), entry_thread_id(0) {} @@ -1941,19 +2378,13 @@ public: bool is_null_result(); bool update_hash(void *ptr, uint length, enum Item_result type, CHARSET_INFO *cs, bool unsigned_arg); - bool send(Protocol *protocol, String *str_arg); + bool send(Protocol *protocol, st_value *buffer); void make_field(THD *thd, Send_field *tmp_field); bool check(bool use_result_field); void save_item_result(Item *item); bool update(); bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); - Field *create_field_for_create_select(TABLE *table) - { - return result_type() != STRING_RESULT ? - create_tmp_field(false, table, MY_INT32_NUM_DECIMAL_DIGITS) : - tmp_table_field_from_field_type(table, false, true); - } void print(String *str, enum_query_type query_type); enum precedence precedence() const { return ASSIGN_PRECEDENCE; } void print_as_stmt(String *str, enum_query_type query_type); @@ -1980,10 +2411,10 @@ class Item_func_get_user_var :public Item_func_user_var, private Settable_routine_parameter { public: - Item_func_get_user_var(THD *thd, LEX_STRING a): + Item_func_get_user_var(THD *thd, const LEX_CSTRING *a): Item_func_user_var(thd, a) {} enum Functype functype() const { return GUSERVAR_FUNC; } - LEX_STRING get_name() { return name; } + LEX_CSTRING get_name() { return name; } double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal*); @@ -2023,11 +2454,15 @@ public: */ class Item_user_var_as_out_param :public Item { - LEX_STRING name; + LEX_CSTRING name; user_var_entry *entry; public: - Item_user_var_as_out_param(THD *thd, LEX_STRING a): Item(thd), name(a) - { set_name(thd, a.str, 0, system_charset_info); } + Item_user_var_as_out_param(THD *thd, const LEX_CSTRING *a) + :Item(thd), name(*a) + { + DBUG_ASSERT(a->length < UINT_MAX32); + set_name(thd, a->str, (uint) a->length, system_charset_info); + } /* We should return something different from FIELD_ITEM here */ enum Type type() const { return STRING_ITEM;} double val_real(); @@ -2039,7 +2474,7 @@ public: void print_for_load(THD *thd, String *str); void set_null_value(CHARSET_INFO* cs); void set_value(const char *str, uint length, CHARSET_INFO* cs); - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + const Type_handler *type_handler() const { return &type_handler_double; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_user_var_as_out_param>(thd, mem_root, this); } }; @@ -2055,7 +2490,7 @@ class Item_func_get_system_var :public Item_func { sys_var *var; enum_var_type var_type, orig_var_type; - LEX_STRING component; + LEX_CSTRING component; longlong cached_llval; double cached_dval; String cached_strval; @@ -2066,7 +2501,7 @@ class Item_func_get_system_var :public Item_func public: Item_func_get_system_var(THD *thd, sys_var *var_arg, enum_var_type var_type_arg, - LEX_STRING *component_arg, const char *name_arg, + LEX_CSTRING *component_arg, const char *name_arg, size_t name_len_arg); enum Functype functype() const { return GSYSVAR_FUNC; } void update_null_value(); @@ -2074,8 +2509,7 @@ public: void print(String *str, enum_query_type query_type); bool const_item() const { return true; } table_map used_tables() const { return 0; } - enum Item_result result_type() const; - enum_field_types field_type() const; + const Type_handler *type_handler() const; double val_real(); longlong val_int(); String* val_str(String*); @@ -2202,11 +2636,13 @@ public: { return get_item_copy<Item_func_bit_xor>(thd, mem_root, this); } }; -class Item_func_is_free_lock :public Item_int_func +class Item_func_is_free_lock :public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_general_purpose_string(func_name()); } String value; public: - Item_func_is_free_lock(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_is_free_lock(THD *thd, Item *a): Item_long_func(thd, a) {} longlong val_int(); const char *func_name() const { return "is_free_lock"; } void fix_length_and_dec() { decimals=0; max_length=1; maybe_null=1;} @@ -2218,11 +2654,13 @@ public: { return get_item_copy<Item_func_is_free_lock>(thd, mem_root, this); } }; -class Item_func_is_used_lock :public Item_int_func +class Item_func_is_used_lock :public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_general_purpose_string(func_name()); } String value; public: - Item_func_is_used_lock(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_is_used_lock(THD *thd, Item *a): Item_long_func(thd, a) {} longlong val_int(); const char *func_name() const { return "is_used_lock"; } void fix_length_and_dec() { decimals=0; max_length=10; maybe_null=1;} @@ -2234,47 +2672,44 @@ public: { return get_item_copy<Item_func_is_used_lock>(thd, mem_root, this); } }; -/* For type casts */ - -enum Cast_target -{ - ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT, - ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR, - ITEM_CAST_DECIMAL, ITEM_CAST_DOUBLE -}; - struct Lex_cast_type_st: public Lex_length_and_dec_st { private: - Cast_target m_type; + const Type_handler *m_type_handler; public: - void set(Cast_target type, const char *length, const char *dec) + void set(const Type_handler *handler, const char *length, const char *dec) { - m_type= type; + m_type_handler= handler; Lex_length_and_dec_st::set(length, dec); } - void set(Cast_target type, Lex_length_and_dec_st length_and_dec) + void set(const Type_handler *handler, Lex_length_and_dec_st length_and_dec) { - m_type= type; + m_type_handler= handler; Lex_length_and_dec_st::operator=(length_and_dec); } - void set(Cast_target type, const char *length) + void set(const Type_handler *handler, const char *length) + { + set(handler, length, 0); + } + void set(const Type_handler *handler) { - set(type, length, 0); + set(handler, 0, 0); } - void set(Cast_target type) + const Type_handler *type_handler() const { return m_type_handler; } + Item *create_typecast_item(THD *thd, Item *item, CHARSET_INFO *cs= NULL) { - set(type, 0, 0); + return m_type_handler-> + create_typecast_item(thd, item, + Type_cast_attributes(length(), dec(), cs)); } - Cast_target type() const { return m_type; } }; -class Item_func_row_count :public Item_int_func +class Item_func_row_count :public Item_longlong_func { public: - Item_func_row_count(THD *thd): Item_int_func(thd) {} + Item_func_row_count(THD *thd): Item_longlong_func(thd) {} longlong val_int(); const char *func_name() const { return "row_count"; } void fix_length_and_dec() { decimals= 0; maybe_null=0; } @@ -2312,12 +2747,17 @@ private: bool execute(); bool execute_impl(THD *thd); - bool init_result_field(THD *thd); + bool init_result_field(THD *thd, sp_head *sp); protected: bool is_expensive_processor(void *arg) { return is_expensive(); } - + + bool check_arguments() const + { + // sp_prepare_func_item() checks that the number of columns is correct + return false; + } public: Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name); @@ -2334,18 +2774,16 @@ public: const char *func_name() const; - enum enum_field_types field_type() const; + const Type_handler *type_handler() const; Field *create_field_for_create_select(TABLE *table) { return result_type() != STRING_RESULT ? sp_result_field : - tmp_table_field_from_field_type(table, false, false); + tmp_table_field_from_field_type(table); } void make_field(THD *thd, Send_field *tmp_field); - Item_result result_type() const; - longlong val_int() { if (execute()) @@ -2405,6 +2843,10 @@ public: { return sp_result_field; } + const sp_name *get_sp_name() const + { + return m_name; + } bool check_vcol_func_processor(void *arg); bool limit_index_condition_pushdown_processor(void *opt_arg) @@ -2428,10 +2870,10 @@ public: }; -class Item_func_found_rows :public Item_int_func +class Item_func_found_rows :public Item_longlong_func { public: - Item_func_found_rows(THD *thd): Item_int_func(thd) {} + Item_func_found_rows(THD *thd): Item_longlong_func(thd) {} longlong val_int(); const char *func_name() const { return "found_rows"; } void fix_length_and_dec() { decimals= 0; maybe_null=0; } @@ -2444,12 +2886,55 @@ public: }; +class Item_func_oracle_sql_rowcount :public Item_longlong_func +{ +public: + Item_func_oracle_sql_rowcount(THD *thd): Item_longlong_func(thd) {} + longlong val_int(); + const char *func_name() const { return "SQL%ROWCOUNT"; } + void print(String *str, enum_query_type query_type) + { + str->append(func_name()); + } + bool check_vcol_func_processor(void *arg) + { + return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); + } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_oracle_sql_rowcount>(thd, mem_root, this); } +}; + + +class Item_func_sqlcode: public Item_long_func +{ +public: + Item_func_sqlcode(THD *thd): Item_long_func(thd) { } + longlong val_int(); + const char *func_name() const { return "SQLCODE"; } + void print(String *str, enum_query_type query_type) + { + str->append(func_name()); + } + bool check_vcol_func_processor(void *arg) + { + return mark_unsupported_function(func_name(), "()", arg, VCOL_IMPOSSIBLE); + } + void fix_length_and_dec() + { + maybe_null= null_value= false; + max_length= 11; + } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_sqlcode>(thd, mem_root, this); } +}; + + void uuid_short_init(); -class Item_func_uuid_short :public Item_int_func +class Item_func_uuid_short :public Item_longlong_func { public: - Item_func_uuid_short(THD *thd): Item_int_func(thd) {} + Item_func_uuid_short(THD *thd): Item_longlong_func(thd) {} const char *func_name() const { return "uuid_short"; } longlong val_int(); void fix_length_and_dec() @@ -2475,14 +2960,13 @@ public: String *val_str(String *); my_decimal *val_decimal(my_decimal *); void fix_length_and_dec(); - enum Item_result result_type () const { return last_value->result_type(); } const char *func_name() const { return "last_value"; } + const Type_handler *type_handler() const { return last_value->type_handler(); } bool eval_not_null_tables(void *) { not_null_tables_cache= 0; return 0; } - enum_field_types field_type() const { return last_value->field_type(); } bool const_item() const { return 0; } void evaluate_sideeffects(); void update_used_tables() @@ -2495,11 +2979,73 @@ public: }; -Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name, - LEX_STRING component); -extern bool check_reserved_words(LEX_STRING *name); -extern enum_field_types agg_field_type(Item **items, uint nitems, - bool treat_bit_as_number); +/* Implementation for sequences: NEXT VALUE FOR sequence and NEXTVAL() */ + +class Item_func_nextval :public Item_longlong_func +{ +protected: + TABLE_LIST *table_list; +public: + Item_func_nextval(THD *thd, TABLE_LIST *table): + Item_longlong_func(thd), table_list(table) {} + longlong val_int(); + const char *func_name() const { return "nextval"; } + void fix_length_and_dec() + { + unsigned_flag= 0; + max_length= MAX_BIGINT_WIDTH; + maybe_null= 1; /* In case of errors */ + } + bool const_item() const { return 0; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_nextval>(thd, mem_root, this); } + void print(String *str, enum_query_type query_type); + bool check_vcol_func_processor(void *arg) + { + return mark_unsupported_function(func_name(), "()", arg, + VCOL_NON_DETERMINISTIC); + } +}; + + +/* Implementation for sequences: LASTVAL(sequence), PostgreSQL style */ + +class Item_func_lastval :public Item_func_nextval +{ +public: + Item_func_lastval(THD *thd, TABLE_LIST *table): + Item_func_nextval(thd, table) {} + longlong val_int(); + const char *func_name() const { return "lastval"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_lastval>(thd, mem_root, this); } +}; + + +/* Implementation for sequences: SETVAL(sequence), PostgreSQL style */ + +class Item_func_setval :public Item_func_nextval +{ + longlong nextval; + ulonglong round; + bool is_used; +public: + Item_func_setval(THD *thd, TABLE_LIST *table, longlong nextval_arg, + ulonglong round_arg, bool is_used_arg) + : Item_func_nextval(thd, table), + nextval(nextval_arg), round(round_arg), is_used(is_used_arg) + {} + longlong val_int(); + const char *func_name() const { return "setval"; } + void print(String *str, enum_query_type query_type); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_setval>(thd, mem_root, this); } +}; + + +Item *get_system_var(THD *thd, enum_var_type var_type, LEX_CSTRING name, + LEX_CSTRING component); +extern bool check_reserved_words(const LEX_CSTRING *name); Item *find_date_time_item(Item **args, uint nargs, uint col); double my_double_round(double value, longlong dec, bool dec_unsigned, bool truncate); diff --git a/sql/item_geofunc.cc b/sql/item_geofunc.cc index 3f0efbfa871..04952739e85 100644 --- a/sql/item_geofunc.cc +++ b/sql/item_geofunc.cc @@ -40,20 +40,11 @@ #include "opt_range.h" -Field *Item_geometry_func::create_field_for_create_select(TABLE *t_arg) -{ - Field *result; - if ((result= new Field_geom(max_length, maybe_null, name, t_arg->s, - get_geometry_type()))) - result->init(t_arg); - return result; -} - void Item_geometry_func::fix_length_and_dec() { collation.set(&my_charset_bin); decimals=0; - max_length= (uint32) 4294967295U; + max_length= (uint32) UINT_MAX32; maybe_null= 1; } @@ -223,7 +214,7 @@ String *Item_func_as_wkt::val_str_ascii(String *str) void Item_func_as_wkt::fix_length_and_dec() { collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); - max_length=MAX_BLOB_WIDTH; + max_length= (uint32) UINT_MAX32; maybe_null= 1; } diff --git a/sql/item_geofunc.h b/sql/item_geofunc.h index 199bc1f47de..d332d067c37 100644 --- a/sql/item_geofunc.h +++ b/sql/item_geofunc.h @@ -39,12 +39,163 @@ public: Item_str_func(thd, a, b, c) {} Item_geometry_func(THD *thd, List<Item> &list): Item_str_func(thd, list) {} void fix_length_and_dec(); - enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; } - Field *create_field_for_create_select(TABLE *table); + const Type_handler *type_handler() const { return &type_handler_geometry; } }; + +/* + Functions returning REAL measurements of a single GEOMETRY argument +*/ +class Item_real_func_args_geometry: public Item_real_func +{ +protected: + String value; + bool check_arguments() const + { + DBUG_ASSERT(arg_count == 1); + return args[0]->check_type_or_binary(func_name(), &type_handler_geometry); + } +public: + Item_real_func_args_geometry(THD *thd, Item *a) + :Item_real_func(thd, a) {} +}; + + +/* + Functions returning INT measurements of a single GEOMETRY argument +*/ +class Item_long_func_args_geometry: public Item_long_func +{ + bool check_arguments() const + { + DBUG_ASSERT(arg_count == 1); + return args[0]->check_type_or_binary(func_name(), &type_handler_geometry); + } +protected: + String value; +public: + Item_long_func_args_geometry(THD *thd, Item *a) + :Item_long_func(thd, a) {} +}; + + +/* + Functions returning BOOL measurements of a single GEOMETRY argument +*/ +class Item_bool_func_args_geometry: public Item_bool_func +{ +protected: + String value; + bool check_arguments() const + { + DBUG_ASSERT(arg_count == 1); + return args[0]->check_type_or_binary(func_name(), &type_handler_geometry); + } +public: + Item_bool_func_args_geometry(THD *thd, Item *a) + :Item_bool_func(thd, a) {} +}; + + +/* + Functions returning ASCII string measurements of a single GEOMETRY argument +*/ +class Item_str_ascii_func_args_geometry: public Item_str_ascii_func +{ +protected: + bool check_arguments() const + { + DBUG_ASSERT(arg_count >= 1); + return args[0]->check_type_or_binary(func_name(), &type_handler_geometry); + } +public: + Item_str_ascii_func_args_geometry(THD *thd, Item *a) + :Item_str_ascii_func(thd, a) {} + Item_str_ascii_func_args_geometry(THD *thd, Item *a, Item *b) + :Item_str_ascii_func(thd, a, b) {} + Item_str_ascii_func_args_geometry(THD *thd, Item *a, Item *b, Item *c) + :Item_str_ascii_func(thd, a, b, c) {} +}; + + +/* + Functions returning binary string measurements of a single GEOMETRY argument +*/ +class Item_binary_func_args_geometry: public Item_str_func +{ +protected: + bool check_arguments() const + { + DBUG_ASSERT(arg_count >= 1); + return args[0]->check_type_or_binary(func_name(), &type_handler_geometry); + } +public: + Item_binary_func_args_geometry(THD *thd, Item *a) + :Item_str_func(thd, a) {} +}; + + +/* + Functions returning GEOMETRY measurements of a single GEOEMETRY argument +*/ +class Item_geometry_func_args_geometry: public Item_geometry_func +{ +protected: + bool check_arguments() const + { + DBUG_ASSERT(arg_count >= 1); + return args[0]->check_type_or_binary(func_name(), &type_handler_geometry); + } +public: + Item_geometry_func_args_geometry(THD *thd, Item *a) + :Item_geometry_func(thd, a) {} + Item_geometry_func_args_geometry(THD *thd, Item *a, Item *b) + :Item_geometry_func(thd, a, b) {} +}; + + +/* + Functions returning REAL result relationships between two GEOMETRY arguments +*/ +class Item_real_func_args_geometry_geometry: public Item_real_func +{ +protected: + bool check_arguments() const + { + DBUG_ASSERT(arg_count >= 2); + return check_argument_types_or_binary(&type_handler_geometry, 0, 2); + } +public: + Item_real_func_args_geometry_geometry(THD *thd, Item *a, Item *b) + :Item_real_func(thd, a, b) {} +}; + + +/* + Functions returning BOOL result relationships between two GEOMETRY arguments +*/ +class Item_bool_func_args_geometry_geometry: public Item_bool_func +{ +protected: + String value; + bool check_arguments() const + { + DBUG_ASSERT(arg_count >= 2); + return check_argument_types_or_binary(&type_handler_geometry, 0, 2); + } +public: + Item_bool_func_args_geometry_geometry(THD *thd, Item *a, Item *b, Item *c) + :Item_bool_func(thd, a, b, c) {} +}; + + class Item_func_geometry_from_text: public Item_geometry_func { + bool check_arguments() const + { + return args[0]->check_type_general_purpose_string(func_name()) || + check_argument_types_can_return_int(1, MY_MIN(2, arg_count)); + } public: Item_func_geometry_from_text(THD *thd, Item *a): Item_geometry_func(thd, a) {} Item_func_geometry_from_text(THD *thd, Item *a, Item *srid): @@ -57,6 +208,11 @@ public: class Item_func_geometry_from_wkb: public Item_geometry_func { + bool check_arguments() const + { + return args[0]->check_type_or_binary(func_name(), &type_handler_geometry) || + check_argument_types_can_return_int(1, MY_MIN(2, arg_count)); + } public: Item_func_geometry_from_wkb(THD *thd, Item *a): Item_geometry_func(thd, a) {} Item_func_geometry_from_wkb(THD *thd, Item *a, Item *srid): @@ -71,6 +227,12 @@ public: class Item_func_geometry_from_json: public Item_geometry_func { String tmp_js; + bool check_arguments() const + { + // TODO: check with Alexey, for better args[1] and args[2] type control + return args[0]->check_type_general_purpose_string(func_name()) || + check_argument_types_traditional_scalar(1, MY_MIN(3, arg_count)); + } public: Item_func_geometry_from_json(THD *thd, Item *js): Item_geometry_func(thd, js) {} Item_func_geometry_from_json(THD *thd, Item *js, Item *opt): @@ -84,10 +246,11 @@ public: }; -class Item_func_as_wkt: public Item_str_ascii_func +class Item_func_as_wkt: public Item_str_ascii_func_args_geometry { public: - Item_func_as_wkt(THD *thd, Item *a): Item_str_ascii_func(thd, a) {} + Item_func_as_wkt(THD *thd, Item *a) + :Item_str_ascii_func_args_geometry(thd, a) {} const char *func_name() const { return "st_astext"; } String *val_str_ascii(String *); void fix_length_and_dec(); @@ -95,26 +258,41 @@ public: { return get_item_copy<Item_func_as_wkt>(thd, mem_root, this); } }; -class Item_func_as_wkb: public Item_geometry_func +class Item_func_as_wkb: public Item_binary_func_args_geometry { public: - Item_func_as_wkb(THD *thd, Item *a): Item_geometry_func(thd, a) {} + Item_func_as_wkb(THD *thd, Item *a) + :Item_binary_func_args_geometry(thd, a) {} const char *func_name() const { return "st_aswkb"; } String *val_str(String *); - enum_field_types field_type() const { return MYSQL_TYPE_BLOB; } + const Type_handler *type_handler() const { return &type_handler_long_blob; } + void fix_length_and_dec() + { + collation.set(&my_charset_bin); + decimals=0; + max_length= (uint32) UINT_MAX32; + maybe_null= 1; + } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_as_wkb>(thd, mem_root, this); } }; -class Item_func_as_geojson: public Item_str_ascii_func +class Item_func_as_geojson: public Item_str_ascii_func_args_geometry { + bool check_arguments() const + { + // TODO: check with Alexey, for better args[1] and args[2] type control + return Item_str_ascii_func_args_geometry::check_arguments() || + check_argument_types_traditional_scalar(1, MY_MIN(3, arg_count)); + } public: - Item_func_as_geojson(THD *thd, Item *js): Item_str_ascii_func(thd, js) {} - Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits): - Item_str_ascii_func(thd, js, max_dec_digits) {} - Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits, Item *opt): - Item_str_ascii_func(thd, js, max_dec_digits, opt) {} + Item_func_as_geojson(THD *thd, Item *js) + :Item_str_ascii_func_args_geometry(thd, js) {} + Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits) + :Item_str_ascii_func_args_geometry(thd, js, max_dec_digits) {} + Item_func_as_geojson(THD *thd, Item *js, Item *max_dec_digits, Item *opt) + :Item_str_ascii_func_args_geometry(thd, js, max_dec_digits, opt) {} const char *func_name() const { return "st_asgeojson"; } void fix_length_and_dec(); String *val_str_ascii(String *); @@ -123,10 +301,11 @@ public: }; -class Item_func_geometry_type: public Item_str_ascii_func +class Item_func_geometry_type: public Item_str_ascii_func_args_geometry { public: - Item_func_geometry_type(THD *thd, Item *a): Item_str_ascii_func(thd, a) {} + Item_func_geometry_type(THD *thd, Item *a) + :Item_str_ascii_func_args_geometry(thd, a) {} String *val_str_ascii(String *); const char *func_name() const { return "st_geometrytype"; } void fix_length_and_dec() @@ -141,7 +320,7 @@ public: // #define HEAVY_CONVEX_HULL -class Item_func_convexhull: public Item_geometry_func +class Item_func_convexhull: public Item_geometry_func_args_geometry { class ch_node: public Gcalc_dyn_list::Item { @@ -164,7 +343,8 @@ class Item_func_convexhull: public Item_geometry_func ch_node *new_ch_node() { return (ch_node *) res_heap.new_item(); } int add_node_to_line(ch_node **p_cur, int dir, const Gcalc_heap::Info *pi); public: - Item_func_convexhull(THD *thd, Item *a): Item_geometry_func(thd, a), + Item_func_convexhull(THD *thd, Item *a) + :Item_geometry_func_args_geometry(thd, a), res_heap(8192, sizeof(ch_node)) {} const char *func_name() const { return "st_convexhull"; } @@ -174,10 +354,11 @@ public: }; -class Item_func_centroid: public Item_geometry_func +class Item_func_centroid: public Item_geometry_func_args_geometry { public: - Item_func_centroid(THD *thd, Item *a): Item_geometry_func(thd, a) {} + Item_func_centroid(THD *thd, Item *a) + :Item_geometry_func_args_geometry(thd, a) {} const char *func_name() const { return "st_centroid"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; @@ -185,10 +366,11 @@ public: { return get_item_copy<Item_func_centroid>(thd, mem_root, this); } }; -class Item_func_envelope: public Item_geometry_func +class Item_func_envelope: public Item_geometry_func_args_geometry { public: - Item_func_envelope(THD *thd, Item *a): Item_geometry_func(thd, a) {} + Item_func_envelope(THD *thd, Item *a) + :Item_geometry_func_args_geometry(thd, a) {} const char *func_name() const { return "st_envelope"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; @@ -197,7 +379,7 @@ public: }; -class Item_func_boundary: public Item_geometry_func +class Item_func_boundary: public Item_geometry_func_args_geometry { class Transporter : public Gcalc_shape_transporter { @@ -222,7 +404,8 @@ class Item_func_boundary: public Item_geometry_func }; Gcalc_result_receiver res_receiver; public: - Item_func_boundary(THD *thd, Item *a): Item_geometry_func(thd, a) {} + Item_func_boundary(THD *thd, Item *a) + :Item_geometry_func_args_geometry(thd, a) {} const char *func_name() const { return "st_boundary"; } String *val_str(String *); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -232,6 +415,8 @@ public: class Item_func_point: public Item_geometry_func { + bool check_arguments() const + { return check_argument_types_can_return_real(0, 2); } public: Item_func_point(THD *thd, Item *a, Item *b): Item_geometry_func(thd, a, b) {} Item_func_point(THD *thd, Item *a, Item *b, Item *srid): @@ -243,12 +428,12 @@ public: { return get_item_copy<Item_func_point>(thd, mem_root, this); } }; -class Item_func_spatial_decomp: public Item_geometry_func +class Item_func_spatial_decomp: public Item_geometry_func_args_geometry { enum Functype decomp_func; public: Item_func_spatial_decomp(THD *thd, Item *a, Item_func::Functype ft): - Item_geometry_func(thd, a) { decomp_func = ft; } + Item_geometry_func_args_geometry(thd, a) { decomp_func = ft; } const char *func_name() const { switch (decomp_func) @@ -269,12 +454,19 @@ public: { return get_item_copy<Item_func_spatial_decomp>(thd, mem_root, this); } }; -class Item_func_spatial_decomp_n: public Item_geometry_func +class Item_func_spatial_decomp_n: public Item_geometry_func_args_geometry { enum Functype decomp_func_n; + bool check_arguments() const + { + return Item_geometry_func_args_geometry::check_arguments() || + args[1]->check_type_can_return_int(func_name()); + } public: - Item_func_spatial_decomp_n(THD *thd, Item *a, Item *b, Item_func::Functype ft): - Item_geometry_func(thd, a, b) { decomp_func_n = ft; } + Item_func_spatial_decomp_n(THD *thd, Item *a, Item *b, Item_func::Functype ft) + :Item_geometry_func_args_geometry(thd, a, b), + decomp_func_n(ft) + { } const char *func_name() const { switch (decomp_func_n) @@ -297,6 +489,10 @@ public: class Item_func_spatial_collection: public Item_geometry_func { + bool check_arguments() const + { + return check_argument_types_or_binary(&type_handler_geometry, 0, arg_count); + } enum Geometry::wkbType coll_type; enum Geometry::wkbType item_type; public: @@ -342,6 +538,11 @@ protected: SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *param, Field *field, KEY_PART *key_part, Item_func::Functype type, Item *value); + bool check_arguments() const + { + DBUG_ASSERT(arg_count >= 2); + return check_argument_types_or_binary(&type_handler_geometry, 0, 2); + } public: Item_func_spatial_rel(THD *thd, Item *a, Item *b, enum Functype sp_rel): Item_bool_func2_with_rev(thd, a, b), spatial_rel(sp_rel) @@ -403,15 +604,20 @@ public: }; -class Item_func_spatial_relate: public Item_bool_func +class Item_func_spatial_relate: public Item_bool_func_args_geometry_geometry { Gcalc_heap collector; Gcalc_scan_iterator scan_it; Gcalc_function func; String tmp_value1, tmp_value2, tmp_matrix; + bool check_arguments() const + { + return Item_bool_func_args_geometry_geometry::check_arguments() || + args[2]->check_type_general_purpose_string(func_name()); + } public: Item_func_spatial_relate(THD *thd, Item *a, Item *b, Item *matrix): - Item_bool_func(thd, a, b, matrix) + Item_bool_func_args_geometry_geometry(thd, a, b, matrix) { } longlong val_int(); const char *func_name() const { return "st_relate"; } @@ -427,6 +633,11 @@ public: class Item_func_spatial_operation: public Item_geometry_func { + bool check_arguments() const + { + DBUG_ASSERT(arg_count >= 2); + return check_argument_types_or_binary(&type_handler_geometry, 0, 2); + } public: Gcalc_function::op_type spatial_op; Gcalc_heap collector; @@ -452,8 +663,13 @@ public: }; -class Item_func_buffer: public Item_geometry_func +class Item_func_buffer: public Item_geometry_func_args_geometry { + bool check_arguments() const + { + return Item_geometry_func_args_geometry::check_arguments() || + args[1]->check_type_can_return_real(func_name()); + } protected: class Transporter : public Gcalc_operation_transporter { @@ -495,8 +711,8 @@ protected: Gcalc_operation_reducer operation; public: - Item_func_buffer(THD *thd, Item *obj, Item *distance): - Item_geometry_func(thd, obj, distance) {} + Item_func_buffer(THD *thd, Item *obj, Item *distance) + :Item_geometry_func_args_geometry(thd, obj, distance) {} const char *func_name() const { return "st_buffer"; } String *val_str(String *); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -504,10 +720,11 @@ public: }; -class Item_func_isempty: public Item_bool_func +class Item_func_isempty: public Item_bool_func_args_geometry { public: - Item_func_isempty(THD *thd, Item *a): Item_bool_func(thd, a) {} + Item_func_isempty(THD *thd, Item *a) + :Item_bool_func_args_geometry(thd, a) {} longlong val_int(); const char *func_name() const { return "st_isempty"; } void fix_length_and_dec() { maybe_null= 1; } @@ -516,14 +733,15 @@ public: { return get_item_copy<Item_func_isempty>(thd, mem_root, this); } }; -class Item_func_issimple: public Item_int_func +class Item_func_issimple: public Item_long_func_args_geometry { Gcalc_heap collector; Gcalc_function func; Gcalc_scan_iterator scan_it; String tmp; public: - Item_func_issimple(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_issimple(THD *thd, Item *a) + :Item_long_func_args_geometry(thd, a) {} longlong val_int(); const char *func_name() const { return "st_issimple"; } void fix_length_and_dec() { decimals=0; max_length=2; } @@ -532,10 +750,11 @@ public: { return get_item_copy<Item_func_issimple>(thd, mem_root, this); } }; -class Item_func_isclosed: public Item_int_func +class Item_func_isclosed: public Item_long_func_args_geometry { public: - Item_func_isclosed(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_isclosed(THD *thd, Item *a) + :Item_long_func_args_geometry(thd, a) {} longlong val_int(); const char *func_name() const { return "st_isclosed"; } void fix_length_and_dec() { decimals=0; max_length=2; } @@ -554,11 +773,11 @@ public: { return get_item_copy<Item_func_isring>(thd, mem_root, this); } }; -class Item_func_dimension: public Item_int_func +class Item_func_dimension: public Item_long_func_args_geometry { - String value; public: - Item_func_dimension(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_dimension(THD *thd, Item *a) + :Item_long_func_args_geometry(thd, a) {} longlong val_int(); const char *func_name() const { return "st_dimension"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } @@ -566,11 +785,11 @@ public: { return get_item_copy<Item_func_dimension>(thd, mem_root, this); } }; -class Item_func_x: public Item_real_func + +class Item_func_x: public Item_real_func_args_geometry { - String value; public: - Item_func_x(THD *thd, Item *a): Item_real_func(thd, a) {} + Item_func_x(THD *thd, Item *a): Item_real_func_args_geometry(thd, a) {} double val_real(); const char *func_name() const { return "st_x"; } void fix_length_and_dec() @@ -583,11 +802,10 @@ public: }; -class Item_func_y: public Item_real_func +class Item_func_y: public Item_real_func_args_geometry { - String value; public: - Item_func_y(THD *thd, Item *a): Item_real_func(thd, a) {} + Item_func_y(THD *thd, Item *a): Item_real_func_args_geometry(thd, a) {} double val_real(); const char *func_name() const { return "st_y"; } void fix_length_and_dec() @@ -600,11 +818,11 @@ public: }; -class Item_func_numgeometries: public Item_int_func +class Item_func_numgeometries: public Item_long_func_args_geometry { - String value; public: - Item_func_numgeometries(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_numgeometries(THD *thd, Item *a) + :Item_long_func_args_geometry(thd, a) {} longlong val_int(); const char *func_name() const { return "st_numgeometries"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } @@ -613,11 +831,11 @@ public: }; -class Item_func_numinteriorring: public Item_int_func +class Item_func_numinteriorring: public Item_long_func_args_geometry { - String value; public: - Item_func_numinteriorring(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_numinteriorring(THD *thd, Item *a) + :Item_long_func_args_geometry(thd, a) {} longlong val_int(); const char *func_name() const { return "st_numinteriorrings"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } @@ -626,11 +844,11 @@ public: }; -class Item_func_numpoints: public Item_int_func +class Item_func_numpoints: public Item_long_func_args_geometry { - String value; public: - Item_func_numpoints(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_numpoints(THD *thd, Item *a) + :Item_long_func_args_geometry(thd, a) {} longlong val_int(); const char *func_name() const { return "st_numpoints"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } @@ -639,11 +857,10 @@ public: }; -class Item_func_area: public Item_real_func +class Item_func_area: public Item_real_func_args_geometry { - String value; public: - Item_func_area(THD *thd, Item *a): Item_real_func(thd, a) {} + Item_func_area(THD *thd, Item *a): Item_real_func_args_geometry(thd, a) {} double val_real(); const char *func_name() const { return "st_area"; } void fix_length_and_dec() @@ -656,11 +873,12 @@ public: }; -class Item_func_glength: public Item_real_func +class Item_func_glength: public Item_real_func_args_geometry { String value; public: - Item_func_glength(THD *thd, Item *a): Item_real_func(thd, a) {} + Item_func_glength(THD *thd, Item *a) + :Item_real_func_args_geometry(thd, a) {} double val_real(); const char *func_name() const { return "st_length"; } void fix_length_and_dec() @@ -673,11 +891,11 @@ public: }; -class Item_func_srid: public Item_int_func +class Item_func_srid: public Item_long_func_args_geometry { - String value; public: - Item_func_srid(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_srid(THD *thd, Item *a) + :Item_long_func_args_geometry(thd, a) {} longlong val_int(); const char *func_name() const { return "srid"; } void fix_length_and_dec() { max_length= 10; maybe_null= 1; } @@ -686,7 +904,7 @@ public: }; -class Item_func_distance: public Item_real_func +class Item_func_distance: public Item_real_func_args_geometry_geometry { String tmp_value1; String tmp_value2; @@ -694,7 +912,8 @@ class Item_func_distance: public Item_real_func Gcalc_function func; Gcalc_scan_iterator scan_it; public: - Item_func_distance(THD *thd, Item *a, Item *b): Item_real_func(thd, a, b) {} + Item_func_distance(THD *thd, Item *a, Item *b) + :Item_real_func_args_geometry_geometry(thd, a, b) {} double val_real(); const char *func_name() const { return "st_distance"; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -702,14 +921,15 @@ public: }; -class Item_func_pointonsurface: public Item_geometry_func +class Item_func_pointonsurface: public Item_geometry_func_args_geometry { String tmp_value; Gcalc_heap collector; Gcalc_function func; Gcalc_scan_iterator scan_it; public: - Item_func_pointonsurface(THD *thd, Item *a): Item_geometry_func(thd, a) {} + Item_func_pointonsurface(THD *thd, Item *a) + :Item_geometry_func_args_geometry(thd, a) {} const char *func_name() const { return "st_pointonsurface"; } String *val_str(String *); Field::geometry_type get_geometry_type() const; @@ -719,11 +939,12 @@ public: #ifndef DBUG_OFF -class Item_func_gis_debug: public Item_int_func +class Item_func_gis_debug: public Item_long_func { public: - Item_func_gis_debug(THD *thd, Item *a): Item_int_func(thd, a) + Item_func_gis_debug(THD *thd, Item *a): Item_long_func(thd, a) { null_value= false; } + void fix_length_and_dec() { fix_char_length(10); } const char *func_name() const { return "st_gis_debug"; } longlong val_int(); bool check_vcol_func_processor(void *arg) diff --git a/sql/item_inetfunc.cc b/sql/item_inetfunc.cc index 7f3ec3829f4..9aaa64823f7 100644 --- a/sql/item_inetfunc.cc +++ b/sql/item_inetfunc.cc @@ -217,7 +217,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres { DBUG_PRINT("error", ("str_to_ipv4(%.*s): " "invalid IPv4 address: too short.", - str_length, str)); + (int) str_length, str)); return false; } @@ -225,7 +225,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres { DBUG_PRINT("error", ("str_to_ipv4(%.*s): " "invalid IPv4 address: too long.", - str_length, str)); + (int) str_length, str)); return false; } @@ -248,7 +248,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres { DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " "too many characters in a group.", - str_length, str)); + (int) str_length, str)); return false; } @@ -258,7 +258,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres { DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " "invalid byte value.", - str_length, str)); + (int) str_length, str)); return false; } } @@ -268,7 +268,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres { DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " "too few characters in a group.", - str_length, str)); + (int) str_length, str)); return false; } @@ -281,7 +281,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres if (dot_count > 3) { DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " - "too many dots.", str_length, str)); + "too many dots.", (int) str_length, str)); return false; } } @@ -289,7 +289,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres { DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " "invalid character at pos %d.", - str_length, str, (int) (p - str))); + (int) str_length, str, (int) (p - str))); return false; } } @@ -297,7 +297,7 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres if (c == '.') { DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " - "ending at '.'.", str_length, str)); + "ending at '.'.", (int) str_length, str)); return false; } @@ -305,14 +305,14 @@ static bool str_to_ipv4(const char *str, size_t str_length, in_addr *ipv4_addres { DBUG_PRINT("error", ("str_to_ipv4(%.*s): invalid IPv4 address: " "too few groups.", - str_length, str)); + (int) str_length, str)); return false; } ipv4_bytes[3]= (unsigned char) byte_value; DBUG_PRINT("info", ("str_to_ipv4(%.*s): valid IPv4 address: %d.%d.%d.%d", - str_length, str, + (int) str_length, str, ipv4_bytes[0], ipv4_bytes[1], ipv4_bytes[2], ipv4_bytes[3])); return true; diff --git a/sql/item_inetfunc.h b/sql/item_inetfunc.h index 741b9f7d997..13ce003a374 100644 --- a/sql/item_inetfunc.h +++ b/sql/item_inetfunc.h @@ -24,10 +24,12 @@ Item_func_inet_aton implements INET_ATON() SQL-function. *************************************************************************/ -class Item_func_inet_aton : public Item_int_func +class Item_func_inet_aton : public Item_longlong_func { + bool check_arguments() const + { return check_argument_types_can_return_text(0, arg_count); } public: - Item_func_inet_aton(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_inet_aton(THD *thd, Item *a): Item_longlong_func(thd, a) {} longlong val_int(); const char *func_name() const { return "inet_aton"; } void fix_length_and_dec() diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 77363f80919..2f4c1ef8e46 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -390,7 +390,7 @@ longlong Item_func_json_valid::val_int() void Item_func_json_exists::fix_length_and_dec() { - Item_int_func::fix_length_and_dec(); + Item_bool_func::fix_length_and_dec(); maybe_null= 1; path.set_constant_flag(args[1]->const_item()); } @@ -941,7 +941,7 @@ void Item_func_json_contains::fix_length_and_dec() maybe_null= 1; if (arg_count > 2) path.set_constant_flag(args[2]->const_item()); - Item_int_func::fix_length_and_dec(); + Item_bool_func::fix_length_and_dec(); } @@ -1186,7 +1186,7 @@ void Item_func_json_contains_path::fix_length_and_dec() ooa_parsed= FALSE; maybe_null= 1; mark_constant_paths(paths, args+2, arg_count-2); - Item_int_func::fix_length_and_dec(); + Item_bool_func::fix_length_and_dec(); } @@ -1309,6 +1309,7 @@ longlong Item_func_json_contains_path::val_int() longlong result; json_path_t p; int n_found; + LINT_INIT(n_found); if ((null_value= args[0]->null_value)) return 0; @@ -2051,6 +2052,7 @@ String *Item_func_json_merge::val_str(String *str) json_engine_t je1, je2; String *js1= args[0]->val_json(&tmp_js1), *js2; uint n_arg; + LINT_INIT(js2); if (args[0]->null_value) goto null_return; @@ -2114,6 +2116,7 @@ void Item_func_json_length::fix_length_and_dec() if (arg_count > 1) path.set_constant_flag(args[1]->const_item()); maybe_null= 1; + max_length= 10; } diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index 927b60015b8..77e7588be25 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -40,27 +40,26 @@ public: }; -class Item_func_json_valid: public Item_int_func +class Item_func_json_valid: public Item_bool_func { protected: String tmp_value; public: - Item_func_json_valid(THD *thd, Item *json) : Item_int_func(thd, json) {} + Item_func_json_valid(THD *thd, Item *json) : Item_bool_func(thd, json) {} longlong val_int(); const char *func_name() const { return "json_valid"; } void fix_length_and_dec() { - Item_int_func::fix_length_and_dec(); + Item_bool_func::fix_length_and_dec(); maybe_null= 1; } - bool is_bool_type() { return true; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_json_valid>(thd, mem_root, this); } }; -class Item_func_json_exists: public Item_int_func +class Item_func_json_exists: public Item_bool_func { protected: json_path_with_flags path; @@ -68,9 +67,8 @@ protected: public: Item_func_json_exists(THD *thd, Item *js, Item *i_path): - Item_int_func(thd, js, i_path) {} + Item_bool_func(thd, js, i_path) {} const char *func_name() const { return "json_exists"; } - bool is_bool_type() { return true; } void fix_length_and_dec(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_json_exists>(thd, mem_root, this); } @@ -175,7 +173,7 @@ public: }; -class Item_func_json_contains: public Item_int_func +class Item_func_json_contains: public Item_bool_func { protected: String tmp_js; @@ -185,7 +183,7 @@ protected: String tmp_val, *val; public: Item_func_json_contains(THD *thd, List<Item> &list): - Item_int_func(thd, list) {} + Item_bool_func(thd, list) {} const char *func_name() const { return "json_contains"; } void fix_length_and_dec(); longlong val_int(); @@ -194,7 +192,7 @@ public: }; -class Item_func_json_contains_path: public Item_int_func +class Item_func_json_contains_path: public Item_bool_func { protected: String tmp_js; @@ -206,7 +204,7 @@ protected: public: Item_func_json_contains_path(THD *thd, List<Item> &list): - Item_int_func(thd, list), tmp_paths(0) {} + Item_bool_func(thd, list), tmp_paths(0) {} const char *func_name() const { return "json_contains_path"; } bool fix_fields(THD *thd, Item **ref); void fix_length_and_dec(); @@ -295,15 +293,21 @@ public: }; -class Item_func_json_length: public Item_int_func +class Item_func_json_length: public Item_long_func { + bool check_arguments() const + { + return args[0]->check_type_can_return_text(func_name()) || + (arg_count > 1 && + args[1]->check_type_general_purpose_string(func_name())); + } protected: json_path_with_flags path; String tmp_js; String tmp_path; public: Item_func_json_length(THD *thd, List<Item> &list): - Item_int_func(thd, list) {} + Item_long_func(thd, list) {} const char *func_name() const { return "json_length"; } void fix_length_and_dec(); longlong val_int(); @@ -312,13 +316,16 @@ public: }; -class Item_func_json_depth: public Item_int_func +class Item_func_json_depth: public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_can_return_text(func_name()); } protected: String tmp_js; public: - Item_func_json_depth(THD *thd, Item *js): Item_int_func(thd, js) {} + Item_func_json_depth(THD *thd, Item *js): Item_long_func(thd, js) {} const char *func_name() const { return "json_depth"; } + void fix_length_and_dec() { max_length= 10; } longlong val_int(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_json_depth>(thd, mem_root, this); } diff --git a/sql/item_row.h b/sql/item_row.h index cd58e15fe8b..9c92e19caab 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -56,6 +56,7 @@ public: {} enum Type type() const { return ROW_ITEM; }; + const Type_handler *type_handler() const { return &type_handler_row; } void illegal_method_call(const char *); bool is_null() { return null_value; } void make_field(THD *thd, Send_field *) @@ -89,13 +90,6 @@ public: List<Item> &fields, uint flags); table_map used_tables() const { return used_tables_cache; }; bool const_item() const { return const_item_cache; }; - enum Item_result result_type() const { return ROW_RESULT; } - Item_result cmp_type() const { return ROW_RESULT; } - enum_field_types field_type() const - { - DBUG_ASSERT(0); - return MYSQL_TYPE_DOUBLE; - } void update_used_tables() { used_tables_and_const_cache_init(); @@ -113,7 +107,7 @@ public: Item *transform(THD *thd, Item_transformer transformer, uchar *arg); bool eval_not_null_tables(void *opt_arg); - uint cols() { return arg_count; } + uint cols() const { return arg_count; } Item* element_index(uint i) { return args[i]; } Item** addr(uint i) { return args + i; } bool check_cols(uint c); diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 89e55234482..69f8ff185b5 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -457,7 +457,7 @@ String *Item_func_from_base64::val_str(String *str) THD *thd= current_thd; push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_BASE64_DATA, ER_THD(thd, ER_BAD_BASE64_DATA), - end_ptr - res->ptr()); + (int) (end_ptr - res->ptr())); goto err; } @@ -554,125 +554,85 @@ String *Item_func_decode_histogram::val_str(String *str) String *Item_func_concat::val_str(String *str) { DBUG_ASSERT(fixed == 1); - String *res,*res2,*use_as_buff; + THD *thd= current_thd; + String *res,*use_as_buff; uint i; bool is_const= 0; null_value=0; - if (!(res=args[0]->val_str(str))) + if (!(res= arg_val_str(0, str, &is_const))) goto null; use_as_buff= &tmp_value; - is_const= args[0]->const_item(); for (i=1 ; i < arg_count ; i++) { if (res->length() == 0) { - if (!(res=args[i]->val_str(str))) - goto null; /* CONCAT accumulates its result in the result of its the first non-empty argument. Because of this we need is_const to be evaluated only for it. */ - is_const= args[i]->const_item(); + if (!(res= arg_val_str(i, str, &is_const))) + goto null; } else { + const String *res2; if (!(res2=args[i]->val_str(use_as_buff))) - goto null; + goto null; if (res2->length() == 0) - continue; - if (res->length()+res2->length() > - current_thd->variables.max_allowed_packet) - { - THD *thd= current_thd; - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_WARN_ALLOWED_PACKET_OVERFLOWED, - ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED), - func_name(), - thd->variables.max_allowed_packet); - goto null; - } - if (!is_const && res->alloced_length() >= res->length()+res2->length()) - { // Use old buffer - res->append(*res2); - } - else if (str->alloced_length() >= res->length()+res2->length()) - { - if (str->ptr() == res2->ptr()) - str->replace(0,0,*res); - else - { - str->copy(*res); - str->append(*res2); - } - res= str; - use_as_buff= &tmp_value; - } - else if (res == &tmp_value) - { - if (res->append(*res2)) // Must be a blob - goto null; - } - else if (res2 == &tmp_value) - { // This can happend only 1 time - if (tmp_value.replace(0,0,*res)) - goto null; - res= &tmp_value; - use_as_buff=str; // Put next arg here - } - else if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() && - res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length()) - { - /* - This happens really seldom: - In this case res2 is sub string of tmp_value. We will - now work in place in tmp_value to set it to res | res2 - */ - /* Chop the last characters in tmp_value that isn't in res2 */ - tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) + - res2->length()); - /* Place res2 at start of tmp_value, remove chars before res2 */ - if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()), - *res)) - goto null; - res= &tmp_value; - use_as_buff=str; // Put next arg here - } - else - { // Two big const strings - /* - NOTE: We should be prudent in the initial allocation unit -- the - size of the arguments is a function of data distribution, which - can be any. Instead of overcommitting at the first row, we grow - the allocated amount by the factor of 2. This ensures that no - more than 25% of memory will be overcommitted on average. - */ + continue; + if (!(res= append_value(thd, res, is_const, str, &use_as_buff, res2))) + goto null; + is_const= 0; + } + } + res->set_charset(collation.collation); + return res; - uint concat_len= res->length() + res2->length(); +null: + null_value=1; + return 0; +} - if (tmp_value.alloced_length() < concat_len) - { - if (tmp_value.alloced_length() == 0) - { - if (tmp_value.alloc(concat_len)) - goto null; - } - else - { - uint new_len = MY_MAX(tmp_value.alloced_length() * 2, concat_len); - if (tmp_value.realloc(new_len)) - goto null; - } - } +String *Item_func_concat_operator_oracle::val_str(String *str) +{ + THD *thd= current_thd; + DBUG_ASSERT(fixed == 1); + String *res, *use_as_buff; + uint i; + bool is_const= false; + + null_value= 0; + // Search first non null argument + for (i= 0; i < arg_count; i++) + { + if ((res= arg_val_str(i, str, &is_const))) + break; + } + if (i == arg_count) + goto null; - if (tmp_value.copy(*res) || tmp_value.append(*res2)) - goto null; + use_as_buff= &tmp_value; - res= &tmp_value; - use_as_buff=str; - } + for (i++ ; i < arg_count ; i++) + { + if (res->length() == 0) + { + // See comments in Item_func_concat::val_str() + String *tmp; + if (!(tmp= arg_val_str(i, str, &is_const))) + continue; + res= tmp; + } + else + { + const String *res2; + if (!(res2= args[i]->val_str(use_as_buff)) || res2->length() == 0) + continue; + if (!(res= append_value(thd, res, is_const, str, &use_as_buff, res2))) + goto null; is_const= 0; } } @@ -680,11 +640,123 @@ String *Item_func_concat::val_str(String *str) return res; null: - null_value=1; + null_value= true; return 0; } +String *Item_func_concat::append_value(THD *thd, + String *res, + bool res_is_const, + String *str, + String **use_as_buff, + const String *res2) +{ + DBUG_ASSERT(res2->length() > 0); + + if ((ulong) res->length() + (ulong) res2->length() > + thd->variables.max_allowed_packet) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_ALLOWED_PACKET_OVERFLOWED, + ER_THD(thd, ER_WARN_ALLOWED_PACKET_OVERFLOWED), + func_name(), + thd->variables.max_allowed_packet); + return NULL; + } + + uint32 concat_len= res->length() + res2->length(); + + if (!res_is_const && res->alloced_length() >= concat_len) + { // Use old buffer + return res->append(*res2) ? NULL : res; + } + + if (str->alloced_length() >= concat_len) + { + if (str->ptr() == res2->ptr()) + { + if (str->replace(0, 0, *res)) + return NULL; + } + else + { + if (str->copy(*res) || str->append(*res2)) + return NULL; + } + *use_as_buff= &tmp_value; + return str; + } + + if (res == &tmp_value) + { + if (res->append(*res2)) // Must be a blob + return NULL; + return res; + } + + if (res2 == &tmp_value) + { // This can happend only 1 time + if (tmp_value.replace(0, 0, *res)) + return NULL; + *use_as_buff= str; // Put next arg here + return &tmp_value; + } + + if (tmp_value.is_alloced() && res2->ptr() >= tmp_value.ptr() && + res2->ptr() <= tmp_value.ptr() + tmp_value.alloced_length()) + { + /* + This happens really seldom: + In this case res2 is sub string of tmp_value. We will + now work in place in tmp_value to set it to res | res2 + */ + /* Chop the last characters in tmp_value that isn't in res2 */ + tmp_value.length((uint32) (res2->ptr() - tmp_value.ptr()) + + res2->length()); + /* Place res2 at start of tmp_value, remove chars before res2 */ + if (tmp_value.replace(0,(uint32) (res2->ptr() - tmp_value.ptr()), + *res)) + return NULL; + *use_as_buff= str; // Put next arg here + return &tmp_value; + } + + /* + Two big const strings + NOTE: We should be prudent in the initial allocation unit -- the + size of the arguments is a function of data distribution, which + can be any. Instead of overcommitting at the first row, we grow + the allocated amount by the factor of 2. This ensures that no + more than 25% of memory will be overcommitted on average. + */ + + if (tmp_value.alloced_length() < concat_len) + { + if (tmp_value.alloced_length() == 0) + { + if (tmp_value.alloc(concat_len)) + return NULL; + } + else + { + uint32 new_len= tmp_value.alloced_length() > INT_MAX32 ? + UINT_MAX32 - 1 : + tmp_value.alloced_length() * 2; + set_if_bigger(new_len, concat_len); + if (tmp_value.realloc(new_len)) + return NULL; + } + } + + if (tmp_value.copy(*res) || tmp_value.append(*res2)) + return NULL; + + *use_as_buff= str; + return &tmp_value; +} + + void Item_func_concat::fix_length_and_dec() { ulonglong char_length= 0; @@ -1133,7 +1205,8 @@ void Item_func_reverse::fix_length_and_dec() Fix that this works with binary strings when using USE_MB */ -String *Item_func_replace::val_str(String *str) +String *Item_func_replace::val_str_internal(String *str, + String *empty_string_for_null) { DBUG_ASSERT(fixed == 1); String *res,*res2,*res3; @@ -1153,8 +1226,11 @@ String *Item_func_replace::val_str(String *str) goto null; res2=args[1]->val_str(&tmp_value); if (args[1]->null_value) - goto null; - + { + if (!empty_string_for_null) + goto null; + res2= empty_string_for_null; + } res->set_charset(collation.collation); #ifdef USE_MB @@ -1172,7 +1248,11 @@ String *Item_func_replace::val_str(String *str) return res; #endif if (!(res3=args[2]->val_str(&tmp_value2))) - goto null; + { + if (!empty_string_for_null) + goto null; + res3= empty_string_for_null; + } from_length= res2->length(); to_length= res3->length(); @@ -1255,6 +1335,9 @@ redo: } while ((offset=res->strstr(*res2,(uint) offset)) >= 0); } + if (empty_string_for_null && !res->length()) + goto null; + return res; null: @@ -2312,6 +2395,25 @@ String *Item_func_database::val_str(String *str) } +String *Item_func_sqlerrm::val_str(String *str) +{ + DBUG_ASSERT(fixed); + DBUG_ASSERT(!null_value); + Diagnostics_area::Sql_condition_iterator it= + current_thd->get_stmt_da()->sql_conditions(); + const Sql_condition *err; + if ((err= it++)) + { + str->copy(err->get_message_text(), err->get_message_octet_length(), + system_charset_info); + return str; + } + str->copy(C_STRING_WITH_LEN("normal, successful completition"), + system_charset_info); + return str; +} + + /** @note USER() is replicated correctly if binlog_format=ROW or (as of BUG#28086) binlog_format=MIXED, but is incorrectly replicated to '' @@ -2566,24 +2668,6 @@ String *Item_func_soundex::val_str(String *str) const int FORMAT_MAX_DECIMALS= 30; -MY_LOCALE *Item_func_format::get_locale(Item *item) -{ - DBUG_ASSERT(arg_count == 3); - String tmp, *locale_name= args[2]->val_str_ascii(&tmp); - MY_LOCALE *lc; - if (!locale_name || - !(lc= my_locale_by_name(locale_name->c_ptr_safe()))) - { - THD *thd= current_thd; - push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, - ER_UNKNOWN_LOCALE, - ER_THD(thd, ER_UNKNOWN_LOCALE), - locale_name ? locale_name->c_ptr_safe() : "NULL"); - lc= &my_locale_en_US; - } - return lc; -} - void Item_func_format::fix_length_and_dec() { uint32 char_length= args[0]->max_char_length(); @@ -2591,7 +2675,7 @@ void Item_func_format::fix_length_and_dec() collation.set(default_charset()); fix_char_length(char_length + max_sep_count + decimals); if (arg_count == 3) - locale= args[2]->basic_const_item() ? get_locale(args[2]) : NULL; + locale= args[2]->basic_const_item() ? args[2]->locale_from_val_str() : NULL; else locale= &my_locale_en_US; /* Two arguments */ } @@ -2610,7 +2694,7 @@ String *Item_func_format::val_str_ascii(String *str) int dec; /* Number of characters used to represent the decimals, including '.' */ uint32 dec_length; - MY_LOCALE *lc; + const MY_LOCALE *lc; DBUG_ASSERT(fixed == 1); dec= (int) args[1]->val_int(); @@ -2620,7 +2704,7 @@ String *Item_func_format::val_str_ascii(String *str) return NULL; } - lc= locale ? locale : get_locale(args[2]); + lc= locale ? locale : args[2]->locale_from_val_str(); dec= set_zone(dec, 0, FORMAT_MAX_DECIMALS); dec_length= dec ? dec+1 : 0; @@ -2843,29 +2927,51 @@ String *Item_func_char::val_str(String *str) { int32 num=(int32) args[i]->val_int(); if (!args[i]->null_value) - { - char tmp[4]; - if (num & 0xFF000000L) - { - mi_int4store(tmp, num); - str->append(tmp, 4, &my_charset_bin); - } - else if (num & 0xFF0000L) - { - mi_int3store(tmp, num); - str->append(tmp, 3, &my_charset_bin); - } - else if (num & 0xFF00L) - { - mi_int2store(tmp, num); - str->append(tmp, 2, &my_charset_bin); - } - else - { - tmp[0]= (char) num; - str->append(tmp, 1, &my_charset_bin); - } - } + append_char(str, num); + } + str->realloc(str->length()); // Add end 0 (for Purify) + return check_well_formed_result(str); +} + + +void Item_func_char::append_char(String *str, int32 num) +{ + char tmp[4]; + if (num & 0xFF000000L) + { + mi_int4store(tmp, num); + str->append(tmp, 4, &my_charset_bin); + } + else if (num & 0xFF0000L) + { + mi_int3store(tmp, num); + str->append(tmp, 3, &my_charset_bin); + } + else if (num & 0xFF00L) + { + mi_int2store(tmp, num); + str->append(tmp, 2, &my_charset_bin); + } + else + { + tmp[0]= (char) num; + str->append(tmp, 1, &my_charset_bin); + } +} + + +String *Item_func_chr::val_str(String *str) +{ + DBUG_ASSERT(fixed == 1); + str->length(0); + str->set_charset(collation.collation); + int32 num=(int32) args[0]->val_int(); + if (!args[0]->null_value) + append_char(str, num); + else + { + null_value= 1; + return 0; } str->realloc(str->length()); // Add end 0 (for Purify) return check_well_formed_result(str); @@ -3090,11 +3196,23 @@ err: } -void Item_func_rpad::fix_length_and_dec() +void Item_func_pad::fix_length_and_dec() { - // Handle character set for args[0] and args[2]. - if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2)) - return; + if (arg_count == 3) + { + // Handle character set for args[0] and args[2]. + if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2)) + return; + } + else + { + if (agg_arg_charsets_for_string_result(collation, &args[0], 1, 1)) + return; + pad_str.set_charset(collation.collation); + pad_str.length(0); + pad_str.append(" ", 1); + } + if (args[1]->const_item()) { ulonglong char_length= (ulonglong) args[1]->val_int(); @@ -3125,12 +3243,17 @@ String *Item_func_rpad::val_str(String *str) longlong count= args[1]->val_int(); longlong byte_count; String *res= args[0]->val_str(str); - String *rpad= args[2]->val_str(&rpad_str); + String *rpad= arg_count == 2 ? &pad_str : args[2]->val_str(&pad_str); if (!res || args[1]->null_value || !rpad || ((count < 0) && !args[1]->unsigned_flag)) goto err; + null_value=0; + + if (count == 0) + return make_empty_result(); + /* Assumes that the maximum length of a String is < INT_MAX32. */ /* Set here so that rest of code sees out-of-bound value as such. */ if ((ulonglong) count > INT_MAX32) @@ -3154,7 +3277,6 @@ String *Item_func_rpad::val_str(String *str) res->length(res->charpos((int) count)); // Shorten result if longer return (res); } - pad_char_length= rpad->numchars(); byte_count= count * collation.collation->mbmaxlen; { @@ -3168,8 +3290,15 @@ String *Item_func_rpad::val_str(String *str) goto err; } } - if (args[2]->null_value || !pad_char_length) - goto err; + + if (arg_count == 3) + { + if (args[2]->null_value || !(pad_char_length= rpad->numchars())) + goto err; + } + else + pad_char_length= 1; // Implicit space + res_byte_length= res->length(); /* Must be done before alloc_buffer */ if (!(res= alloc_buffer(res,str,&tmp_value, (ulong) byte_count))) goto err; @@ -3198,32 +3327,6 @@ String *Item_func_rpad::val_str(String *str) } -void Item_func_lpad::fix_length_and_dec() -{ - // Handle character set for args[0] and args[2]. - if (agg_arg_charsets_for_string_result(collation, &args[0], 2, 2)) - return; - - if (args[1]->const_item()) - { - ulonglong char_length= (ulonglong) args[1]->val_int(); - DBUG_ASSERT(collation.collation->mbmaxlen > 0); - /* Assumes that the maximum length of a String is < INT_MAX32. */ - /* Set here so that rest of code sees out-of-bound value as such. */ - if (args[1]->null_value) - char_length= 0; - else if (char_length > INT_MAX32) - char_length= INT_MAX32; - fix_char_length_ulonglong(char_length); - } - else - { - max_length= MAX_BLOB_WIDTH; - maybe_null= 1; - } -} - - String *Item_func_lpad::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -3232,12 +3335,17 @@ String *Item_func_lpad::val_str(String *str) longlong count= args[1]->val_int(); longlong byte_count; String *res= args[0]->val_str(&tmp_value); - String *pad= args[2]->val_str(&lpad_str); + String *pad= arg_count == 2 ? &pad_str : args[2]->val_str(&pad_str); if (!res || args[1]->null_value || !pad || ((count < 0) && !args[1]->unsigned_flag)) goto err; + null_value=0; + + if (count == 0) + return make_empty_result(); + /* Assumes that the maximum length of a String is < INT_MAX32. */ /* Set here so that rest of code sees out-of-bound value as such. */ if ((ulonglong) count > INT_MAX32) @@ -3265,7 +3373,6 @@ String *Item_func_lpad::val_str(String *str) return res; } - pad_char_length= pad->numchars(); byte_count= count * collation.collation->mbmaxlen; { @@ -3280,9 +3387,16 @@ String *Item_func_lpad::val_str(String *str) } } - if (args[2]->null_value || !pad_char_length || - str->alloc((uint32) byte_count)) + if (str->alloc((uint32) byte_count)) goto err; + + if (arg_count == 3) + { + if (args[2]->null_value || !(pad_char_length= pad->numchars())) + goto err; + } + else + pad_char_length= 1; // Implicit space str->length(0); str->set_charset(collation.collation); @@ -3594,53 +3708,41 @@ void Item_func_weight_string::print(String *str, enum_query_type query_type) } -String *Item_func_hex::val_str_ascii(String *str) +String *Item_func_hex::val_str_ascii_from_val_real(String *str) { - String *res; - DBUG_ASSERT(fixed == 1); - if (args[0]->result_type() != STRING_RESULT) - { - ulonglong dec; - char ans[65],*ptr; - /* Return hex of unsigned longlong value */ - if (args[0]->result_type() == REAL_RESULT || - args[0]->result_type() == DECIMAL_RESULT) - { - double val= args[0]->val_real(); - if ((val <= (double) LONGLONG_MIN) || - (val >= (double) (ulonglong) ULONGLONG_MAX)) - dec= ~(longlong) 0; - else - dec= (ulonglong) (val + (val > 0 ? 0.5 : -0.5)); - } - else - dec= (ulonglong) args[0]->val_int(); + ulonglong dec; + double val= args[0]->val_real(); + if ((null_value= args[0]->null_value)) + return 0; + if ((val <= (double) LONGLONG_MIN) || + (val >= (double) (ulonglong) ULONGLONG_MAX)) + dec= ~(longlong) 0; + else + dec= (ulonglong) (val + (val > 0 ? 0.5 : -0.5)); + return str->set_hex(dec) ? make_empty_result() : str; +} - if ((null_value= args[0]->null_value)) - return 0; - - if (!(ptr= longlong2str(dec, ans, 16)) || - str->copy(ans,(uint32) (ptr - ans), - &my_charset_numeric)) - return make_empty_result(); // End of memory - return str; - } - /* Convert given string to a hex string, character by character */ - res= args[0]->val_str(&tmp_value); - if (!res || str->alloc(res->length()*2+1)) - { - null_value=1; - return 0; - } - null_value=0; - str->length(res->length()*2); - str->set_charset(&my_charset_latin1); +String *Item_func_hex::val_str_ascii_from_val_str(String *str) +{ + DBUG_ASSERT(&tmp_value != str); + String *res= args[0]->val_str(&tmp_value); + DBUG_ASSERT(res != str); + if ((null_value= (res == NULL))) + return NULL; + return str->set_hex(res->ptr(), res->length()) ? make_empty_result() : str; +} - octet2hex((char*) str->ptr(), res->ptr(), res->length()); - return str; + +String *Item_func_hex::val_str_ascii_from_val_int(String *str) +{ + ulonglong dec= (ulonglong) args[0]->val_int(); + if ((null_value= args[0]->null_value)) + return 0; + return str->set_hex(dec) ? make_empty_result() : str; } + /** Convert given hex string to a binary string. */ String *Item_func_unhex::val_str(String *str) diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index e2dcab01e3b..1c35588f884 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -27,8 +27,6 @@ extern size_t username_char_length; -class MY_LOCALE; - class Item_str_func :public Item_func { protected: @@ -64,8 +62,7 @@ public: longlong val_int(); double val_real(); my_decimal *val_decimal(my_decimal *); - enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return string_field_type(); } + const Type_handler *type_handler() const { return string_type_handler(); } void left_right_max_length(); bool fix_fields(THD *thd, Item **ref); void update_null_value() @@ -244,7 +241,40 @@ public: class Item_func_concat :public Item_str_func { +protected: String tmp_value; + /* + Get the i-th argument val_str() and its const_item() + @param i[IN] - The argument number + @param str[IN] - The buffer for val_str() + @param is_const[IN/OUT] - If args[i]->val_str() returned a non-null value, + then args[i]->const_item() is returned here. + Otherwise, the value of is_const is not touched. + @retval - the result of val_str(). + */ + String *arg_val_str(uint i, String *str, bool *is_const) + { + String *res= args[i]->val_str(str); + if (res) + *is_const= args[i]->const_item(); + return res; + } + /* + Append a non-NULL value to the result. + @param [IN] thd - The current thread. + @param [IN/OUT] res - The current val_str() return value. + @param [IN] res_is_const - If "false", then OK to append to "res" + @param [IN/OUT] str - The val_str() argument. + @param [IN] res2 - The value to be appended. + @param [IN/OUT] use_as_buff - Which buffer to use for the next argument: + args[next_arg]->val_str(use_as_buff) + */ + String *append_value(THD *thd, + String *res, + bool res_is_const, + String *str, + String **use_as_buff, + const String *res2); public: Item_func_concat(THD *thd, List<Item> &list): Item_str_func(thd, list) {} Item_func_concat(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {} @@ -255,6 +285,29 @@ public: { return get_item_copy<Item_func_concat>(thd, mem_root, this); } }; + +/* + This class handles the || operator in sql_mode=ORACLE. + Unlike the traditional MariaDB concat(), it treats NULL arguments as ''. +*/ +class Item_func_concat_operator_oracle :public Item_func_concat +{ +public: + Item_func_concat_operator_oracle(THD *thd, List<Item> &list) + :Item_func_concat(thd, list) + { } + Item_func_concat_operator_oracle(THD *thd, Item *a, Item *b) + :Item_func_concat(thd, a, b) + { } + String *val_str(String *); + const char *func_name() const { return "concat_operator_oracle"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { + return get_item_copy<Item_func_concat_operator_oracle>(thd, mem_root, this); + } +}; + + class Item_func_decode_histogram :public Item_str_func { public: @@ -304,14 +357,28 @@ class Item_func_replace :public Item_str_func public: Item_func_replace(THD *thd, Item *org, Item *find, Item *replace): Item_str_func(thd, org, find, replace) {} - String *val_str(String *); + String *val_str(String *to) { return val_str_internal(to, NULL); }; void fix_length_and_dec(); + String *val_str_internal(String *str, String *empty_string_for_null); const char *func_name() const { return "replace"; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_replace>(thd, mem_root, this); } }; +class Item_func_replace_oracle :public Item_func_replace +{ + String tmp_emtpystr; +public: + Item_func_replace_oracle(THD *thd, Item *org, Item *find, Item *replace): + Item_func_replace(thd, org, find, replace) {} + String *val_str(String *to) { return val_str_internal(to, &tmp_emtpystr); }; + const char *func_name() const { return "replace_oracle"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_replace_oracle>(thd, mem_root, this); } +}; + + class Item_func_regexp_replace :public Item_str_func { Regexp_processor_pcre re; @@ -717,6 +784,27 @@ public: }; +class Item_func_sqlerrm :public Item_func_sysconst +{ +public: + Item_func_sqlerrm(THD *thd): Item_func_sysconst(thd) {} + String *val_str(String *); + const char *func_name() const { return "SQLERRM"; } + const char *fully_qualified_func_name() const { return "SQLERRM"; } + void print(String *str, enum_query_type query_type) + { + str->append(func_name()); + } + void fix_length_and_dec() + { + max_length= 512 * system_charset_info->mbmaxlen; + null_value= maybe_null= false; + } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_sqlerrm>(thd, mem_root, this); } +}; + + class Item_func_user :public Item_func_sysconst { protected: @@ -842,14 +930,13 @@ public: class Item_func_format :public Item_str_ascii_func { - MY_LOCALE *locale; + const MY_LOCALE *locale; public: Item_func_format(THD *thd, Item *org, Item *dec): Item_str_ascii_func(thd, org, dec) {} Item_func_format(THD *thd, Item *org, Item *dec, Item *lang): Item_str_ascii_func(thd, org, dec, lang) {} - MY_LOCALE *get_locale(Item *item); String *val_str_ascii(String *); void fix_length_and_dec(); const char *func_name() const { return "format"; } @@ -866,7 +953,11 @@ public: Item_func_char(THD *thd, List<Item> &list, CHARSET_INFO *cs): Item_str_func(thd, list) { collation.set(cs); } + Item_func_char(THD *thd, Item *arg1, CHARSET_INFO *cs): + Item_str_func(thd, arg1) + { collation.set(cs); } String *val_str(String *); + void append_char(String * str, int32 num); void fix_length_and_dec() { max_length= arg_count * 4; @@ -876,6 +967,20 @@ public: { return get_item_copy<Item_func_char>(thd, mem_root, this); } }; +class Item_func_chr :public Item_func_char +{ +public: + Item_func_chr(THD *thd, Item *arg1, CHARSET_INFO *cs): + Item_func_char(thd, arg1, cs) {} + String *val_str(String *); + void fix_length_and_dec() + { + max_length= 4; + } + const char *func_name() const { return "chr"; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_chr>(thd, mem_root, this); } +}; class Item_func_repeat :public Item_str_func { @@ -920,28 +1025,41 @@ public: }; -class Item_func_rpad :public Item_str_func +class Item_func_pad: public Item_str_func { - String tmp_value, rpad_str; +protected: + String tmp_value, pad_str; public: - Item_func_rpad(THD *thd, Item *arg1, Item *arg2, Item *arg3): + Item_func_pad(THD *thd, Item *arg1, Item *arg2, Item *arg3): Item_str_func(thd, arg1, arg2, arg3) {} - String *val_str(String *); + Item_func_pad(THD *thd, Item *arg1, Item *arg2): + Item_str_func(thd, arg1, arg2) {} void fix_length_and_dec(); +}; + + +class Item_func_rpad :public Item_func_pad +{ +public: + Item_func_rpad(THD *thd, Item *arg1, Item *arg2, Item *arg3): + Item_func_pad(thd, arg1, arg2, arg3) {} + Item_func_rpad(THD *thd, Item *arg1, Item *arg2): + Item_func_pad(thd, arg1, arg2) {} + String *val_str(String *); const char *func_name() const { return "rpad"; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_rpad>(thd, mem_root, this); } }; -class Item_func_lpad :public Item_str_func +class Item_func_lpad :public Item_func_pad { - String tmp_value, lpad_str; public: Item_func_lpad(THD *thd, Item *arg1, Item *arg2, Item *arg3): - Item_str_func(thd, arg1, arg2, arg3) {} + Item_func_pad(thd, arg1, arg2, arg3) {} + Item_func_lpad(THD *thd, Item *arg1, Item *arg2): + Item_func_pad(thd, arg1, arg2) {} String *val_str(String *); - void fix_length_and_dec(); const char *func_name() const { return "lpad"; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_lpad>(thd, mem_root, this); } @@ -968,17 +1086,33 @@ public: class Item_func_hex :public Item_str_ascii_checksum_func { +protected: String tmp_value; + /* + Calling arg[0]->type_handler() can be expensive on every row. + It's a virtual method, and in case if args[0] is a complex Item, + its type_handler() can call more virtual methods. + So let's cache it during fix_length_and_dec(). + */ + const Type_handler *m_arg0_type_handler; public: Item_func_hex(THD *thd, Item *a): - Item_str_ascii_checksum_func(thd, a) {} + Item_str_ascii_checksum_func(thd, a), m_arg0_type_handler(NULL) {} const char *func_name() const { return "hex"; } - String *val_str_ascii(String *); + String *val_str_ascii_from_val_int(String *str); + String *val_str_ascii_from_val_real(String *str); + String *val_str_ascii_from_val_str(String *str); + String *val_str_ascii(String *str) + { + DBUG_ASSERT(fixed); + return m_arg0_type_handler->Item_func_hex_val_str_ascii(this, str); + } void fix_length_and_dec() { - collation.set(default_charset()); + collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); decimals=0; fix_char_length(args[0]->max_length * 2); + m_arg0_type_handler= args[0]->type_handler(); } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_hex>(thd, mem_root, this); } @@ -1315,11 +1449,13 @@ public: { return get_item_copy<Item_func_weight_string>(thd, mem_root, this); } }; -class Item_func_crc32 :public Item_int_func +class Item_func_crc32 :public Item_long_func { + bool check_arguments() const + { return args[0]->check_type_can_return_str(func_name()); } String value; public: - Item_func_crc32(THD *thd, Item *a): Item_int_func(thd, a) + Item_func_crc32(THD *thd, Item *a): Item_long_func(thd, a) { unsigned_flag= 1; } const char *func_name() const { return "crc32"; } void fix_length_and_dec() { max_length=10; } @@ -1328,11 +1464,12 @@ public: { return get_item_copy<Item_func_crc32>(thd, mem_root, this); } }; -class Item_func_uncompressed_length : public Item_int_func +class Item_func_uncompressed_length : public Item_long_func_length { String value; public: - Item_func_uncompressed_length(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_uncompressed_length(THD *thd, Item *a) + :Item_long_func_length(thd, a) {} const char *func_name() const{return "uncompressed_length";} void fix_length_and_dec() { max_length=10; maybe_null= true; } longlong val_int(); diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index 29433be4af8..71594c0478b 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -45,7 +45,7 @@ double get_post_group_estimate(JOIN* join, double join_op_rows); -const char *exists_outer_expr_name= "<exists outer expr>"; +LEX_CSTRING exists_outer_expr_name= { STRING_WITH_LEN("<exists outer expr>") }; int check_and_do_in_subquery_rewrites(JOIN *join); @@ -118,7 +118,7 @@ void Item_subselect::init(st_select_lex *select_lex, */ parsing_place= (outer_select->in_sum_expr ? NO_MATTER : outer_select->parsing_place); - if (unit->is_union()) + if (unit->is_unit_op()) engine= new subselect_union_engine(unit, result, this); else engine= new subselect_single_select_engine(select_lex, result, this); @@ -290,7 +290,6 @@ bool Item_subselect::fix_fields(THD *thd_param, Item **ref) (*ref)= substitution; substitution->name= name; - substitution->name_length= name_length; if (have_to_be_excluded) engine->exclude(); substitution= 0; @@ -1066,7 +1065,7 @@ void Item_maxmin_subselect::no_rows_in_result() */ if (parsing_place != SELECT_LIST || const_item()) return; - value= Item_cache::get_cache(thd, new (thd->mem_root) Item_null(thd)); + value= (new (thd->mem_root) Item_null(thd))->get_cache(thd); null_value= 0; was_values= 0; make_const(); @@ -1084,7 +1083,7 @@ void Item_singlerow_subselect::no_rows_in_result() */ if (parsing_place != SELECT_LIST || const_item()) return; - value= Item_cache::get_cache(thd, new (thd->mem_root) Item_null(thd)); + value= (new (thd->mem_root) Item_null(thd))->get_cache(thd); reset(); make_const(); } @@ -1126,7 +1125,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join) SELECT_LEX *select_lex= join->select_lex; Query_arena *arena= thd->stmt_arena; - if (!select_lex->master_unit()->is_union() && + if (!select_lex->master_unit()->is_unit_op() && !select_lex->table_list.elements && select_lex->item_list.elements == 1 && !select_lex->item_list.head()->with_sum_func && @@ -1173,23 +1172,9 @@ void Item_singlerow_subselect::store(uint i, Item *item) row[i]->cache_value(); } -enum Item_result Item_singlerow_subselect::result_type() const +const Type_handler *Item_singlerow_subselect::type_handler() const { - return engine->type(); -} - -enum Item_result Item_singlerow_subselect::cmp_type() const -{ - return engine->cmptype(); -} - -/* - Don't rely on the result type to calculate field type. - Ask the engine instead. -*/ -enum_field_types Item_singlerow_subselect::field_type() const -{ - return engine->field_type(); + return engine->type_handler(); } void Item_singlerow_subselect::fix_length_and_dec() @@ -1260,7 +1245,7 @@ Item* Item_singlerow_subselect::expr_cache_insert_transformer(THD *tmp_thd, } -uint Item_singlerow_subselect::cols() +uint Item_singlerow_subselect::cols() const { return engine->cols(); } @@ -1818,7 +1803,7 @@ Item_in_subselect::single_value_transformer(JOIN *join) if (!(join_having || select_lex->with_sum_func || select_lex->group_list.elements) && select_lex->table_list.elements == 0 && - !select_lex->master_unit()->is_union()) + !select_lex->master_unit()->is_unit_op()) { Item *where_item= (Item*) select_lex->item_list.head(); /* @@ -1878,8 +1863,8 @@ Item_in_subselect::single_value_transformer(JOIN *join) */ expr= new (thd->mem_root) Item_direct_ref(thd, &select_lex->context, (Item**)optimizer->get_cache(), - (char *)"<no matter>", - (char *)in_left_expr_name); + "<no matter>", + &in_left_expr_name); } DBUG_RETURN(false); @@ -2102,6 +2087,8 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, if (join_having || select_lex->with_sum_func || select_lex->group_list.elements) { + const char *tmp= this->full_name(); + LEX_CSTRING field_name= {tmp, safe_strlen(tmp)}; Item *item= func->create(thd, expr, new (thd->mem_root) Item_ref_null_helper( thd, @@ -2110,7 +2097,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, &select_lex-> ref_pointer_array[0], (char *)"<ref>", - this->full_name())); + &field_name)); if (!abort_on_null && left_expr->maybe_null) { /* @@ -2122,7 +2109,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, } if (!join_having) - item->name= (char*) in_having_cond; + item->name= in_having_cond; if (fix_having(item, select_lex)) DBUG_RETURN(true); *having_item= item; @@ -2147,7 +2134,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, get_cond_guard(0)))) DBUG_RETURN(true); } - having->name= (char*) in_having_cond; + having->name= in_having_cond; if (fix_having(having, select_lex)) DBUG_RETURN(true); *having_item= having; @@ -2172,15 +2159,16 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, single_value_transformer but there is no corresponding action in row_value_transformer? */ - item->name= (char *) in_additional_cond; + item->name= in_additional_cond; if (!item->fixed && item->fix_fields(thd, 0)) DBUG_RETURN(true); *where_item= item; } else { - if (select_lex->master_unit()->is_union()) + if (select_lex->master_unit()->is_unit_op()) { + LEX_CSTRING field_name= {STRING_WITH_LEN("<result>") }; Item *new_having= func->create(thd, expr, new (thd->mem_root) Item_ref_null_helper(thd, @@ -2188,7 +2176,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, this, &select_lex->ref_pointer_array[0], (char *)"<no matter>", - (char *)"<result>")); + &field_name)); if (!abort_on_null && left_expr->maybe_null) { disable_cond_guard_for_const_null_left_expr(0); @@ -2197,7 +2185,7 @@ Item_in_subselect::create_single_in_to_exists_cond(JOIN *join, DBUG_RETURN(true); } - new_having->name= (char*) in_having_cond; + new_having->name= in_having_cond; if (fix_having(new_having, select_lex)) DBUG_RETURN(true); *having_item= new_having; @@ -2344,7 +2332,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, bool is_having_used= (join_having || select_lex->with_sum_func || select_lex->group_list.first || !select_lex->table_list.elements); - + LEX_CSTRING list_ref= { STRING_WITH_LEN("<list ref>")}; DBUG_ENTER("Item_in_subselect::create_row_in_to_exists_cond"); DBUG_ASSERT(thd == join->thd); @@ -2366,6 +2354,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, if (select_lex->ref_pointer_array[i]-> check_cols(left_expr->element_index(i)->cols())) DBUG_RETURN(true); + Item *item_eq= new (thd->mem_root) Item_func_eq(thd, new (thd->mem_root) @@ -2373,12 +2362,12 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, (*optimizer->get_cache())-> addr(i), (char *)"<no matter>", - (char *)in_left_expr_name), + &in_left_expr_name), new (thd->mem_root) Item_ref(thd, &select_lex->context, &select_lex->ref_pointer_array[i], (char *)"<no matter>", - (char *)"<list ref>")); + &list_ref)); Item *item_isnull= new (thd->mem_root) Item_func_isnull(thd, @@ -2386,7 +2375,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, Item_ref(thd, &select_lex->context, &select_lex->ref_pointer_array[i], (char *)"<no matter>", - (char *)"<list ref>")); + &list_ref)); Item *col_item= new (thd->mem_root) Item_cond_or(thd, item_eq, item_isnull); if (!abort_on_null && left_expr->element_index(i)->maybe_null && @@ -2407,7 +2396,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, &select_lex-> ref_pointer_array[i], (char *)"<no matter>", - (char *)"<list ref>")); + &list_ref)); if (!abort_on_null && left_expr->element_index(i)->maybe_null && get_cond_guard(i) ) { @@ -2442,13 +2431,13 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, (*optimizer->get_cache())-> addr(i), (char *)"<no matter>", - (char *)in_left_expr_name), + &in_left_expr_name), new (thd->mem_root) Item_direct_ref(thd, &select_lex->context, &select_lex-> ref_pointer_array[i], (char *)"<no matter>", - (char *)"<list ref>")); + &list_ref)); if (!abort_on_null && select_lex->ref_pointer_array[i]->maybe_null) { Item *having_col_item= @@ -2458,8 +2447,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, Item_ref(thd, &select_lex->context, &select_lex->ref_pointer_array[i], (char *)"<no matter>", - (char *)"<list ref>")); - + &list_ref)); item_isnull= new (thd->mem_root) Item_func_isnull(thd, @@ -2468,7 +2456,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, &select_lex-> ref_pointer_array[i], (char *)"<no matter>", - (char *)"<list ref>")); + &list_ref)); item= new (thd->mem_root) Item_cond_or(thd, item, item_isnull); if (left_expr->element_index(i)->maybe_null && get_cond_guard(i)) { @@ -2503,7 +2491,7 @@ Item_in_subselect::create_row_in_to_exists_cond(JOIN * join, if (*having_item) { if (!join_having) - (*having_item)->name= (char*) in_having_cond; + (*having_item)->name= in_having_cond; if (fix_having(*having_item, select_lex)) DBUG_RETURN(true); (*having_item)->top_level_item(); @@ -3000,7 +2988,7 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg) Item_direct_ref(thd, &first_select->context, (Item**)optimizer->get_cache(), (char *)"<no matter>", - (char *)in_left_expr_name); + &in_left_expr_name); if (in_subs->fix_fields(thd, optimizer->arguments() + 1)) { res= TRUE; @@ -3071,7 +3059,7 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg) &unit->outer_select()->context, optimizer->arguments(), (char *)"<no matter>", - (char *)exists_outer_expr_name)), + &exists_outer_expr_name)), optimizer) : (Item *)optimizer); } @@ -3095,7 +3083,7 @@ bool Item_exists_subselect::exists2in_processor(void *opt_arg) &unit->outer_select()->context, optimizer->arguments()[0]->addr(i), (char *)"<no matter>", - (char *)exists_outer_expr_name)), + &exists_outer_expr_name)), thd->mem_root); } } @@ -3284,7 +3272,7 @@ bool Item_in_subselect::fix_fields(THD *thd_arg, Item **ref) { outer_cols_num= left_expr->cols(); - if (unit->is_union()) + if (unit->is_unit_op()) inner_cols= &(unit->types); else inner_cols= &(unit->first_select()->item_list); @@ -3705,24 +3693,21 @@ void subselect_engine::set_row(List<Item> &item_list, Item_cache **row) { Item *sel_item; List_iterator_fast<Item> li(item_list); - cmp_type= res_type= STRING_RESULT; - res_field_type= MYSQL_TYPE_VAR_STRING; + set_handler(&type_handler_varchar); for (uint i= 0; (sel_item= li++); i++) { item->max_length= sel_item->max_length; - res_type= sel_item->result_type(); - cmp_type= sel_item->cmp_type(); - res_field_type= sel_item->field_type(); + set_handler(sel_item->type_handler()); item->decimals= sel_item->decimals; item->unsigned_flag= sel_item->unsigned_flag; maybe_null= sel_item->maybe_null; - if (!(row[i]= Item_cache::get_cache(thd, sel_item, sel_item->cmp_type()))) + if (!(row[i]= sel_item->get_cache(thd))) return; row[i]->setup(thd, sel_item); //psergey-backport-timours: row[i]->store(sel_item); } if (item_list.elements > 1) - cmp_type= res_type= ROW_RESULT; + set_handler(&type_handler_row); } void subselect_single_select_engine::fix_length_and_dec(Item_cache **row) @@ -4285,7 +4270,7 @@ int subselect_indexsubquery_engine::exec() } -uint subselect_single_select_engine::cols() +uint subselect_single_select_engine::cols() const { //psergey-sj-backport: the following assert was gone in 6.0: //DBUG_ASSERT(select_lex->join != 0); // should be called after fix_fields() @@ -4294,7 +4279,7 @@ uint subselect_single_select_engine::cols() } -uint subselect_union_engine::cols() +uint subselect_union_engine::cols() const { DBUG_ASSERT(unit->is_prepared()); // should be called after fix_fields() return unit->types.elements; @@ -4375,7 +4360,7 @@ void subselect_union_engine::print(String *str, enum_query_type query_type) void subselect_uniquesubquery_engine::print(String *str, enum_query_type query_type) { - char *table_name= tab->table->s->table_name.str; + const char *table_name= tab->table->s->table_name.str; str->append(STRING_WITH_LEN("<primary_index_lookup>(")); tab->ref.items[0]->print(str, query_type); str->append(STRING_WITH_LEN(" in ")); @@ -4894,7 +4879,7 @@ my_bitmap_init_memroot(MY_BITMAP *map, uint n_bits, MEM_ROOT *mem_root) bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id) { THD *thd= get_thd(); - select_union *result_sink; + select_unit *result_sink; /* Options to create_tmp_table. */ ulonglong tmp_create_options= thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS; /* | TMP_TABLE_FORCE_MYISAM; TIMOUR: force MYISAM */ @@ -4932,7 +4917,7 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id) DBUG_RETURN(TRUE); char buf[32]; - uint len= my_snprintf(buf, sizeof(buf), "<subquery%d>", subquery_id); + uint len= my_snprintf(buf, sizeof(buf), "<subquery%u>", subquery_id); char *name; if (!(name= (char*)thd->alloc(len + 1))) DBUG_RETURN(TRUE); @@ -4946,7 +4931,7 @@ bool subselect_hash_sj_engine::init(List<Item> *tmp_columns, uint subquery_id) } if (result_sink->create_result_table(thd, tmp_columns, TRUE, tmp_create_options, - name, TRUE, TRUE)) + name, TRUE, TRUE, FALSE, 0)) DBUG_RETURN(TRUE); tmp_table= result_sink->table; @@ -5975,10 +5960,10 @@ void Ordered_key::print(String *str) str->append(", ("); for (i= 0; i < key_column_count - 1; i++) { - str->append(key_columns[i]->field->field_name); + str->append(&key_columns[i]->field->field_name); str->append(", "); } - str->append(key_columns[i]->field->field_name); + str->append(&key_columns[i]->field->field_name); str->append("), "); str->append("null_bitmap: (bits="); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 7f827bbe982..6112c1c22f4 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -303,12 +303,10 @@ public: my_decimal *val_decimal(my_decimal *); bool val_bool(); bool get_date(MYSQL_TIME *ltime, ulonglong fuzzydate); - enum Item_result result_type() const; - enum Item_result cmp_type() const; - enum_field_types field_type() const; + const Type_handler *type_handler() const; void fix_length_and_dec(); - uint cols(); + uint cols() const; Item* element_index(uint i) { return reinterpret_cast<Item*>(row[i]); } Item** addr(uint i) { return (Item**)row + i; } bool check_cols(uint c); @@ -395,8 +393,7 @@ public: } void no_rows_in_result(); - enum Item_result result_type() const { return INT_RESULT;} - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const { return &type_handler_longlong; } longlong val_int(); double val_real(); String *val_str(String*); @@ -773,15 +770,13 @@ public: }; -class subselect_engine: public Sql_alloc +class subselect_engine: public Sql_alloc, + public Type_handler_hybrid_field_type { protected: select_result_interceptor *result; /* results storage class */ THD *thd; /* pointer to current THD */ Item_subselect *item; /* item, that use this engine */ - enum Item_result res_type; /* type of results */ - enum Item_result cmp_type; /* how to compare the results */ - enum_field_types res_field_type; /* column type of the results */ bool maybe_null; /* may be null (first item in select) */ public: @@ -792,12 +787,11 @@ public: subselect_engine(Item_subselect *si, select_result_interceptor *res): + Type_handler_hybrid_field_type(&type_handler_varchar), thd(NULL) { result= res; item= si; - cmp_type= res_type= STRING_RESULT; - res_field_type= MYSQL_TYPE_VAR_STRING; maybe_null= 0; } virtual ~subselect_engine() {}; // to satisfy compiler @@ -832,11 +826,8 @@ public: caller should call exec() again for the new engine. */ virtual int exec()= 0; - virtual uint cols()= 0; /* return number of columns in select */ + virtual uint cols() const= 0; /* return number of columns in select */ virtual uint8 uncacheable()= 0; /* query is uncacheable */ - enum Item_result type() { return res_type; } - enum Item_result cmptype() { return cmp_type; } - enum_field_types field_type() { return res_field_type; } virtual void exclude()= 0; virtual bool may_be_null() { return maybe_null; }; virtual table_map upper_select_const_tables()= 0; @@ -872,7 +863,7 @@ public: int prepare(THD *thd); void fix_length_and_dec(Item_cache** row); int exec(); - uint cols(); + uint cols() const; uint8 uncacheable(); void exclude(); table_map upper_select_const_tables(); @@ -907,7 +898,7 @@ public: int prepare(THD *); void fix_length_and_dec(Item_cache** row); int exec(); - uint cols(); + uint cols() const; uint8 uncacheable(); void exclude(); table_map upper_select_const_tables(); @@ -965,7 +956,7 @@ public: int prepare(THD *); void fix_length_and_dec(Item_cache** row); int exec(); - uint cols() { return 1; } + uint cols() const { return 1; } uint8 uncacheable() { return UNCACHEABLE_DEPENDENT_INJECTED; } void exclude(); table_map upper_select_const_tables() { return 0; } @@ -1103,7 +1094,7 @@ public: int prepare(THD *); int exec(); void print(String *str, enum_query_type query_type); - uint cols() { return materialize_engine->cols(); } + uint cols() const { return materialize_engine->cols(); } uint8 uncacheable() { return materialize_engine->uncacheable(); } table_map upper_select_const_tables() { return 0; } bool no_rows() { return !tmp_table->file->stats.records; } @@ -1386,7 +1377,7 @@ public: int prepare(THD *thd_arg) { set_thd(thd_arg); return 0; } int exec(); void fix_length_and_dec(Item_cache**) {} - uint cols() { /* TODO: what is the correct value? */ return 1; } + uint cols() const { /* TODO: what is the correct value? */ return 1; } uint8 uncacheable() { return UNCACHEABLE_DEPENDENT; } void exclude() {} table_map upper_select_const_tables() { return 0; } diff --git a/sql/item_sum.cc b/sql/item_sum.cc index 92f18d7402f..0c0b5a64953 100644 --- a/sql/item_sum.cc +++ b/sql/item_sum.cc @@ -897,7 +897,8 @@ bool Aggregator_distinct::setup(THD *thd) if (always_null) DBUG_RETURN(FALSE); - Field *field= arg->make_num_distinct_aggregator_field(thd->mem_root, arg); + Field *field= arg->type_handler()-> + make_num_distinct_aggregator_field(thd->mem_root, arg); if (!field || !(table= create_virtual_tmp_table(thd, field))) DBUG_RETURN(TRUE); @@ -1156,38 +1157,13 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) if ((!item->fixed && item->fix_fields(thd, args)) || (item= args[0])->check_cols(1)) return TRUE; - Type_std_attributes::set(args[0]); with_subselect= args[0]->with_subselect; with_window_func|= args[0]->with_window_func; - Item *item2= item->real_item(); - if (item2->type() == Item::FIELD_ITEM) - set_handler_by_field_type(((Item_field*) item2)->field->type()); - else if (item->cmp_type() == TIME_RESULT) - set_handler_by_field_type(item2->field_type()); - else - set_handler_by_result_type(item2->result_type(), - max_length, collation.collation); - - switch (Item_sum_hybrid::result_type()) { - case INT_RESULT: - case DECIMAL_RESULT: - case STRING_RESULT: - break; - case REAL_RESULT: - max_length= float_length(decimals); - break; - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); - }; + fix_length_and_dec(); if (!is_window_func_sum_expr()) setup_hybrid(thd, args[0], NULL); - /* MIN/MAX can return NULL for empty set indepedent of the used column */ - maybe_null= 1; result_field=0; - null_value=1; - fix_length_and_dec(); if (check_sum_func(thd, ref)) return TRUE; @@ -1198,6 +1174,14 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) } +void Item_sum_hybrid::fix_length_and_dec() +{ + DBUG_ASSERT(args[0]->field_type() == args[0]->real_item()->field_type()); + DBUG_ASSERT(args[0]->result_type() == args[0]->real_item()->result_type()); + (void) args[0]->type_handler()->Item_sum_hybrid_fix_length_and_dec(this); +} + + /** MIN/MAX function setup. @@ -1217,14 +1201,14 @@ Item_sum_hybrid::fix_fields(THD *thd, Item **ref) void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg) { - if (!(value= Item_cache::get_cache(thd, item, item->cmp_type()))) + if (!(value= item->get_cache(thd))) return; value->setup(thd, item); value->store(value_arg); /* Don't cache value, as it will change */ if (!item->const_item()) value->set_used_tables(RAND_TABLE_BIT); - if (!(arg_cache= Item_cache::get_cache(thd, item, item->cmp_type()))) + if (!(arg_cache= item->get_cache(thd))) return; arg_cache->setup(thd, item); /* Don't cache value, as it will change */ @@ -1233,51 +1217,20 @@ void Item_sum_hybrid::setup_hybrid(THD *thd, Item *item, Item *value_arg) cmp= new Arg_comparator(); if (cmp) cmp->set_cmp_func(this, (Item**)&arg_cache, (Item**)&value, FALSE); - collation.set(item->collation); } Field *Item_sum_hybrid::create_tmp_field(bool group, TABLE *table) { - Field *field; - MEM_ROOT *mem_root; - if (args[0]->type() == Item::FIELD_ITEM) { - field= ((Item_field*) args[0])->field; - - if ((field= create_tmp_field_from_field(table->in_use, field, name, table, - NULL))) + Field *field= ((Item_field*) args[0])->field; + if ((field= create_tmp_field_from_field(table->in_use, field, &name, + table, NULL))) field->flags&= ~NOT_NULL_FLAG; return field; } - - /* - DATE/TIME fields have STRING_RESULT result types. - In order to preserve field type, it's needed to handle DATE/TIME - fields creations separately. - */ - mem_root= table->in_use->mem_root; - switch (args[0]->field_type()) { - case MYSQL_TYPE_DATE: - field= new (mem_root) - Field_newdate(0, maybe_null ? (uchar*)"" : 0, 0, Field::NONE, name); - break; - case MYSQL_TYPE_TIME: - field= new_Field_time(mem_root, 0, maybe_null ? (uchar*)"" : 0, 0, - Field::NONE, name, decimals); - break; - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_DATETIME: - field= new_Field_datetime(mem_root, 0, maybe_null ? (uchar*)"" : 0, 0, - Field::NONE, name, decimals); - break; - default: - return Item_sum::create_tmp_field(group, table); - } - if (field) - field->init(table); - return field; + return tmp_table_field_from_field_type(table); } @@ -1296,7 +1249,7 @@ Item_sum_sum::Item_sum_sum(THD *thd, Item_sum_sum *item) count(item->count) { /* TODO: check if the following assignments are really needed */ - if (Item_sum_sum::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) { my_decimal2decimal(item->dec_buffs, dec_buffs); my_decimal2decimal(item->dec_buffs + 1, dec_buffs + 1); @@ -1316,7 +1269,7 @@ void Item_sum_sum::clear() DBUG_ENTER("Item_sum_sum::clear"); null_value=1; count= 0; - if (Item_sum_sum::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) { curr_dec_buff= 0; my_decimal_set_zero(dec_buffs); @@ -1327,41 +1280,35 @@ void Item_sum_sum::clear() } +void Item_sum_sum::fix_length_and_dec_double() +{ + set_handler(&type_handler_double); // Change FLOAT to DOUBLE + decimals= args[0]->decimals; + sum= 0.0; +} + + +void Item_sum_sum::fix_length_and_dec_decimal() +{ + set_handler(&type_handler_newdecimal); // Change temporal to new DECIMAL + decimals= args[0]->decimals; + /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */ + int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS; + max_length= my_decimal_precision_to_length_no_truncation(precision, + decimals, + unsigned_flag); + curr_dec_buff= 0; + my_decimal_set_zero(dec_buffs); +} + + void Item_sum_sum::fix_length_and_dec() { DBUG_ENTER("Item_sum_sum::fix_length_and_dec"); maybe_null=null_value=1; - decimals= args[0]->decimals; - switch (args[0]->cast_to_int_type()) { - case REAL_RESULT: - case STRING_RESULT: - set_handler_by_field_type(MYSQL_TYPE_DOUBLE); - sum= 0.0; - break; - case INT_RESULT: - case TIME_RESULT: - case DECIMAL_RESULT: - { - /* SUM result can't be longer than length(arg) + length(MAX_ROWS) */ - int precision= args[0]->decimal_precision() + DECIMAL_LONGLONG_DIGITS; - max_length= my_decimal_precision_to_length_no_truncation(precision, - decimals, - unsigned_flag); - curr_dec_buff= 0; - set_handler_by_field_type(MYSQL_TYPE_NEWDECIMAL); - my_decimal_set_zero(dec_buffs); - break; - } - case ROW_RESULT: - DBUG_ASSERT(0); - } - DBUG_PRINT("info", ("Type: %s (%d, %d)", - (result_type() == REAL_RESULT ? "REAL_RESULT" : - result_type() == DECIMAL_RESULT ? "DECIMAL_RESULT" : - result_type() == INT_RESULT ? "INT_RESULT" : - "--ILLEGAL!!!--"), - max_length, - (int)decimals)); + args[0]->cast_to_int_type_handler()->Item_sum_sum_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s (%d, %d)", type_handler()->name().ptr(), + max_length, (int) decimals)); DBUG_VOID_RETURN; } @@ -1377,7 +1324,7 @@ void Item_sum_sum::add_helper(bool perform_removal) { DBUG_ENTER("Item_sum_sum::add_helper"); - if (Item_sum_sum::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) { my_decimal value; const my_decimal *val= aggr->arg_val_decimal(&value); @@ -1434,7 +1381,7 @@ longlong Item_sum_sum::val_int() DBUG_ASSERT(fixed == 1); if (aggr) aggr->endup(); - if (Item_sum_sum::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) { longlong result; my_decimal2int(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, unsigned_flag, @@ -1450,7 +1397,7 @@ double Item_sum_sum::val_real() DBUG_ASSERT(fixed == 1); if (aggr) aggr->endup(); - if (Item_sum_sum::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) my_decimal2double(E_DEC_FATAL_ERROR, dec_buffs + curr_dec_buff, &sum); return sum; } @@ -1460,7 +1407,7 @@ String *Item_sum_sum::val_str(String *str) { if (aggr) aggr->endup(); - if (Item_sum_sum::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) return val_string_from_decimal(str); return val_string_from_real(str); } @@ -1470,7 +1417,7 @@ my_decimal *Item_sum_sum::val_decimal(my_decimal *val) { if (aggr) aggr->endup(); - if (Item_sum_sum::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) return null_value ? NULL : (dec_buffs + curr_dec_buff); return val_decimal_from_real(val); } @@ -1662,28 +1609,39 @@ void Item_sum_count::cleanup() /* Avgerage */ + +void Item_sum_avg::fix_length_and_dec_decimal() +{ + Item_sum_sum::fix_length_and_dec_decimal(); + int precision= args[0]->decimal_precision() + prec_increment; + decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); + max_length= my_decimal_precision_to_length_no_truncation(precision, + decimals, + unsigned_flag); + f_precision= MY_MIN(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION); + f_scale= args[0]->decimals; + dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale); +} + + +void Item_sum_avg::fix_length_and_dec_double() +{ + Item_sum_sum::fix_length_and_dec_double(); + decimals= MY_MIN(args[0]->decimals + prec_increment, + FLOATING_POINT_DECIMALS); + max_length= MY_MIN(args[0]->max_length + prec_increment, float_length(decimals)); +} + + void Item_sum_avg::fix_length_and_dec() { - Item_sum_sum::fix_length_and_dec(); - maybe_null=null_value=1; + DBUG_ENTER("Item_sum_avg::fix_length_and_dec"); prec_increment= current_thd->variables.div_precincrement; - if (Item_sum_avg::result_type() == DECIMAL_RESULT) - { - int precision= args[0]->decimal_precision() + prec_increment; - decimals= MY_MIN(args[0]->decimals + prec_increment, DECIMAL_MAX_SCALE); - max_length= my_decimal_precision_to_length_no_truncation(precision, - decimals, - unsigned_flag); - f_precision= MY_MIN(precision+DECIMAL_LONGLONG_DIGITS, DECIMAL_MAX_PRECISION); - f_scale= args[0]->decimals; - dec_bin_size= my_decimal_get_binary_size(f_precision, f_scale); - } - else - { - decimals= MY_MIN(args[0]->decimals + prec_increment, - FLOATING_POINT_DECIMALS); - max_length= MY_MIN(args[0]->max_length + prec_increment, float_length(decimals)); - } + maybe_null=null_value=1; + args[0]->cast_to_int_type_handler()->Item_sum_avg_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s (%d, %d)", type_handler()->name().ptr(), + max_length, (int) decimals)); + DBUG_VOID_RETURN; } @@ -1695,8 +1653,6 @@ Item *Item_sum_avg::copy_or_same(THD* thd) Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table) { - Field *field; - MEM_ROOT *mem_root= table->in_use->mem_root; if (group) { @@ -1705,19 +1661,15 @@ Field *Item_sum_avg::create_tmp_field(bool group, TABLE *table) The easiest way is to do this is to store both value in a string and unpack on access. */ - field= new (mem_root) - Field_string(((Item_sum_avg::result_type() == DECIMAL_RESULT) ? - dec_bin_size : sizeof(double)) + sizeof(longlong), - 0, name, &my_charset_bin); + Field *field= new (table->in_use->mem_root) + Field_string(((result_type() == DECIMAL_RESULT) ? + dec_bin_size : sizeof(double)) + sizeof(longlong), + 0, &name, &my_charset_bin); + if (field) + field->init(table); + return field; } - else if (Item_sum_avg::result_type() == DECIMAL_RESULT) - field= Field_new_decimal::create_from_item(mem_root, this); - else - field= new (mem_root) Field_double(max_length, maybe_null, name, decimals, - TRUE); - if (field) - field->init(table); - return field; + return tmp_table_field_from_field_type(table); } @@ -1778,7 +1730,7 @@ my_decimal *Item_sum_avg::val_decimal(my_decimal *val) For non-DECIMAL result_type() the division will be done in Item_sum_avg::val_real(). */ - if (Item_sum_avg::result_type() != DECIMAL_RESULT) + if (result_type() != DECIMAL_RESULT) return val_decimal_from_real(val); sum_dec= dec_buffs + curr_dec_buff; @@ -1792,7 +1744,7 @@ String *Item_sum_avg::val_str(String *str) { if (aggr) aggr->endup(); - if (Item_sum_avg::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) return val_string_from_decimal(str); return val_string_from_real(str); } @@ -1882,6 +1834,25 @@ Item_sum_variance::Item_sum_variance(THD *thd, Item_sum_variance *item): } +void Item_sum_variance::fix_length_and_dec_double() +{ + DBUG_ASSERT(Item_sum_variance::type_handler() == &type_handler_double); + decimals= MY_MIN(args[0]->decimals + 4, FLOATING_POINT_DECIMALS); +} + + +void Item_sum_variance::fix_length_and_dec_decimal() +{ + DBUG_ASSERT(Item_sum_variance::type_handler() == &type_handler_double); + int precision= args[0]->decimal_precision() * 2 + prec_increment; + decimals= MY_MIN(args[0]->decimals + prec_increment, + FLOATING_POINT_DECIMALS - 1); + max_length= my_decimal_precision_to_length_no_truncation(precision, + decimals, + unsigned_flag); +} + + void Item_sum_variance::fix_length_and_dec() { DBUG_ENTER("Item_sum_variance::fix_length_and_dec"); @@ -1895,28 +1866,9 @@ void Item_sum_variance::fix_length_and_dec() type. */ - switch (args[0]->result_type()) { - case REAL_RESULT: - case STRING_RESULT: - decimals= MY_MIN(args[0]->decimals + 4, FLOATING_POINT_DECIMALS); - break; - case INT_RESULT: - case DECIMAL_RESULT: - { - int precision= args[0]->decimal_precision()*2 + prec_increment; - decimals= MY_MIN(args[0]->decimals + prec_increment, - FLOATING_POINT_DECIMALS-1); - max_length= my_decimal_precision_to_length_no_truncation(precision, - decimals, - unsigned_flag); - - break; - } - case ROW_RESULT: - case TIME_RESULT: - DBUG_ASSERT(0); - } - DBUG_PRINT("info", ("Type: REAL_RESULT (%d, %d)", max_length, (int)decimals)); + args[0]->type_handler()->Item_sum_variance_fix_length_and_dec(this); + DBUG_PRINT("info", ("Type: %s (%d, %d)", type_handler()->name().ptr(), + max_length, (int)decimals)); DBUG_VOID_RETURN; } @@ -1942,10 +1894,12 @@ Field *Item_sum_variance::create_tmp_field(bool group, TABLE *table) The easiest way is to do this is to store both value in a string and unpack on access. */ - field= new Field_string(sizeof(double)*2 + sizeof(longlong), 0, name, &my_charset_bin); + field= new Field_string(sizeof(double)*2 + sizeof(longlong), 0, + &name, &my_charset_bin); } else - field= new Field_double(max_length, maybe_null, name, decimals, TRUE); + field= new Field_double(max_length, maybe_null, &name, decimals, + TRUE); if (field != NULL) field->init(table); @@ -2373,7 +2327,7 @@ void Item_sum_num::reset_field() void Item_sum_hybrid::reset_field() { - switch(Item_sum_hybrid::result_type()) { + switch(result_type()) { case STRING_RESULT: { char buff[MAX_FIELD_WIDTH]; @@ -2456,7 +2410,7 @@ void Item_sum_hybrid::reset_field() void Item_sum_sum::reset_field() { DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); - if (Item_sum_sum::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) { my_decimal value, *arg_val= args[0]->val_decimal(&value); if (!arg_val) // Null @@ -2492,7 +2446,7 @@ void Item_sum_avg::reset_field() { uchar *res=result_field->ptr; DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); - if (Item_sum_avg::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) { longlong tmp; my_decimal value, *arg_dec= args[0]->val_decimal(&value); @@ -2550,7 +2504,7 @@ void Item_sum_bit::update_field() void Item_sum_sum::update_field() { DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); - if (Item_sum_sum::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) { my_decimal value, *arg_val= args[0]->val_decimal(&value); if (!args[0]->null_value) @@ -2605,7 +2559,7 @@ void Item_sum_avg::update_field() DBUG_ASSERT (aggr->Aggrtype() != Aggregator::DISTINCT_AGGREGATOR); - if (Item_sum_avg::result_type() == DECIMAL_RESULT) + if (result_type() == DECIMAL_RESULT) { my_decimal value, *arg_val= args[0]->val_decimal(&value); if (!args[0]->null_value) @@ -2644,7 +2598,7 @@ void Item_sum_avg::update_field() Item *Item_sum_avg::result_item(THD *thd, Field *field) { return - Item_sum_avg::result_type() == DECIMAL_RESULT ? + result_type() == DECIMAL_RESULT ? (Item_avg_field*) new (thd->mem_root) Item_avg_field_decimal(thd, this) : (Item_avg_field*) new (thd->mem_root) Item_avg_field_double(thd, this); } @@ -2652,7 +2606,7 @@ Item *Item_sum_avg::result_item(THD *thd, Field *field) void Item_sum_hybrid::update_field() { - switch (Item_sum_hybrid::result_type()) { + switch (result_type()) { case STRING_RESULT: min_max_update_str_field(); break; @@ -3384,24 +3338,6 @@ void Item_func_group_concat::cleanup() } -Field *Item_func_group_concat::make_string_field(TABLE *table_arg) -{ - Field *field; - DBUG_ASSERT(collation.collation); - if (too_big_for_varchar()) - field= new Field_blob(max_length, - maybe_null, name, collation.collation, TRUE); - else - field= new Field_varstring(max_length, - maybe_null, name, table_arg->s, - collation.collation); - - if (field) - field->init(table_arg); - return field; -} - - Item *Item_func_group_concat::copy_or_same(THD* thd) { return new (thd->mem_root) Item_func_group_concat(thd, this); diff --git a/sql/item_sum.h b/sql/item_sum.h index 0c655ded153..7845ed3318f 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -456,7 +456,6 @@ public: Updated value is then saved in the field. */ virtual void update_field()=0; - virtual bool keep_field_type(void) const { return 0; } virtual void fix_length_and_dec() { maybe_null=1; null_value=1; } virtual Item *result_item(THD *thd, Field *field); @@ -510,10 +509,7 @@ public: } virtual void make_unique() { force_copy_fields= TRUE; } Item *get_tmp_table_item(THD *thd); - Field *create_tmp_field(bool group, TABLE *table) - { - return Item::create_tmp_field(group, table, MY_INT32_NUM_DECIMAL_DIGITS); - } + Field *create_tmp_field(bool group, TABLE *table); virtual bool collect_outer_ref_processor(void *param); bool init_sum_func_check(THD *thd); bool check_sum_func(THD *thd, Item **ref); @@ -521,7 +517,7 @@ public: st_select_lex *depended_from() { return (nest_level == aggr_level ? 0 : aggr_sel); } - Item *get_arg(uint i) { return args[i]; } + Item *get_arg(uint i) const { return args[i]; } Item *set_arg(uint i, THD *thd, Item *new_val); uint get_arg_count() const { return arg_count; } @@ -750,8 +746,7 @@ public: double val_real() { DBUG_ASSERT(fixed == 1); return (double) val_int(); } String *val_str(String*str); my_decimal *val_decimal(my_decimal *); - enum Item_result result_type () const { return INT_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const { return &type_handler_longlong; } void fix_length_and_dec() { decimals=0; max_length=21; maybe_null=null_value=0; } }; @@ -783,12 +778,10 @@ public: longlong val_int(); String *val_str(String*str); my_decimal *val_decimal(my_decimal *); - enum_field_types field_type() const - { return Type_handler_hybrid_field_type::field_type(); } - enum Item_result result_type () const - { return Type_handler_hybrid_field_type::result_type(); } - enum Item_result cmp_type () const - { return Type_handler_hybrid_field_type::cmp_type(); } + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } + void fix_length_and_dec_double(); + void fix_length_and_dec_decimal(); void reset_field(); void update_field(); void no_rows_in_result() {} @@ -888,6 +881,8 @@ public: :Item_sum_sum(thd, item), count(item->count), prec_increment(item->prec_increment) {} + void fix_length_and_dec_double(); + void fix_length_and_dec_decimal(); void fix_length_and_dec(); enum Sumfunctype sum_func () const { @@ -962,6 +957,8 @@ public: {} Item_sum_variance(THD *thd, Item_sum_variance *item); enum Sumfunctype sum_func () const { return VARIANCE_FUNC; } + void fix_length_and_dec_double(); + void fix_length_and_dec_decimal(); void clear(); bool add(); double val_real(); @@ -974,8 +971,7 @@ public: { return sample ? "var_samp(" : "variance("; } Item *copy_or_same(THD* thd); Field *create_tmp_field(bool group, TABLE *table); - enum Item_result result_type () const { return REAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE;} + const Type_handler *type_handler() const { return &type_handler_double; } void cleanup() { count= 0; @@ -1021,7 +1017,7 @@ protected: public: Item_sum_hybrid(THD *thd, Item *item_par,int sign): Item_sum(thd, item_par), - Type_handler_hybrid_field_type(MYSQL_TYPE_LONGLONG), + Type_handler_hybrid_field_type(&type_handler_longlong), value(0), arg_cache(0), cmp(0), cmp_sign(sign), was_values(TRUE) { collation.set(&my_charset_bin); } @@ -1032,6 +1028,7 @@ protected: cmp_sign(item->cmp_sign), was_values(item->was_values) { } bool fix_fields(THD *, Item **); + void fix_length_and_dec(); void setup_hybrid(THD *thd, Item *item, Item *value_arg); void clear(); double val_real(); @@ -1039,13 +1036,13 @@ protected: my_decimal *val_decimal(my_decimal *); void reset_field(); String *val_str(String *); - bool keep_field_type(void) const { return 1; } - enum Item_result result_type () const - { return Type_handler_hybrid_field_type::result_type(); } - enum Item_result cmp_type () const - { return Type_handler_hybrid_field_type::cmp_type(); } - enum enum_field_types field_type() const - { return Type_handler_hybrid_field_type::field_type(); } + const Type_handler *real_type_handler() const + { + return get_arg(0)->real_type_handler(); + } + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } + TYPELIB *get_typelib() const { return args[0]->get_typelib(); } void update_field(); void min_max_update_str_field(); void min_max_update_real_field(); @@ -1141,7 +1138,7 @@ public: } protected: - static const int NUM_BIT_COUNTERS= 64; + enum bit_counters { NUM_BIT_COUNTERS= 64 }; ulonglong reset_bits,bits; /* Marks whether the function is to be computed as a window function. @@ -1227,7 +1224,7 @@ public: void save_in_result_field(bool no_conversions) { DBUG_ASSERT(0); } bool check_vcol_func_processor(void *arg) { - return mark_unsupported_function(name, arg, VCOL_IMPOSSIBLE); + return mark_unsupported_function(name.str, arg, VCOL_IMPOSSIBLE); } }; @@ -1251,8 +1248,7 @@ public: Item_avg_field_double(THD *thd, Item_sum_avg *item) :Item_avg_field(thd, item) { } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } - enum Item_result result_type () const { return REAL_RESULT; } + const Type_handler *type_handler() const { return &type_handler_double; } longlong val_int() { return val_int_from_real(); } my_decimal *val_decimal(my_decimal *dec) { return val_decimal_from_real(dec); } String *val_str(String *str) { return val_string_from_real(str); } @@ -1272,8 +1268,7 @@ public: f_scale(item->f_scale), dec_bin_size(item->dec_bin_size) { } - enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; } - enum Item_result result_type () const { return DECIMAL_RESULT; } + const Type_handler *type_handler() const { return &type_handler_newdecimal; } double val_real() { return val_real_from_decimal(); } longlong val_int() { return val_int_from_decimal(); } String *val_str(String *str) { return val_string_from_decimal(str); } @@ -1298,8 +1293,7 @@ public: my_decimal *val_decimal(my_decimal *dec_buf) { return val_decimal_from_real(dec_buf); } bool is_null() { update_null_value(); return null_value; } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } - enum Item_result result_type () const { return REAL_RESULT; } + const Type_handler *type_handler() const { return &type_handler_double; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_variance_field>(thd, mem_root, this); } }; @@ -1397,9 +1391,7 @@ class Item_sum_udf_float :public Item_udf_sum double val_real(); String *val_str(String*str); my_decimal *val_decimal(my_decimal *); - enum Item_result result_type () const { return REAL_RESULT; } - enum Item_result cmp_type () const { return REAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + const Type_handler *type_handler() const { return &type_handler_double; } void fix_length_and_dec() { fix_num_length_and_dec(); } Item *copy_or_same(THD* thd); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -1421,8 +1413,7 @@ public: { DBUG_ASSERT(fixed == 1); return (double) Item_sum_udf_int::val_int(); } String *val_str(String*str); my_decimal *val_decimal(my_decimal *); - enum Item_result result_type () const { return INT_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const { return &type_handler_longlong; } void fix_length_and_dec() { decimals=0; max_length=21; } Item *copy_or_same(THD* thd); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -1463,8 +1454,7 @@ public: return cs->cset->strtoll10(cs, res->ptr(), &end, &err_not_used); } my_decimal *val_decimal(my_decimal *dec); - enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return string_field_type(); } + const Type_handler *type_handler() const { return string_type_handler(); } void fix_length_and_dec(); Item *copy_or_same(THD* thd); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -1485,8 +1475,7 @@ public: double val_real(); longlong val_int(); my_decimal *val_decimal(my_decimal *); - enum Item_result result_type () const { return DECIMAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; } + const Type_handler *type_handler() const { return &type_handler_newdecimal; } void fix_length_and_dec() { fix_num_length_and_dec(); } Item *copy_or_same(THD* thd); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -1561,7 +1550,6 @@ public: { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } double val_real() { DBUG_ASSERT(fixed == 1); null_value=1; return 0.0; } longlong val_int() { DBUG_ASSERT(fixed == 1); null_value=1; return 0; } - enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec() { maybe_null=1; max_length=0; } enum Sumfunctype sum_func () const { return UDF_SUM_FUNC; } void clear() {} @@ -1624,9 +1612,6 @@ class Item_func_group_concat : public Item_sum friend int dump_leaf_key(void* key_arg, element_count count __attribute__((unused)), void* item_arg); -protected: - virtual Field *make_string_field(TABLE *table); - public: Item_func_group_concat(THD *thd, Name_resolution_context *context_arg, bool is_distinct, List<Item> *is_select, @@ -1638,14 +1623,11 @@ public: enum Sumfunctype sum_func () const {return GROUP_CONCAT_FUNC;} const char *func_name() const { return "group_concat("; } - virtual Item_result result_type () const { return STRING_RESULT; } - virtual Item_result cmp_type () const { return STRING_RESULT; } - enum_field_types field_type() const + const Type_handler *type_handler() const { if (too_big_for_varchar()) - return MYSQL_TYPE_BLOB; - else - return MYSQL_TYPE_VARCHAR; + return &type_handler_blob; + return &type_handler_varchar; } void clear(); bool add(); diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc index 2724bb63eda..885db3a64f1 100644 --- a/sql/item_timefunc.cc +++ b/sql/item_timefunc.cc @@ -455,7 +455,8 @@ err: */ static bool make_date_time(DATE_TIME_FORMAT *format, MYSQL_TIME *l_time, - timestamp_type type, MY_LOCALE *locale, String *str) + timestamp_type type, const MY_LOCALE *locale, + String *str) { char intbuff[15]; uint hours_i; @@ -1470,34 +1471,6 @@ bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval) } -void Item_temporal_func::fix_length_and_dec() -{ - uint char_length= mysql_temporal_int_part_length(field_type()); - /* - We set maybe_null to 1 as default as any bad argument with date or - time can get us to return NULL. - */ - maybe_null= (arg_count > 0); - if (decimals) - { - if (decimals == NOT_FIXED_DEC) - char_length+= TIME_SECOND_PART_DIGITS + 1; - else - { - set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); - char_length+= decimals + 1; - } - } - sql_mode= current_thd->variables.sql_mode & - (MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE); - collation.set(field_type() == MYSQL_TYPE_STRING ? - default_charset() : &my_charset_numeric, - field_type() == MYSQL_TYPE_STRING ? - DERIVATION_COERCIBLE : DERIVATION_NUMERIC, - MY_REPERTOIRE_ASCII); - fix_char_length(char_length); -} - String *Item_temporal_func::val_str(String *str) { DBUG_ASSERT(fixed == 1); @@ -1572,7 +1545,7 @@ String *Item_temporal_hybrid_func::val_str_ascii(String *str) /* Check that the returned timestamp type matches to the function type */ DBUG_ASSERT(field_type() == MYSQL_TYPE_STRING || ltime.time_type == MYSQL_TIMESTAMP_NONE || - mysql_type_to_time_type(field_type()) == ltime.time_type); + ltime.time_type == mysql_timestamp_type()); return str; } @@ -1861,7 +1834,14 @@ overflow: void Item_func_date_format::fix_length_and_dec() { THD* thd= current_thd; - locale= thd->variables.lc_time_names; + if (!is_time_format) + { + if (arg_count < 3) + locale= thd->variables.lc_time_names; + else + if (args[2]->basic_const_item()) + locale= args[2]->locale_from_val_str(); + } /* Must use this_item() in case it's a local SP variable @@ -1903,6 +1883,8 @@ bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const if (this == item) return 1; item_func= (Item_func_date_format*) item; + if (arg_count != item_func->arg_count) + return 0; if (!args[0]->eq(item_func->args[0], binary_cmp)) return 0; /* @@ -1912,6 +1894,8 @@ bool Item_func_date_format::eq(const Item *item, bool binary_cmp) const */ if (!args[1]->eq(item_func->args[1], 1)) return 0; + if (arg_count > 2 && !args[2]->eq(item_func->args[2], 1)) + return 0; return 1; } @@ -1995,15 +1979,18 @@ String *Item_func_date_format::val_str(String *str) String *format; MYSQL_TIME l_time; uint size; - int is_time_flag = is_time_format ? TIME_TIME_ONLY : 0; + const MY_LOCALE *lc= 0; DBUG_ASSERT(fixed == 1); - if (get_arg0_date(&l_time, is_time_flag)) + if (get_arg0_date(&l_time, is_time_format ? TIME_TIME_ONLY : 0)) return 0; if (!(format = args[1]->val_str(str)) || !format->length()) goto null_date; + if (!is_time_format && !(lc= locale) && !(lc= args[2]->locale_from_val_str())) + goto null_date; // invalid locale + if (fixed_length) size=max_length; else @@ -2026,7 +2013,7 @@ String *Item_func_date_format::val_str(String *str) if (!make_date_time(&date_time_format, &l_time, is_time_format ? MYSQL_TIMESTAMP_TIME : MYSQL_TIMESTAMP_DATE, - locale, str)) + lc, str)) return str; null_date: @@ -2040,8 +2027,8 @@ void Item_func_from_unixtime::fix_length_and_dec() THD *thd= current_thd; thd->time_zone_used= 1; tz= thd->variables.time_zone; - decimals= args[0]->decimals; - Item_temporal_func::fix_length_and_dec(); + fix_attributes_datetime_not_fixed_dec(args[0]->decimals); + maybe_null= true; } @@ -2068,13 +2055,6 @@ bool Item_func_from_unixtime::get_date(MYSQL_TIME *ltime, } -void Item_func_convert_tz::fix_length_and_dec() -{ - decimals= args[0]->temporal_precision(MYSQL_TYPE_DATETIME); - Item_temporal_func::fix_length_and_dec(); -} - - bool Item_func_convert_tz::get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date __attribute__((unused))) { @@ -2124,6 +2104,13 @@ void Item_date_add_interval::fix_length_and_dec() { enum_field_types arg0_field_type; + if (!args[0]->type_handler()->is_traditional_type()) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + args[0]->type_handler()->name().ptr(), + "interval", func_name()); + return; + } /* The field type for the result of an Item_datefunc is defined as follows: @@ -2139,7 +2126,6 @@ void Item_date_add_interval::fix_length_and_dec() (This is because you can't know if the string contains a DATE, MYSQL_TIME or DATETIME argument) */ - set_handler_by_field_type(MYSQL_TYPE_STRING); arg0_field_type= args[0]->field_type(); uint interval_dec= 0; if (int_type == INTERVAL_MICROSECOND || @@ -2152,30 +2138,45 @@ void Item_date_add_interval::fix_length_and_dec() if (arg0_field_type == MYSQL_TYPE_DATETIME || arg0_field_type == MYSQL_TYPE_TIMESTAMP) { - decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec); - set_handler_by_field_type(MYSQL_TYPE_DATETIME); + uint dec= MY_MAX(args[0]->datetime_precision(), interval_dec); + set_handler(&type_handler_datetime); + fix_attributes_datetime(dec); } else if (arg0_field_type == MYSQL_TYPE_DATE) { if (int_type <= INTERVAL_DAY || int_type == INTERVAL_YEAR_MONTH) - set_handler_by_field_type(arg0_field_type); + { + set_handler(&type_handler_newdate); + fix_attributes_date(); + } else { - decimals= interval_dec; - set_handler_by_field_type(MYSQL_TYPE_DATETIME); + set_handler(&type_handler_datetime2); + fix_attributes_datetime(interval_dec); } } else if (arg0_field_type == MYSQL_TYPE_TIME) { - decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME), interval_dec); + uint dec= MY_MAX(args[0]->time_precision(), interval_dec); if (int_type >= INTERVAL_DAY && int_type != INTERVAL_YEAR_MONTH) - set_handler_by_field_type(arg0_field_type); + { + set_handler(&type_handler_time2); + fix_attributes_time(dec); + } else - set_handler_by_field_type(MYSQL_TYPE_DATETIME); + { + set_handler(&type_handler_datetime2); + fix_attributes_datetime(dec); + } } else - decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), interval_dec); - Item_temporal_func::fix_length_and_dec(); + { + uint dec= MY_MAX(args[0]->datetime_precision(), interval_dec); + set_handler(&type_handler_string); + collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); + fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); + } + maybe_null= true; } @@ -2533,7 +2534,23 @@ end: } -void Item_char_typecast::fix_length_and_dec() +void Item_char_typecast::fix_length_and_dec_numeric() +{ + fix_length_and_dec_internal(from_cs= cast_cs->mbminlen == 1 ? + cast_cs : + &my_charset_latin1); +} + + +void Item_char_typecast::fix_length_and_dec_str() +{ + fix_length_and_dec_internal(from_cs= args[0]->dynamic_result() ? + 0 : + args[0]->collation.collation); +} + + +void Item_char_typecast::fix_length_and_dec_internal(CHARSET_INFO *from_cs) { uint32 char_length; /* @@ -2563,12 +2580,6 @@ void Item_char_typecast::fix_length_and_dec() Note (TODO): we could use repertoire technique here. */ - from_cs= ((args[0]->result_type() == INT_RESULT || - args[0]->result_type() == DECIMAL_RESULT || - args[0]->result_type() == REAL_RESULT) ? - (cast_cs->mbminlen == 1 ? cast_cs : &my_charset_latin1) : - args[0]->dynamic_result() ? 0 : - args[0]->collation.collation); charset_conversion= !from_cs || (cast_cs->mbmaxlen > 1) || (!my_charset_same(from_cs, cast_cs) && from_cs != &my_charset_bin && @@ -2670,8 +2681,15 @@ err: void Item_func_add_time::fix_length_and_dec() { enum_field_types arg0_field_type; - decimals= MY_MAX(args[0]->decimals, args[1]->decimals); + if (!args[0]->type_handler()->is_traditional_type() || + !args[1]->type_handler()->is_traditional_type()) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + args[0]->type_handler()->name().ptr(), + args[1]->type_handler()->name().ptr(), func_name()); + return; + } /* The field type for the result of an Item_func_add_time function is defined as follows: @@ -2682,24 +2700,30 @@ void Item_func_add_time::fix_length_and_dec() - Otherwise the result is MYSQL_TYPE_STRING */ - set_handler_by_field_type(MYSQL_TYPE_STRING); arg0_field_type= args[0]->field_type(); if (arg0_field_type == MYSQL_TYPE_DATE || arg0_field_type == MYSQL_TYPE_DATETIME || arg0_field_type == MYSQL_TYPE_TIMESTAMP || is_date) { - set_handler_by_field_type(MYSQL_TYPE_DATETIME); - decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_DATETIME), - args[1]->temporal_precision(MYSQL_TYPE_TIME)); + uint dec= MY_MAX(args[0]->datetime_precision(), args[1]->time_precision()); + set_handler(&type_handler_datetime2); + fix_attributes_datetime(dec); } else if (arg0_field_type == MYSQL_TYPE_TIME) { - set_handler_by_field_type(MYSQL_TYPE_TIME); - decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME), - args[1]->temporal_precision(MYSQL_TYPE_TIME)); + uint dec= MY_MAX(args[0]->time_precision(), args[1]->time_precision()); + set_handler(&type_handler_time2); + fix_attributes_time(dec); + } + else + { + uint dec= MY_MAX(args[0]->decimals, args[1]->decimals); + set_handler(&type_handler_string); + collation.set(default_charset(), DERIVATION_COERCIBLE, MY_REPERTOIRE_ASCII); + fix_char_length_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); } - Item_temporal_func::fix_length_and_dec(); + maybe_null= true; } /** @@ -3190,6 +3214,14 @@ get_date_time_result_type(const char *format, uint length) void Item_func_str_to_date::fix_length_and_dec() { + if (!args[0]->type_handler()->is_traditional_type() || + !args[1]->type_handler()->is_traditional_type()) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + args[0]->type_handler()->name().ptr(), + args[1]->type_handler()->name().ptr(), func_name()); + return; + } if (agg_arg_charsets(collation, args, 2, MY_COLL_ALLOW_CONV, 1)) return; if (collation.collation->mbminlen > 1) @@ -3201,8 +3233,10 @@ void Item_func_str_to_date::fix_length_and_dec() #endif } - set_handler_by_field_type(MYSQL_TYPE_DATETIME); - decimals= TIME_SECOND_PART_DIGITS; + maybe_null= true; + set_handler(&type_handler_datetime2); + fix_attributes_datetime(TIME_SECOND_PART_DIGITS); + if ((const_item= args[1]->const_item())) { char format_buff[64]; @@ -3216,25 +3250,29 @@ void Item_func_str_to_date::fix_length_and_dec() get_date_time_result_type(format->ptr(), format->length()); switch (cached_format_type) { case DATE_ONLY: - set_handler_by_field_type(MYSQL_TYPE_DATE); + set_handler(&type_handler_newdate); + fix_attributes_date(); break; case TIME_MICROSECOND: - decimals= 6; - /* fall through */ + set_handler(&type_handler_time2); + fix_attributes_time(TIME_SECOND_PART_DIGITS); + break; case TIME_ONLY: - set_handler_by_field_type(MYSQL_TYPE_TIME); + set_handler(&type_handler_time2); + fix_attributes_time(0); break; case DATE_TIME_MICROSECOND: - decimals= 6; - /* fall through */ + set_handler(&type_handler_datetime2); + fix_attributes_datetime(TIME_SECOND_PART_DIGITS); + break; case DATE_TIME: - set_handler_by_field_type(MYSQL_TYPE_DATETIME); + set_handler(&type_handler_datetime2); + fix_attributes_datetime(0); break; } } } - cached_timestamp_type= mysql_type_to_time_type(field_type()); - Item_temporal_func::fix_length_and_dec(); + cached_timestamp_type= mysql_timestamp_type(); } diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index dccccc479ef..65057a71c4e 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -31,22 +31,35 @@ enum date_time_format_types }; -static inline uint -mysql_temporal_int_part_length(enum enum_field_types mysql_type) +bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval); + + +class Item_long_func_date_field: public Item_long_func { - static uint max_time_type_width[5]= - { MAX_DATETIME_WIDTH, MAX_DATETIME_WIDTH, MAX_DATE_WIDTH, - MAX_DATETIME_WIDTH, MIN_TIME_WIDTH }; - return max_time_type_width[mysql_type_to_time_type(mysql_type)+2]; -} + bool check_arguments() const + { return args[0]->check_type_can_return_date(func_name()); } +public: + Item_long_func_date_field(THD *thd, Item *a) + :Item_long_func(thd, a) { } +}; -bool get_interval_value(Item *args,interval_type int_type, INTERVAL *interval); +class Item_long_func_time_field: public Item_long_func +{ + bool check_arguments() const + { return args[0]->check_type_can_return_time(func_name()); } +public: + Item_long_func_time_field(THD *thd, Item *a) + :Item_long_func(thd, a) { } +}; + -class Item_func_period_add :public Item_int_func +class Item_func_period_add :public Item_long_func { + bool check_arguments() const + { return check_argument_types_can_return_int(0, 2); } public: - Item_func_period_add(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} + Item_func_period_add(THD *thd, Item *a, Item *b): Item_long_func(thd, a, b) {} longlong val_int(); const char *func_name() const { return "period_add"; } void fix_length_and_dec() @@ -58,10 +71,12 @@ public: }; -class Item_func_period_diff :public Item_int_func +class Item_func_period_diff :public Item_long_func { + bool check_arguments() const + { return check_argument_types_can_return_int(0, 2); } public: - Item_func_period_diff(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} + Item_func_period_diff(THD *thd, Item *a, Item *b): Item_long_func(thd, a, b) {} longlong val_int(); const char *func_name() const { return "period_diff"; } void fix_length_and_dec() @@ -74,10 +89,10 @@ public: }; -class Item_func_to_days :public Item_int_func +class Item_func_to_days :public Item_long_func_date_field { public: - Item_func_to_days(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_to_days(THD *thd, Item *a): Item_long_func_date_field(thd, a) {} longlong val_int(); const char *func_name() const { return "to_days"; } void fix_length_and_dec() @@ -99,16 +114,18 @@ public: }; -class Item_func_to_seconds :public Item_int_func +class Item_func_to_seconds :public Item_longlong_func { + bool check_arguments() const + { return check_argument_types_can_return_date(0, arg_count); } public: - Item_func_to_seconds(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_to_seconds(THD *thd, Item *a): Item_longlong_func(thd, a) {} longlong val_int(); const char *func_name() const { return "to_seconds"; } void fix_length_and_dec() { decimals=0; - max_length=6*MY_CHARSET_BIN_MB_MAXLEN; + fix_char_length(12); maybe_null= 1; } enum_monotonicity_info get_monotonicity_info() const; @@ -125,10 +142,10 @@ public: }; -class Item_func_dayofmonth :public Item_int_func +class Item_func_dayofmonth :public Item_long_func_date_field { public: - Item_func_dayofmonth(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_dayofmonth(THD *thd, Item *a): Item_long_func_date_field(thd, a) {} longlong val_int(); const char *func_name() const { return "dayofmonth"; } void fix_length_and_dec() @@ -165,8 +182,7 @@ public: return str; } const char *func_name() const { return "month"; } - enum Item_result result_type () const { return INT_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const { return &type_handler_long; } void fix_length_and_dec() { decimals= 0; @@ -206,10 +222,10 @@ public: }; -class Item_func_dayofyear :public Item_int_func +class Item_func_dayofyear :public Item_long_func_date_field { public: - Item_func_dayofyear(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_dayofyear(THD *thd, Item *a): Item_long_func_date_field(thd, a) {} longlong val_int(); const char *func_name() const { return "dayofyear"; } void fix_length_and_dec() @@ -229,10 +245,10 @@ public: }; -class Item_func_hour :public Item_int_func +class Item_func_hour :public Item_long_func_time_field { public: - Item_func_hour(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_hour(THD *thd, Item *a): Item_long_func_time_field(thd, a) {} longlong val_int(); const char *func_name() const { return "hour"; } void fix_length_and_dec() @@ -252,10 +268,10 @@ public: }; -class Item_func_minute :public Item_int_func +class Item_func_minute :public Item_long_func_time_field { public: - Item_func_minute(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_minute(THD *thd, Item *a): Item_long_func_time_field(thd, a) {} longlong val_int(); const char *func_name() const { return "minute"; } void fix_length_and_dec() @@ -275,10 +291,10 @@ public: }; -class Item_func_quarter :public Item_int_func +class Item_func_quarter :public Item_long_func_date_field { public: - Item_func_quarter(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_quarter(THD *thd, Item *a): Item_long_func_date_field(thd, a) {} longlong val_int(); const char *func_name() const { return "quarter"; } void fix_length_and_dec() @@ -298,10 +314,10 @@ public: }; -class Item_func_second :public Item_int_func +class Item_func_second :public Item_long_func_time_field { public: - Item_func_second(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_second(THD *thd, Item *a): Item_long_func_time_field(thd, a) {} longlong val_int(); const char *func_name() const { return "second"; } void fix_length_and_dec() @@ -321,11 +337,16 @@ public: }; -class Item_func_week :public Item_int_func +class Item_func_week :public Item_long_func { + bool check_arguments() const + { + return args[0]->check_type_can_return_date(func_name()) || + (arg_count > 1 && args[1]->check_type_can_return_int(func_name())); + } public: - Item_func_week(THD *thd, Item *a): Item_int_func(thd, a) {} - Item_func_week(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} + Item_func_week(THD *thd, Item *a): Item_long_func(thd, a) {} + Item_func_week(THD *thd, Item *a, Item *b): Item_long_func(thd, a, b) {} longlong val_int(); const char *func_name() const { return "week"; } void fix_length_and_dec() @@ -348,10 +369,16 @@ public: { return get_item_copy<Item_func_week>(thd, mem_root, this); } }; -class Item_func_yearweek :public Item_int_func +class Item_func_yearweek :public Item_long_func { + bool check_arguments() const + { + return args[0]->check_type_can_return_date(func_name()) || + args[1]->check_type_can_return_int(func_name()); + } public: - Item_func_yearweek(THD *thd, Item *a, Item *b): Item_int_func(thd, a, b) {} + Item_func_yearweek(THD *thd, Item *a, Item *b) + :Item_long_func(thd, a, b) {} longlong val_int(); const char *func_name() const { return "yearweek"; } void fix_length_and_dec() @@ -371,10 +398,10 @@ public: }; -class Item_func_year :public Item_int_func +class Item_func_year :public Item_long_func_date_field { public: - Item_func_year(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_year(THD *thd, Item *a): Item_long_func_date_field(thd, a) {} longlong val_int(); const char *func_name() const { return "year"; } enum_monotonicity_info get_monotonicity_info() const; @@ -414,8 +441,7 @@ public: { return (odbc_type ? "dayofweek" : "weekday"); } - enum Item_result result_type () const { return INT_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const { return &type_handler_long; } void fix_length_and_dec() { decimals= 0; @@ -439,8 +465,7 @@ class Item_func_dayname :public Item_func_weekday Item_func_dayname(THD *thd, Item *a): Item_func_weekday(thd, a, 0) {} const char *func_name() const { return "dayname"; } String *val_str(String *str); - enum Item_result result_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } + const Type_handler *type_handler() const { return &type_handler_varchar; } void fix_length_and_dec(); bool check_partition_func_processor(void *int_arg) {return TRUE;} bool check_vcol_func_processor(void *arg) @@ -452,19 +477,19 @@ class Item_func_dayname :public Item_func_weekday class Item_func_seconds_hybrid: public Item_func_numhybrid { -protected: - virtual enum_field_types arg0_expected_type() const = 0; public: Item_func_seconds_hybrid(THD *thd): Item_func_numhybrid(thd) {} Item_func_seconds_hybrid(THD *thd, Item *a): Item_func_numhybrid(thd, a) {} - void fix_length_and_dec() + void fix_length_and_dec_generic(uint dec) { - if (arg_count) - decimals= args[0]->temporal_precision(arg0_expected_type()); - set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); + DBUG_ASSERT(dec <= TIME_SECOND_PART_DIGITS); + decimals= dec; max_length=17 + (decimals ? decimals + 1 : 0); maybe_null= true; - set_handler_by_result_type(decimals ? DECIMAL_RESULT : INT_RESULT); + if (decimals) + set_handler(&type_handler_newdecimal); + else + set_handler(type_handler_long_or_longlong()); } double real_op() { DBUG_ASSERT(0); return 0; } String *str_op(String *str) { DBUG_ASSERT(0); return 0; } @@ -475,8 +500,6 @@ public: class Item_func_unix_timestamp :public Item_func_seconds_hybrid { bool get_timestamp_value(my_time_t *seconds, ulong *second_part); -protected: - enum_field_types arg0_expected_type() const { return MYSQL_TYPE_DATETIME; } public: Item_func_unix_timestamp(THD *thd): Item_func_seconds_hybrid(thd) {} Item_func_unix_timestamp(THD *thd, Item *a): @@ -500,6 +523,10 @@ public: return FALSE; return mark_unsupported_function(func_name(), "()", arg, VCOL_TIME_FUNC); } + void fix_length_and_dec() + { + fix_length_and_dec_generic(arg_count ? args[0]->datetime_precision() : 0); + } longlong int_op(); my_decimal *decimal_op(my_decimal* buf); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -509,8 +536,6 @@ public: class Item_func_time_to_sec :public Item_func_seconds_hybrid { -protected: - enum_field_types arg0_expected_type() const { return MYSQL_TYPE_TIME; } public: Item_func_time_to_sec(THD *thd, Item *item): Item_func_seconds_hybrid(thd, item) {} @@ -521,6 +546,10 @@ public: { return !has_time_args(); } + void fix_length_and_dec() + { + fix_length_and_dec_generic(args[0]->time_precision()); + } longlong int_op(); my_decimal *decimal_op(my_decimal* buf); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -530,25 +559,19 @@ public: class Item_temporal_func: public Item_func { - sql_mode_t sql_mode; public: Item_temporal_func(THD *thd): Item_func(thd) {} Item_temporal_func(THD *thd, Item *a): Item_func(thd, a) {} Item_temporal_func(THD *thd, Item *a, Item *b): Item_func(thd, a, b) {} Item_temporal_func(THD *thd, Item *a, Item *b, Item *c): Item_func(thd, a, b, c) {} - enum Item_result result_type () const { return STRING_RESULT; } - Item_result cmp_type() const { return TIME_RESULT; } String *val_str(String *str); longlong val_int() { return val_int_from_date(); } double val_real() { return val_real_from_date(); } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date) { DBUG_ASSERT(0); return 1; } my_decimal *val_decimal(my_decimal *decimal_value) { return val_decimal_from_date(decimal_value); } - Field *create_field_for_create_select(TABLE *table) - { return tmp_table_field_from_field_type(table, false, false); } int save_in_field(Field *field, bool no_conversions) - { return save_date_in_field(field); } - void fix_length_and_dec(); + { return save_date_in_field(field, no_conversions); } }; @@ -556,32 +579,20 @@ public: Abstract class for functions returning TIME, DATE, DATETIME or string values, whose data type depends on parameters and is set at fix_fields time. */ -class Item_temporal_hybrid_func: public Item_temporal_func, - public Type_handler_hybrid_field_type +class Item_temporal_hybrid_func: public Item_hybrid_func { protected: String ascii_buf; // Conversion buffer public: Item_temporal_hybrid_func(THD *thd, Item *a, Item *b): - Item_temporal_func(thd, a, b) {} - enum_field_types field_type() const - { return Type_handler_hybrid_field_type::field_type(); } - enum Item_result result_type () const - { return Type_handler_hybrid_field_type::result_type(); } - enum Item_result cmp_type () const - { return Type_handler_hybrid_field_type::cmp_type(); } - CHARSET_INFO *charset_for_protocol() const - { - /* - Can return TIME, DATE, DATETIME or VARCHAR depending on arguments. - Send using "binary" when TIME, DATE or DATETIME, - or using collation.collation when VARCHAR - (which is fixed from @@collation_connection in fix_length_and_dec). - */ - DBUG_ASSERT(fixed == 1); - return Item_temporal_hybrid_func::field_type() == MYSQL_TYPE_STRING ? - collation.collation : &my_charset_bin; - } + Item_hybrid_func(thd, a, b) {} + + longlong val_int() { return val_int_from_date(); } + double val_real() { return val_real_from_date(); } + bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date)= 0; + my_decimal *val_decimal(my_decimal *decimal_value) + { return val_decimal_from_date(decimal_value); } + /** Fix the returned timestamp to match field_type(), which is important for val_str(). @@ -606,7 +617,13 @@ class Item_datefunc :public Item_temporal_func public: Item_datefunc(THD *thd): Item_temporal_func(thd) { } Item_datefunc(THD *thd, Item *a): Item_temporal_func(thd, a) { } - enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + Item_datefunc(THD *thd, Item *a, Item *b): Item_temporal_func(thd, a, b) { } + const Type_handler *type_handler() const { return &type_handler_newdate; } + void fix_length_and_dec() + { + fix_attributes_date(); + maybe_null= (arg_count > 0); + } }; @@ -618,7 +635,7 @@ public: Item_timefunc(THD *thd, Item *a, Item *b): Item_temporal_func(thd, a, b) {} Item_timefunc(THD *thd, Item *a, Item *b, Item *c): Item_temporal_func(thd, a, b ,c) {} - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + const Type_handler *type_handler() const { return &type_handler_time2; } }; @@ -629,7 +646,7 @@ public: Item_datetimefunc(THD *thd, Item *a): Item_temporal_func(thd, a) {} Item_datetimefunc(THD *thd, Item *a, Item *b, Item *c): Item_temporal_func(thd, a, b ,c) {} - enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } + const Type_handler *type_handler() const { return &type_handler_datetime2; } }; @@ -643,6 +660,7 @@ public: Item_func_curtime(THD *thd, uint dec): Item_timefunc(thd), last_query_id(0) { decimals= dec; } bool fix_fields(THD *, Item **); + void fix_length_and_dec() { fix_attributes_time(decimals); } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); /* Abstract method that defines which time zone is used for conversion. @@ -729,6 +747,7 @@ public: Item_func_now(THD *thd, uint dec): Item_datetimefunc(thd), last_query_id(0) { decimals= dec; } bool fix_fields(THD *, Item **); + void fix_length_and_dec() { fix_attributes_datetime(decimals); } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); virtual void store_now_in_TIME(THD *thd, MYSQL_TIME *now_time)=0; bool check_vcol_func_processor(void *arg) @@ -799,6 +818,8 @@ public: class Item_func_from_days :public Item_datefunc { + bool check_arguments() const + { return args[0]->check_type_can_return_int(func_name()); } public: Item_func_from_days(THD *thd, Item *a): Item_datefunc(thd, a) {} const char *func_name() const { return "from_days"; } @@ -816,22 +837,24 @@ public: class Item_func_date_format :public Item_str_func { - MY_LOCALE *locale; + const MY_LOCALE *locale; int fixed_length; - const bool is_time_format; String value; +protected: + bool is_time_format; public: - Item_func_date_format(THD *thd, Item *a, Item *b, bool is_time_format_arg): - Item_str_func(thd, a, b), is_time_format(is_time_format_arg) {} + Item_func_date_format(THD *thd, Item *a, Item *b): + Item_str_func(thd, a, b), locale(0), is_time_format(false) {} + Item_func_date_format(THD *thd, Item *a, Item *b, Item *c): + Item_str_func(thd, a, b, c), locale(0), is_time_format(false) {} String *val_str(String *str); - const char *func_name() const - { return is_time_format ? "time_format" : "date_format"; } + const char *func_name() const { return "date_format"; } void fix_length_and_dec(); uint format_length(const String *format); bool eq(const Item *item, bool binary_cmp) const; bool check_vcol_func_processor(void *arg) { - if (is_time_format) + if (arg_count > 2) return false; return mark_unsupported_function(func_name(), "()", arg, VCOL_SESSION_FUNC); } @@ -839,9 +862,22 @@ public: { return get_item_copy<Item_func_date_format>(thd, mem_root, this); } }; +class Item_func_time_format: public Item_func_date_format +{ +public: + Item_func_time_format(THD *thd, Item *a, Item *b): + Item_func_date_format(thd, a, b) { is_time_format= true; } + const char *func_name() const { return "time_format"; } + bool check_vcol_func_processor(void *arg) { return false; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_time_format>(thd, mem_root, this); } +}; + class Item_func_from_unixtime :public Item_datetimefunc { + bool check_arguments() const + { return args[0]->check_type_can_return_decimal(func_name()); } Time_zone *tz; public: Item_func_from_unixtime(THD *thd, Item *a): Item_datetimefunc(thd, a) {} @@ -869,6 +905,11 @@ class Time_zone; */ class Item_func_convert_tz :public Item_datetimefunc { + bool check_arguments() const + { + return args[0]->check_type_can_return_date(func_name()) || + check_argument_types_can_return_text(1, arg_count); + } /* If time zone parameters are constants we are caching objects that represent them (we use separate from_tz_cached/to_tz_cached members @@ -881,7 +922,11 @@ class Item_func_convert_tz :public Item_datetimefunc Item_func_convert_tz(THD *thd, Item *a, Item *b, Item *c): Item_datetimefunc(thd, a, b, c), from_tz_cached(0), to_tz_cached(0) {} const char *func_name() const { return "convert_tz"; } - void fix_length_and_dec(); + void fix_length_and_dec() + { + fix_attributes_datetime(args[0]->datetime_precision()); + maybe_null= true; + } bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); void cleanup(); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -891,13 +936,15 @@ class Item_func_convert_tz :public Item_datetimefunc class Item_func_sec_to_time :public Item_timefunc { + bool check_arguments() const + { return args[0]->check_type_can_return_decimal(func_name()); } public: Item_func_sec_to_time(THD *thd, Item *item): Item_timefunc(thd, item) {} bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); void fix_length_and_dec() { - decimals= MY_MIN(args[0]->decimals, TIME_SECOND_PART_DIGITS); - Item_timefunc::fix_length_and_dec(); + fix_attributes_time(args[0]->decimals); + maybe_null= true; } const char *func_name() const { return "sec_to_time"; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -949,7 +996,7 @@ class Item_extract :public Item_int_func const interval_type int_type; // keep it public Item_extract(THD *thd, interval_type type_arg, Item *a): Item_int_func(thd, a), int_type(type_arg) {} - enum_field_types field_type() const + const Type_handler *type_handler() const { switch (int_type) { case INTERVAL_YEAR: @@ -969,16 +1016,16 @@ class Item_extract :public Item_int_func case INTERVAL_SECOND: case INTERVAL_MICROSECOND: case INTERVAL_SECOND_MICROSECOND: - return MYSQL_TYPE_LONG; + return &type_handler_long; case INTERVAL_DAY_MICROSECOND: case INTERVAL_HOUR_MICROSECOND: case INTERVAL_MINUTE_MICROSECOND: - return MYSQL_TYPE_LONGLONG; + return &type_handler_longlong; case INTERVAL_LAST: break; } DBUG_ASSERT(0); - return MYSQL_TYPE_LONGLONG; + return &type_handler_longlong; } longlong val_int(); enum Functype functype() const { return EXTRACT_FUNC; } @@ -1029,9 +1076,6 @@ class Item_extract :public Item_int_func } return true; } - Field *create_field_for_create_select(TABLE *table) - { return tmp_table_field_from_field_type(table, false, false); } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_extract>(thd, mem_root, this); } }; @@ -1048,14 +1092,21 @@ class Item_char_typecast :public Item_str_func String *copy(String *src, CHARSET_INFO *cs); uint adjusted_length_with_warn(uint length); void check_truncation_with_warn(String *src, uint dstlen); + void fix_length_and_dec_internal(CHARSET_INFO *fromcs); public: Item_char_typecast(THD *thd, Item *a, uint length_arg, CHARSET_INFO *cs_arg): Item_str_func(thd, a), cast_length(length_arg), cast_cs(cs_arg) {} enum Functype functype() const { return CHAR_TYPECAST_FUNC; } bool eq(const Item *item, bool binary_cmp) const; const char *func_name() const { return "cast_as_char"; } + CHARSET_INFO *cast_charset() const { return cast_cs; } String *val_str(String *a); - void fix_length_and_dec(); + void fix_length_and_dec_numeric(); + void fix_length_and_dec_str(); + void fix_length_and_dec() + { + args[0]->type_handler()->Item_char_typecast_fix_length_and_dec(this); + } void print(String *str, enum_query_type query_type); bool need_parentheses_in_default() { return true; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -1069,12 +1120,6 @@ public: Item_temporal_typecast(THD *thd, Item *a): Item_temporal_func(thd, a) {} virtual const char *cast_type() const = 0; void print(String *str, enum_query_type query_type); - void fix_length_and_dec() - { - if (decimals == NOT_FIXED_DEC) - decimals= args[0]->temporal_precision(field_type()); - Item_temporal_func::fix_length_and_dec(); - } }; class Item_date_typecast :public Item_temporal_typecast @@ -1084,7 +1129,11 @@ public: const char *func_name() const { return "cast_as_date"; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); const char *cast_type() const { return "date"; } - enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + const Type_handler *type_handler() const { return &type_handler_newdate; } + void fix_length_and_dec() + { + args[0]->type_handler()->Item_date_typecast_fix_length_and_dec(this); + } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_date_typecast>(thd, mem_root, this); } }; @@ -1098,7 +1147,11 @@ public: const char *func_name() const { return "cast_as_time"; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); const char *cast_type() const { return "time"; } - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + const Type_handler *type_handler() const { return &type_handler_time2; } + void fix_length_and_dec() + { + args[0]->type_handler()->Item_time_typecast_fix_length_and_dec(this); + } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_time_typecast>(thd, mem_root, this); } }; @@ -1111,20 +1164,25 @@ public: Item_temporal_typecast(thd, a) { decimals= dec_arg; } const char *func_name() const { return "cast_as_datetime"; } const char *cast_type() const { return "datetime"; } - enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } + const Type_handler *type_handler() const { return &type_handler_datetime2; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); + void fix_length_and_dec() + { + args[0]->type_handler()->Item_datetime_typecast_fix_length_and_dec(this); + } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_datetime_typecast>(thd, mem_root, this); } }; -class Item_func_makedate :public Item_temporal_func +class Item_func_makedate :public Item_datefunc { + bool check_arguments() const + { return check_argument_types_can_return_int(0, arg_count); } public: Item_func_makedate(THD *thd, Item *a, Item *b): - Item_temporal_func(thd, a, b) {} + Item_datefunc(thd, a, b) {} const char *func_name() const { return "makedate"; } - enum_field_types field_type() const { return MYSQL_TYPE_DATE; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_func_makedate>(thd, mem_root, this); } @@ -1150,14 +1208,16 @@ public: class Item_func_timediff :public Item_timefunc { + bool check_arguments() const + { return check_argument_types_can_return_time(0, arg_count); } public: Item_func_timediff(THD *thd, Item *a, Item *b): Item_timefunc(thd, a, b) {} const char *func_name() const { return "timediff"; } void fix_length_and_dec() { - decimals= MY_MAX(args[0]->temporal_precision(MYSQL_TYPE_TIME), - args[1]->temporal_precision(MYSQL_TYPE_TIME)); - Item_timefunc::fix_length_and_dec(); + uint dec= MY_MAX(args[0]->time_precision(), args[1]->time_precision()); + fix_attributes_time(dec); + maybe_null= true; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); Item *get_copy(THD *thd, MEM_ROOT *mem_root) @@ -1166,14 +1226,19 @@ public: class Item_func_maketime :public Item_timefunc { + bool check_arguments() const + { + return check_argument_types_can_return_int(0, 2) || + args[2]->check_type_can_return_decimal(func_name()); + } public: Item_func_maketime(THD *thd, Item *a, Item *b, Item *c): Item_timefunc(thd, a, b, c) {} void fix_length_and_dec() { - decimals= MY_MIN(args[2]->decimals, TIME_SECOND_PART_DIGITS); - Item_timefunc::fix_length_and_dec(); + fix_attributes_time(args[2]->decimals); + maybe_null= true; } const char *func_name() const { return "maketime"; } bool get_date(MYSQL_TIME *ltime, ulonglong fuzzy_date); @@ -1182,16 +1247,17 @@ public: }; -class Item_func_microsecond :public Item_int_func +class Item_func_microsecond :public Item_long_func_time_field { public: - Item_func_microsecond(THD *thd, Item *a): Item_int_func(thd, a) {} + Item_func_microsecond(THD *thd, Item *a): Item_long_func_time_field(thd, a) {} longlong val_int(); const char *func_name() const { return "microsecond"; } void fix_length_and_dec() { decimals=0; maybe_null=1; + fix_char_length(6); } bool check_partition_func_processor(void *int_arg) {return FALSE;} bool check_vcol_func_processor(void *arg) { return FALSE;} @@ -1204,12 +1270,14 @@ public: }; -class Item_func_timestamp_diff :public Item_int_func +class Item_func_timestamp_diff :public Item_longlong_func { + bool check_arguments() const + { return check_argument_types_can_return_date(0, arg_count); } const interval_type int_type; public: Item_func_timestamp_diff(THD *thd, Item *a, Item *b, interval_type type_arg): - Item_int_func(thd, a, b), int_type(type_arg) {} + Item_longlong_func(thd, a, b), int_type(type_arg) {} const char *func_name() const { return "timestampdiff"; } longlong val_int(); void fix_length_and_dec() @@ -1271,6 +1339,8 @@ public: class Item_func_last_day :public Item_datefunc { + bool check_arguments() const + { return args[0]->check_type_can_return_date(func_name()); } public: Item_func_last_day(THD *thd, Item *a): Item_datefunc(thd, a) {} const char *func_name() const { return "last_day"; } diff --git a/sql/item_windowfunc.cc b/sql/item_windowfunc.cc index 27b007201c5..a216930cad6 100644 --- a/sql/item_windowfunc.cc +++ b/sql/item_windowfunc.cc @@ -13,7 +13,7 @@ Item_window_func::resolve_window_name(THD *thd) return false; } DBUG_ASSERT(window_name != NULL && window_spec == NULL); - char *ref_name= window_name->str; + const char *ref_name= window_name->str; /* !TODO: Add the code to resolve ref_name in outer queries */ /* @@ -26,7 +26,7 @@ Item_window_func::resolve_window_name(THD *thd) Window_spec *win_spec; while((win_spec= it++)) { - char *win_spec_name= win_spec->name(); + const char *win_spec_name= win_spec->name(); if (win_spec_name && my_strcasecmp(system_charset_info, ref_name, win_spec_name) == 0) { @@ -243,14 +243,14 @@ bool Item_sum_hybrid_simple::fix_fields(THD *thd, Item **ref) Item *item2= args[0]->real_item(); if (item2->type() == Item::FIELD_ITEM) - set_handler_by_field_type(((Item_field*) item2)->field->type()); + set_handler(item2->type_handler()); else if (args[0]->cmp_type() == TIME_RESULT) - set_handler_by_field_type(item2->field_type()); + set_handler(item2->type_handler()); else set_handler_by_result_type(item2->result_type(), max_length, collation.collation); - switch (Item_sum_hybrid_simple::result_type()) { + switch (result_type()) { case INT_RESULT: case DECIMAL_RESULT: case STRING_RESULT: @@ -290,7 +290,7 @@ bool Item_sum_hybrid_simple::add() void Item_sum_hybrid_simple::setup_hybrid(THD *thd, Item *item) { - if (!(value= Item_cache::get_cache(thd, item, item->cmp_type()))) + if (!(value= item->get_cache(thd))) return; value->setup(thd, item); value->store(item); @@ -352,7 +352,7 @@ Field *Item_sum_hybrid_simple::create_tmp_field(bool group, TABLE *table) void Item_sum_hybrid_simple::reset_field() { - switch(Item_sum_hybrid_simple::result_type()) { + switch(result_type()) { case STRING_RESULT: { char buff[MAX_FIELD_WIDTH]; diff --git a/sql/item_windowfunc.h b/sql/item_windowfunc.h index a1ef6854288..9fe95ed6cee 100644 --- a/sql/item_windowfunc.h +++ b/sql/item_windowfunc.h @@ -285,13 +285,13 @@ class Item_sum_hybrid_simple : public Item_sum, public: Item_sum_hybrid_simple(THD *thd, Item *arg): Item_sum(thd, arg), - Type_handler_hybrid_field_type(MYSQL_TYPE_LONGLONG), + Type_handler_hybrid_field_type(&type_handler_longlong), value(NULL) { collation.set(&my_charset_bin); } Item_sum_hybrid_simple(THD *thd, Item *arg1, Item *arg2): Item_sum(thd, arg1, arg2), - Type_handler_hybrid_field_type(MYSQL_TYPE_LONGLONG), + Type_handler_hybrid_field_type(&type_handler_longlong), value(NULL) { collation.set(&my_charset_bin); } @@ -303,14 +303,8 @@ class Item_sum_hybrid_simple : public Item_sum, my_decimal *val_decimal(my_decimal *); void reset_field(); String *val_str(String *); - /* TODO(cvicentiu) copied from Item_sum_hybrid, what does it do? */ - bool keep_field_type(void) const { return 1; } - enum Item_result result_type() const - { return Type_handler_hybrid_field_type::result_type(); } - enum Item_result cmp_type() const - { return Type_handler_hybrid_field_type::cmp_type(); } - enum enum_field_types field_type() const - { return Type_handler_hybrid_field_type::field_type(); } + const Type_handler *type_handler() const + { return Type_handler_hybrid_field_type::type_handler(); } void update_field(); Field *create_tmp_field(bool group, TABLE *table); void clear() @@ -513,8 +507,7 @@ class Item_sum_percent_rank: public Item_sum_window_with_row_count row_number= 0; } bool add(); - enum Item_result result_type () const { return REAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + const Type_handler *type_handler() const { return &type_handler_double; } void fix_length_and_dec() { @@ -599,8 +592,7 @@ class Item_sum_cume_dist: public Item_sum_window_with_row_count } void update_field() {} - enum Item_result result_type () const { return REAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + const Type_handler *type_handler() const { return &type_handler_double; } void fix_length_and_dec() { @@ -676,8 +668,7 @@ class Item_sum_ntile : public Item_sum_window_with_row_count void update_field() {} - enum Item_result result_type () const { return INT_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const { return &type_handler_longlong; } Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return get_item_copy<Item_sum_ntile>(thd, mem_root, this); } @@ -692,12 +683,12 @@ class Item_window_func : public Item_func_or_sum { /* Window function parameters as we've got them from the parser */ public: - LEX_STRING *window_name; + LEX_CSTRING *window_name; public: Window_spec *window_spec; public: - Item_window_func(THD *thd, Item_sum *win_func, LEX_STRING *win_name) + Item_window_func(THD *thd, Item_sum *win_func, LEX_CSTRING *win_name) : Item_func_or_sum(thd, (Item *) win_func), window_name(win_name), window_spec(NULL), force_return_blank(true), @@ -802,9 +793,9 @@ public: */ void setup_partition_border_check(THD *thd); - enum_field_types field_type() const - { - return ((Item_sum *) args[0])->field_type(); + const Type_handler *type_handler() const + { + return ((Item_sum *) args[0])->type_handler(); } enum Item::Type type() const { return Item::WINDOW_FUNC_ITEM; } diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 10dda8f20e8..ba33d103d0c 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -462,13 +462,13 @@ public: }; -class Item_func_xpath_position :public Item_int_func +class Item_func_xpath_position :public Item_long_func { String *pxml; String tmp_value; public: Item_func_xpath_position(THD *thd, Item *a, String *p): - Item_int_func(thd, a), pxml(p) {} + Item_long_func(thd, a), pxml(p) {} const char *func_name() const { return "xpath_position"; } void fix_length_and_dec() { max_length=10; } longlong val_int() @@ -483,13 +483,13 @@ public: }; -class Item_func_xpath_count :public Item_int_func +class Item_func_xpath_count :public Item_long_func { String *pxml; String tmp_value; public: Item_func_xpath_count(THD *thd, Item *a, String *p): - Item_int_func(thd, a), pxml(p) {} + Item_long_func(thd, a), pxml(p) {} const char *func_name() const { return "xpath_count"; } void fix_length_and_dec() { max_length=10; } longlong val_int() @@ -2595,7 +2595,7 @@ my_xpath_parse_QName(MY_XPATH *xpath) static int my_xpath_parse_VariableReference(MY_XPATH *xpath) { - LEX_STRING name; + LEX_CSTRING name; int user_var; const char *dollar_pos; THD *thd= xpath->thd; @@ -2610,7 +2610,7 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath) name.str= (char*) xpath->prevtok.beg; if (user_var) - xpath->item= new (thd->mem_root) Item_func_get_user_var(thd, name); + xpath->item= new (thd->mem_root) Item_func_get_user_var(thd, &name); else { sp_variable *spv; @@ -2618,10 +2618,10 @@ my_xpath_parse_VariableReference(MY_XPATH *xpath) LEX *lex; if ((lex= thd->lex) && (spc= lex->spcont) && - (spv= spc->find_variable(name, false))) + (spv= spc->find_variable(&name, false))) { Item_splocal *splocal= new (thd->mem_root) - Item_splocal(thd, name, spv->offset, spv->sql_type(), 0); + Item_splocal(thd, &name, spv->offset, spv->sql_type(), 0); #ifndef DBUG_OFF if (splocal) splocal->m_sp= lex->sphead; diff --git a/sql/key.cc b/sql/key.cc index cdf5c8c4a74..db44e4780c4 100644 --- a/sql/key.cc +++ b/sql/key.cc @@ -112,7 +112,7 @@ int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field, @param with_zerofill skipped bytes in the key buffer to be filled with 0 */ -void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, +void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info, uint key_length, bool with_zerofill) { uint length; diff --git a/sql/key.h b/sql/key.h index f2521e4a665..7b83d74c901 100644 --- a/sql/key.h +++ b/sql/key.h @@ -27,8 +27,8 @@ typedef struct st_key_part_info KEY_PART_INFO; int find_ref_key(KEY *key, uint key_count, uchar *record, Field *field, uint *key_length, uint *keypart); -void key_copy(uchar *to_key, uchar *from_record, KEY *key_info, uint key_length, - bool with_zerofill= FALSE); +void key_copy(uchar *to_key, const uchar *from_record, KEY *key_info, + uint key_length, bool with_zerofill= FALSE); void key_restore(uchar *to_record, const uchar *from_key, KEY *key_info, uint key_length); bool key_cmp_if_same(TABLE *form,const uchar *key,uint index,uint key_length); diff --git a/sql/keycaches.cc b/sql/keycaches.cc index 78e64ebac72..336f2611a7e 100644 --- a/sql/keycaches.cc +++ b/sql/keycaches.cc @@ -99,11 +99,11 @@ void NAMED_ILIST::delete_elements(void (*free_element)(const char *name, uchar*) /* Key cache functions */ -LEX_STRING default_key_cache_base= {C_STRING_WITH_LEN("default")}; +LEX_CSTRING default_key_cache_base= {C_STRING_WITH_LEN("default")}; KEY_CACHE zero_key_cache; ///< @@nonexistent_cache.param->value_ptr() points here -KEY_CACHE *get_key_cache(const LEX_STRING *cache_name) +KEY_CACHE *get_key_cache(const LEX_CSTRING *cache_name) { if (!cache_name || ! cache_name->length) cache_name= &default_key_cache_base; @@ -145,10 +145,10 @@ KEY_CACHE *create_key_cache(const char *name, uint length) KEY_CACHE *get_or_create_key_cache(const char *name, uint length) { - LEX_STRING key_cache_name; + LEX_CSTRING key_cache_name; KEY_CACHE *key_cache; - key_cache_name.str= (char *) name; + key_cache_name.str= name; key_cache_name.length= length; if (!(key_cache= get_key_cache(&key_cache_name))) key_cache= create_key_cache(name, length); diff --git a/sql/keycaches.h b/sql/keycaches.h index fff48d51c6f..1b58dccfe34 100644 --- a/sql/keycaches.h +++ b/sql/keycaches.h @@ -35,12 +35,12 @@ class NAMED_ILIST: public I_List<NAMED_ILINK> }; /* For key cache */ -extern LEX_STRING default_key_cache_base; +extern LEX_CSTRING default_key_cache_base; extern KEY_CACHE zero_key_cache; extern NAMED_ILIST key_caches; KEY_CACHE *create_key_cache(const char *name, uint length); -KEY_CACHE *get_key_cache(const LEX_STRING *cache_name); +KEY_CACHE *get_key_cache(const LEX_CSTRING *cache_name); KEY_CACHE *get_or_create_key_cache(const char *name, uint length); void free_key_cache(const char *name, KEY_CACHE *key_cache); bool process_key_caches(process_key_cache_t func, void *param); diff --git a/sql/lex.h b/sql/lex.h index baeae088234..67c3bc8620d 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -114,6 +114,7 @@ static SYMBOL symbols[] = { { "CIPHER", SYM(CIPHER_SYM)}, { "CLASS_ORIGIN", SYM(CLASS_ORIGIN_SYM)}, { "CLIENT", SYM(CLIENT_SYM)}, + { "CLOB", SYM(CLOB)}, { "CLOSE", SYM(CLOSE_SYM)}, { "COALESCE", SYM(COALESCE)}, { "CODE", SYM(CODE_SYM)}, @@ -159,6 +160,7 @@ static SYMBOL symbols[] = { { "CURRENT_USER", SYM(CURRENT_USER)}, { "CURSOR", SYM(CURSOR_SYM)}, { "CURSOR_NAME", SYM(CURSOR_NAME_SYM)}, + { "CYCLE", SYM(CYCLE_SYM)}, { "DATA", SYM(DATA_SYM)}, { "DATABASE", SYM(DATABASE)}, { "DATABASES", SYM(DATABASES)}, @@ -202,6 +204,7 @@ static SYMBOL symbols[] = { { "EACH", SYM(EACH_SYM)}, { "ELSE", SYM(ELSE)}, { "ELSEIF", SYM(ELSEIF_SYM)}, + { "ELSIF", SYM(ELSIF_SYM)}, { "ENABLE", SYM(ENABLE_SYM)}, { "ENCLOSED", SYM(ENCLOSED)}, { "END", SYM(END)}, @@ -217,9 +220,11 @@ static SYMBOL symbols[] = { { "EVENTS", SYM(EVENTS_SYM)}, { "EVERY", SYM(EVERY_SYM)}, { "EXAMINED", SYM(EXAMINED_SYM)}, + { "EXCEPT", SYM(EXCEPT_SYM)}, { "EXCHANGE", SYM(EXCHANGE_SYM)}, { "EXCLUDE", SYM(EXCLUDE_SYM)}, { "EXECUTE", SYM(EXECUTE_SYM)}, + { "EXCEPTION", SYM(EXCEPTION_SYM)}, { "EXISTS", SYM(EXISTS)}, { "EXIT", SYM(EXIT_SYM)}, { "EXPANSION", SYM(EXPANSION_SYM)}, @@ -257,6 +262,7 @@ static SYMBOL symbols[] = { { "GET_FORMAT", SYM(GET_FORMAT)}, { "GET", SYM(GET_SYM)}, { "GLOBAL", SYM(GLOBAL_SYM)}, + { "GOTO", SYM(GOTO_SYM)}, { "GRANT", SYM(GRANT)}, { "GRANTS", SYM(GRANTS)}, { "GROUP", SYM(GROUP_SYM)}, @@ -280,7 +286,9 @@ static SYMBOL symbols[] = { { "IGNORE_SERVER_IDS", SYM(IGNORE_SERVER_IDS_SYM)}, { "IMMEDIATE", SYM(IMMEDIATE_SYM)}, { "IMPORT", SYM(IMPORT)}, + { "INTERSECT", SYM(INTERSECT_SYM)}, { "IN", SYM(IN_SYM)}, + { "INCREMENT", SYM(INCREMENT_SYM)}, { "INDEX", SYM(INDEX_SYM)}, { "INDEXES", SYM(INDEXES)}, { "INFILE", SYM(INFILE)}, @@ -305,6 +313,7 @@ static SYMBOL symbols[] = { { "IPC", SYM(IPC_SYM)}, { "IS", SYM(IS)}, { "ISOLATION", SYM(ISOLATION)}, + { "ISOPEN", SYM(ISOPEN_SYM)}, { "ISSUER", SYM(ISSUER_SYM)}, { "ITERATE", SYM(ITERATE_SYM)}, { "INVOKER", SYM(INVOKER_SYM)}, @@ -317,6 +326,7 @@ static SYMBOL symbols[] = { { "LANGUAGE", SYM(LANGUAGE_SYM)}, { "LAST", SYM(LAST_SYM)}, { "LAST_VALUE", SYM(LAST_VALUE)}, + { "LASTVAL", SYM(LASTVAL_SYM)}, { "LEADING", SYM(LEADING)}, { "LEAVE", SYM(LEAVE_SYM)}, { "LEAVES", SYM(LEAVES)}, @@ -372,7 +382,7 @@ static SYMBOL symbols[] = { { "MAX_STATEMENT_TIME", SYM(MAX_STATEMENT_TIME_SYM)}, { "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR)}, { "MAX_USER_CONNECTIONS", SYM(MAX_USER_CONNECTIONS_SYM)}, - { "MAXVALUE", SYM(MAX_VALUE_SYM)}, + { "MAXVALUE", SYM(MAXVALUE_SYM)}, { "MEDIUM", SYM(MEDIUM_SYM)}, { "MEDIUMBLOB", SYM(MEDIUMBLOB)}, { "MEDIUMINT", SYM(MEDIUMINT)}, @@ -386,6 +396,7 @@ static SYMBOL symbols[] = { { "MINUTE", SYM(MINUTE_SYM)}, { "MINUTE_MICROSECOND", SYM(MINUTE_MICROSECOND_SYM)}, { "MINUTE_SECOND", SYM(MINUTE_SECOND_SYM)}, + { "MINVALUE", SYM(MINVALUE_SYM)}, { "MIN_ROWS", SYM(MIN_ROWS)}, { "MOD", SYM(MOD_SYM)}, { "MODE", SYM(MODE_SYM)}, @@ -405,16 +416,23 @@ static SYMBOL symbols[] = { { "NCHAR", SYM(NCHAR_SYM)}, { "NEW", SYM(NEW_SYM)}, { "NEXT", SYM(NEXT_SYM)}, + { "NEXTVAL", SYM(NEXTVAL_SYM)}, { "NO", SYM(NO_SYM)}, + { "NOMAXVALUE", SYM(NOMAXVALUE_SYM)}, + { "NOMINVALUE", SYM(NOMINVALUE_SYM)}, + { "NOCACHE", SYM(NOCACHE_SYM)}, + { "NOCYCLE", SYM(NOCYCLE_SYM)}, { "NO_WAIT", SYM(NO_WAIT_SYM)}, { "NODEGROUP", SYM(NODEGROUP_SYM)}, { "NONE", SYM(NONE_SYM)}, { "NOT", SYM(NOT_SYM)}, + { "NOTFOUND", SYM(NOTFOUND_SYM)}, { "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)}, { "NULL", SYM(NULL_SYM)}, { "NUMBER", SYM(NUMBER_SYM)}, { "NUMERIC", SYM(NUMERIC_SYM)}, { "NVARCHAR", SYM(NVARCHAR_SYM)}, + { "OF", SYM(OF_SYM)}, { "OFFSET", SYM(OFFSET_SYM)}, { "OLD_PASSWORD", SYM(OLD_PASSWORD_SYM)}, { "ON", SYM(ON)}, @@ -457,6 +475,7 @@ static SYMBOL symbols[] = { { "PREPARE", SYM(PREPARE_SYM)}, { "PRESERVE", SYM(PRESERVE_SYM)}, { "PREV", SYM(PREV_SYM)}, + { "PREVIOUS", SYM(PREVIOUS_SYM)}, { "PRIMARY", SYM(PRIMARY_SYM)}, { "PRIVILEGES", SYM(PRIVILEGES)}, { "PROCEDURE", SYM(PROCEDURE_SYM)}, @@ -469,7 +488,9 @@ static SYMBOL symbols[] = { { "QUARTER", SYM(QUARTER_SYM)}, { "QUERY", SYM(QUERY_SYM)}, { "QUICK", SYM(QUICK)}, + { "RAISE", SYM(RAISE_SYM)}, { "RANGE", SYM(RANGE_SYM)}, + { "RAW", SYM(RAW)}, { "READ", SYM(READ_SYM)}, { "READ_ONLY", SYM(READ_ONLY_SYM)}, { "READ_WRITE", SYM(READ_WRITE_SYM)}, @@ -501,6 +522,7 @@ static SYMBOL symbols[] = { { "REQUIRE", SYM(REQUIRE_SYM)}, { "RESET", SYM(RESET_SYM)}, { "RESIGNAL", SYM(RESIGNAL_SYM)}, + { "RESTART", SYM(RESTART_SYM)}, { "RESTORE", SYM(RESTORE_SYM)}, { "RESTRICT", SYM(RESTRICT)}, { "RESUME", SYM(RESUME_SYM)}, @@ -508,6 +530,7 @@ static SYMBOL symbols[] = { { "RETURN", SYM(RETURN_SYM)}, { "RETURNING", SYM(RETURNING_SYM)}, { "RETURNS", SYM(RETURNS_SYM)}, + { "REUSE", SYM(REUSE_SYM)}, { "REVERSE", SYM(REVERSE_SYM)}, { "REVOKE", SYM(REVOKE)}, { "RIGHT", SYM(RIGHT)}, @@ -517,7 +540,9 @@ static SYMBOL symbols[] = { { "ROLLUP", SYM(ROLLUP_SYM)}, { "ROUTINE", SYM(ROUTINE_SYM)}, { "ROW", SYM(ROW_SYM)}, + { "ROWCOUNT", SYM(ROWCOUNT_SYM)}, /* Oracle-N */ { "ROWS", SYM(ROWS_SYM)}, + { "ROWTYPE", SYM(ROWTYPE_SYM)}, { "ROW_COUNT", SYM(ROW_COUNT_SYM)}, { "ROW_FORMAT", SYM(ROW_FORMAT_SYM)}, { "RTREE", SYM(RTREE_SYM)}, @@ -532,11 +557,13 @@ static SYMBOL symbols[] = { { "SELECT", SYM(SELECT_SYM)}, { "SENSITIVE", SYM(SENSITIVE_SYM)}, { "SEPARATOR", SYM(SEPARATOR_SYM)}, + { "SEQUENCE", SYM(SEQUENCE_SYM)}, { "SERIAL", SYM(SERIAL_SYM)}, { "SERIALIZABLE", SYM(SERIALIZABLE_SYM)}, { "SESSION", SYM(SESSION_SYM)}, { "SERVER", SYM(SERVER_SYM)}, { "SET", SYM(SET)}, + { "SETVAL", SYM(SETVAL_SYM)}, { "SHARE", SYM(SHARE_SYM)}, { "SHOW", SYM(SHOW)}, { "SHUTDOWN", SYM(SHUTDOWN)}, @@ -658,6 +685,7 @@ static SYMBOL symbols[] = { { "VARBINARY", SYM(VARBINARY)}, { "VARCHAR", SYM(VARCHAR)}, { "VARCHARACTER", SYM(VARCHAR)}, + { "VARCHAR2", SYM(VARCHAR2)}, { "VARIABLES", SYM(VARIABLES)}, { "VARYING", SYM(VARYING)}, { "VIA", SYM(VIA_SYM)}, @@ -698,6 +726,8 @@ static SYMBOL sql_functions[] = { { "CURTIME", SYM(CURTIME)}, { "DATE_ADD", SYM(DATE_ADD_INTERVAL)}, { "DATE_SUB", SYM(DATE_SUB_INTERVAL)}, + { "DATE_FORMAT", SYM(DATE_FORMAT_SYM)}, + { "DECODE", SYM(DECODE_SYM)}, { "DENSE_RANK", SYM(DENSE_RANK_SYM)}, { "EXTRACT", SYM(EXTRACT_SYM)}, { "FIRST_VALUE", SYM(FIRST_VALUE_SYM)}, diff --git a/sql/lock.cc b/sql/lock.cc index 06cb00fe32d..a34613fb7fe 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -261,7 +261,7 @@ static void track_table_access(THD *thd, TABLE **tables, size_t count) if (t) tst->add_trx_state(thd, t->reginfo.lock_type, - t->file->has_transactions()); + t->file->has_transaction_manager()); } } } @@ -442,10 +442,10 @@ void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock) This will work even if get_lock_data fails (next unlock will free all) */ -void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count) +void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag) { MYSQL_LOCK *sql_lock= - get_lock_data(thd, table, count, GET_LOCK_UNLOCK | GET_LOCK_ON_THD); + get_lock_data(thd, table, count, GET_LOCK_UNLOCK | GET_LOCK_ON_THD | flag); if (sql_lock) mysql_unlock_tables(thd, sql_lock, 0); } @@ -542,7 +542,7 @@ void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table) DBUG_ASSERT(table->lock_position == i); /* Unlock the table. */ - mysql_unlock_some_tables(thd, &table, /* table count */ 1); + mysql_unlock_some_tables(thd, &table, /* table count */ 1, 0); /* Decrement table_count in advance, making below expressions easier */ old_tables= --locked->table_count; @@ -736,6 +736,7 @@ static int unlock_external(THD *thd, TABLE **table,uint count) @param flags One of: - GET_LOCK_UNLOCK : If we should send TL_IGNORE to store lock - GET_LOCK_STORE_LOCKS : Store lock info in TABLE + - GET_LOCK_SKIP_SEQUENCES : Ignore sequences (for temporary unlock) */ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) @@ -753,7 +754,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) TABLE *t= table_ptr[i]; if (t->s->tmp_table != NON_TRANSACTIONAL_TMP_TABLE && - t->s->tmp_table != INTERNAL_TMP_TABLE) + t->s->tmp_table != INTERNAL_TMP_TABLE && + (!(flags & GET_LOCK_SKIP_SEQUENCES) || t->s->sequence == 0)) { lock_count+= t->file->lock_count(); table_count++; @@ -784,7 +786,8 @@ MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags) THR_LOCK_DATA **locks_start; table= table_ptr[i]; if (table->s->tmp_table == NON_TRANSACTIONAL_TMP_TABLE || - table->s->tmp_table == INTERNAL_TMP_TABLE) + table->s->tmp_table == INTERNAL_TMP_TABLE || + ((flags & GET_LOCK_SKIP_SEQUENCES) && table->s->sequence)) continue; lock_type= table->reginfo.lock_type; DBUG_ASSERT(lock_type != TL_WRITE_DEFAULT && lock_type != TL_READ_DEFAULT); diff --git a/sql/lock.h b/sql/lock.h index 341d7a20f9f..35cb3043d57 100644 --- a/sql/lock.h +++ b/sql/lock.h @@ -31,7 +31,7 @@ bool mysql_lock_tables(THD *thd, MYSQL_LOCK *sql_lock, uint flags); void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock, bool free_lock); void mysql_unlock_tables(THD *thd, MYSQL_LOCK *sql_lock); void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock); -void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count); +void mysql_unlock_some_tables(THD *thd, TABLE **table,uint count, uint flag); void mysql_lock_remove(THD *thd, MYSQL_LOCK *locked,TABLE *table); void mysql_lock_abort(THD *thd, TABLE *table, bool upgrade_lock); bool mysql_lock_abort_for_thread(THD *thd, TABLE *table); @@ -47,6 +47,7 @@ bool lock_object_name(THD *thd, MDL_key::enum_mdl_namespace mdl_type, #define GET_LOCK_STORE_LOCKS 1 #define GET_LOCK_ACTION_MASK 1 #define GET_LOCK_ON_THD (1 << 1) +#define GET_LOCK_SKIP_SEQUENCES (1 << 2) MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count, uint flags); void reset_lock_data(MYSQL_LOCK *sql_lock, bool unlock); diff --git a/sql/log.cc b/sql/log.cc index b0115c3e480..d7a3f19c2f0 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -61,6 +61,8 @@ #define MAX_LOG_BUFFER_SIZE 1024 #define MAX_TIME_SIZE 32 #define MY_OFF_T_UNDEF (~(my_off_t)0UL) +/* Truncate cache log files bigger than this */ +#define CACHE_FILE_TRUNC_SIZE 65536 #define FLAGSTR(V,F) ((V)&(F)?#F" ":"") @@ -250,7 +252,8 @@ void make_default_log_name(char **out, const char* log_ext, bool once) class binlog_cache_data { public: - binlog_cache_data(): m_pending(0), before_stmt_pos(MY_OFF_T_UNDEF), + binlog_cache_data(): m_pending(0), status(0), + before_stmt_pos(MY_OFF_T_UNDEF), incident(FALSE), changes_to_non_trans_temp_table_flag(FALSE), saved_max_binlog_cache_size(0), ptr_binlog_cache_use(0), ptr_binlog_cache_disk_use(0) @@ -262,9 +265,22 @@ public: close_cached_file(&cache_log); } + /* + Return 1 if there is no relevant entries in the cache + + This is: + - Cache is empty + - There are row or critical (DDL?) events in the cache + + The status test is needed to avoid writing entries with only + a table map entry, which would crash in do_apply_event() on the slave + as it assumes that there is always a row entry after a table map. + */ bool empty() const { - return pending() == NULL && my_b_tell(&cache_log) == 0; + return (pending() == NULL && + (my_b_write_tell(&cache_log) == 0 || + ((status & (LOGGED_ROW_EVENT | LOGGED_CRITICAL)) == 0))); } Rows_log_event *pending() const @@ -299,23 +315,19 @@ public: void reset() { - compute_statistics(); - truncate(0); - if(cache_log.file != -1) + bool cache_was_empty= empty(); + bool truncate_file= (cache_log.file != -1 && + my_b_write_tell(&cache_log) > CACHE_FILE_TRUNC_SIZE); + truncate(0,1); // Forget what's in cache + if (!cache_was_empty) + compute_statistics(); + if (truncate_file) my_chsize(cache_log.file, 0, 0, MYF(MY_WME)); changes_to_non_trans_temp_table_flag= FALSE; + status= 0; incident= FALSE; before_stmt_pos= MY_OFF_T_UNDEF; - /* - The truncate function calls reinit_io_cache that calls - my_b_flush_io_cache which may increase disk_writes. This breaks - the disk_writes use by the binary log which aims to compute the - ratio between in-memory cache usage and disk cache usage. To - avoid this undesirable behavior, we reset the variable after - truncating the cache. - */ - cache_log.disk_writes= 0; DBUG_ASSERT(empty()); } @@ -376,6 +388,11 @@ public: cache_log.end_of_file= saved_max_binlog_cache_size; } + void add_status(enum_logged_status status_arg) + { + status|= status_arg; + } + /* Cache to store data before copying it to the binary log. */ @@ -389,6 +406,13 @@ private: Rows_log_event *m_pending; /* + Bit flags for what has been writting to cache. Used to + discard logs without any data changes. + see enum_logged_status; + */ + uint32 status; + + /* Binlog position before the start of the current statement. */ my_off_t before_stmt_pos; @@ -410,11 +434,16 @@ private: */ void compute_statistics() { - if (!empty()) + statistic_increment(*ptr_binlog_cache_use, &LOCK_status); + if (cache_log.disk_writes != 0) { - statistic_increment(*ptr_binlog_cache_use, &LOCK_status); - if (cache_log.disk_writes != 0) - statistic_increment(*ptr_binlog_cache_disk_use, &LOCK_status); +#ifdef REAL_STATISTICS + statistic_add(*ptr_binlog_cache_disk_use, + cache_log.disk_writes, &LOCK_status); +#else + statistic_increment(*ptr_binlog_cache_disk_use, &LOCK_status); +#endif + cache_log.disk_writes= 0; } } @@ -443,7 +472,7 @@ private: It truncates the cache to a certain position. This includes deleting the pending event. */ - void truncate(my_off_t pos) + void truncate(my_off_t pos, bool reset_cache=0) { DBUG_PRINT("info", ("truncating to position %lu", (ulong) pos)); if (pending()) @@ -451,7 +480,7 @@ private: delete pending(); set_pending(0); } - reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, 0); + reinit_io_cache(&cache_log, WRITE_CACHE, pos, 0, reset_cache); cache_log.end_of_file= saved_max_binlog_cache_size; } @@ -459,6 +488,13 @@ private: binlog_cache_data(const binlog_cache_data& info); }; + +void Log_event_writer::add_status(enum_logged_status status) +{ + if (likely(cache_data)) + cache_data->add_status(status); +} + class binlog_cache_mngr { public: binlog_cache_mngr(my_off_t param_max_binlog_stmt_cache_size, @@ -976,7 +1012,7 @@ int Log_to_csv_event_handler:: { TABLE_LIST table_list; TABLE *table; - LEX_STRING *UNINIT_VAR(log_name); + LEX_CSTRING *UNINIT_VAR(log_name); int result; Open_tables_backup open_tables_backup; @@ -5190,12 +5226,14 @@ end: DBUG_RETURN(error); } -bool MYSQL_BIN_LOG::write_event(Log_event *ev, IO_CACHE *file) +bool MYSQL_BIN_LOG::write_event(Log_event *ev, binlog_cache_data *cache_data, + IO_CACHE *file) { - Log_event_writer writer(file, &crypto); + Log_event_writer writer(file, 0, &crypto); if (crypto.scheme && file == &log_file) writer.ctx= alloca(crypto.ctx_size); - + if (cache_data) + cache_data->add_status(ev->logged_status()); return writer.write(ev); } @@ -5632,12 +5670,10 @@ int THD::binlog_write_table_map(TABLE *table, bool is_transactional, binlog_cache_mngr *const cache_mngr= (binlog_cache_mngr*) thd_get_ha_data(this, binlog_hton); - - IO_CACHE *file= - cache_mngr->get_binlog_cache_log(use_trans_cache(this, is_transactional)); - Log_event_writer writer(file); - binlog_cache_data *cache_data= - cache_mngr->get_binlog_cache_data(use_trans_cache(this, is_transactional)); + binlog_cache_data *cache_data= (cache_mngr-> + get_binlog_cache_data(is_transactional)); + IO_CACHE *file= &cache_data->cache_log; + Log_event_writer writer(file, cache_data); if (with_annotate && *with_annotate) { @@ -5778,7 +5814,7 @@ MYSQL_BIN_LOG::flush_and_set_pending_rows_event(THD *thd, if (Rows_log_event* pending= cache_data->pending()) { - Log_event_writer writer(&cache_data->cache_log); + Log_event_writer writer(&cache_data->cache_log, cache_data); /* Write pending event to the cache. @@ -6190,8 +6226,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) goto err; is_trans_cache= use_trans_cache(thd, using_trans); - file= cache_mngr->get_binlog_cache_log(is_trans_cache); cache_data= cache_mngr->get_binlog_cache_data(is_trans_cache); + file= &cache_data->cache_log; if (thd->lex->stmt_accessed_non_trans_temp_table()) cache_data->set_changes_to_non_trans_temp_table(); @@ -6215,21 +6251,19 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) Annotate_rows_log_event anno(thd, using_trans, direct); /* Annotate event should be written not more than once */ *with_annotate= 0; - if (write_event(&anno, file)) + if (write_event(&anno, cache_data, file)) goto err; } - if (thd) { if (!thd->is_current_stmt_binlog_format_row()) { - if (thd->stmt_depends_on_first_successful_insert_id_in_prev_stmt) { Intvar_log_event e(thd,(uchar) LAST_INSERT_ID_EVENT, thd->first_successful_insert_id_in_prev_stmt_for_binlog, using_trans, direct); - if (write_event(&e, file)) + if (write_event(&e, cache_data, file)) goto err; } if (thd->auto_inc_intervals_in_cur_stmt_for_binlog.nb_elements() > 0) @@ -6240,14 +6274,14 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) Intvar_log_event e(thd, (uchar) INSERT_ID_EVENT, thd->auto_inc_intervals_in_cur_stmt_for_binlog. minimum(), using_trans, direct); - if (write_event(&e, file)) + if (write_event(&e, cache_data, file)) goto err; } if (thd->rand_used) { Rand_log_event e(thd,thd->rand_saved_seed1,thd->rand_saved_seed2, using_trans, direct); - if (write_event(&e, file)) + if (write_event(&e, cache_data, file)) goto err; } if (thd->user_var_events.elements) @@ -6271,7 +6305,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) flags, using_trans, direct); - if (write_event(&e, file)) + if (write_event(&e, cache_data, file)) goto err; } } @@ -6281,7 +6315,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info, my_bool *with_annotate) /* Write the event. */ - if (write_event(event_info, file) || + if (write_event(event_info, cache_data, file) || DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0)) goto err; @@ -6666,7 +6700,8 @@ public: CacheWriter(THD *thd_arg, IO_CACHE *file_arg, bool do_checksum, Binlog_crypt_data *cr) - : Log_event_writer(file_arg, cr), remains(0), thd(thd_arg), first(true) + : Log_event_writer(file_arg, 0, cr), remains(0), thd(thd_arg), + first(true) { checksum_len= do_checksum ? BINLOG_CHECKSUM_LEN : 0; } ~CacheWriter() @@ -6674,6 +6709,7 @@ public: int write(uchar* pos, size_t len) { + DBUG_ENTER("CacheWriter::write"); if (first) write_header(pos, len); else @@ -6682,7 +6718,7 @@ public: remains -= len; if ((first= !remains)) write_footer(); - return 0; + DBUG_RETURN(0); } private: THD *thd; diff --git a/sql/log.h b/sql/log.h index 4471f0be26d..eaa63d4072d 100644 --- a/sql/log.h +++ b/sql/log.h @@ -415,8 +415,10 @@ private: ( ((ulong)(c)>>1) == BINLOG_COOKIE_DUMMY_ID ) class binlog_cache_mngr; +class binlog_cache_data; struct rpl_gtid; struct wait_for_commit; + class MYSQL_BIN_LOG: public TC_LOG, private MYSQL_LOG { private: @@ -743,8 +745,8 @@ public: void stop_union_events(THD *thd); bool is_query_in_union(THD *thd, query_id_t query_id_param); - bool write_event(Log_event *ev, IO_CACHE *file); - bool write_event(Log_event *ev) { return write_event(ev, &log_file); } + bool write_event(Log_event *ev, binlog_cache_data *data, IO_CACHE *file); + bool write_event(Log_event *ev) { return write_event(ev, 0, &log_file); } bool write_event_buffer(uchar* buf,uint len); bool append(Log_event* ev); diff --git a/sql/log_event.cc b/sql/log_event.cc index 9299932ac4b..177e6c2c775 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -581,7 +581,7 @@ pretty_print_str(String *packet, const char *str, int len) */ static char *load_data_tmp_prefix(char *name, - LEX_STRING *connection_name) + LEX_CSTRING *connection_name) { name= strmov(name, PREFIX_SQL_LOAD); if (connection_name->length) @@ -617,7 +617,7 @@ static char *load_data_tmp_prefix(char *name, static char *slave_load_file_stem(char *buf, uint file_id, int event_server_id, const char *ext, - LEX_STRING *connection_name) + LEX_CSTRING *connection_name) { char *res; res= buf+ unpack_dirname(buf, slave_load_tmpdir); @@ -638,7 +638,7 @@ static char *slave_load_file_stem(char *buf, uint file_id, Delete all temporary files used for SQL_LOAD. */ -static void cleanup_load_tmpdir(LEX_STRING *connection_name) +static void cleanup_load_tmpdir(LEX_CSTRING *connection_name) { MY_DIR *dirp; FILEINFO *file; @@ -3817,8 +3817,8 @@ bool Query_log_event::write() if (thd && thd->need_binlog_invoker()) { - LEX_STRING user; - LEX_STRING host; + LEX_CSTRING user; + LEX_CSTRING host; memset(&user, 0, sizeof(user)); memset(&host, 0, sizeof(host)); @@ -3841,7 +3841,7 @@ bool Query_log_event::write() else { user.str= ctx->priv_role; - host= empty_lex_str; + host= empty_clex_str; } user.length= strlen(user.str); } @@ -4071,10 +4071,12 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg, switch (lex->sql_command) { case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: use_cache= (lex->tmp_table() && thd->in_multi_stmt_transaction_mode()); break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: /* If we are using CREATE ... SELECT or if we are a slave executing BEGIN...COMMIT (generated by CREATE...SELECT) we @@ -4493,22 +4495,20 @@ Query_log_event::Query_log_event(const char* buf, uint event_len, if (user.length) { - copy_str_and_move((const char **)&(user.str), &start, user.length); + copy_str_and_move(&user.str, &start, user.length); } else { - user.str= (char *) start++; - user.str[0]= '\0'; + user.str= (char*) start; + *(start++)= 0; } if (host.length) - { - copy_str_and_move((const char **)&(host.str), &start, host.length); - } + copy_str_and_move(&host.str, &start, host.length); else { - host.str= (char *) start++; - host.str[0]= '\0'; + host.str= (char*) start; + *(start++)= 0; } /** @@ -5314,7 +5314,8 @@ compare_errors: has already been dropped. To ignore such irrelevant "table does not exist errors", we silently clear the error if TEMPORARY was used. */ - if (thd->lex->sql_command == SQLCOM_DROP_TABLE && + if ((thd->lex->sql_command == SQLCOM_DROP_TABLE || + thd->lex->sql_command == SQLCOM_DROP_SEQUENCE) && thd->lex->tmp_table() && thd->is_error() && thd->get_stmt_da()->sql_errno() == ER_BAD_TABLE_ERROR && !expected_error) @@ -6552,9 +6553,9 @@ Load_log_event::Load_log_event(THD *thd_arg, sql_exchange *ex, while ((item = li++)) { num_fields++; - uchar len = (uchar) strlen(item->name); + uchar len= (uchar) item->name.length; field_block_len += len + 1; - fields_buf.append(item->name, len + 1); + fields_buf.append(item->name.str, len + 1); field_lens_buf.append((char*)&len, 1); } @@ -6780,9 +6781,10 @@ void Load_log_event::set_fields(const char* affected_db, const char* field = fields; for (i= 0; i < num_fields; i++) { + LEX_CSTRING field_name= {field, field_lens[i] }; field_list.push_back(new (thd->mem_root) Item_field(thd, context, affected_db, table_name, - field), + &field_name), thd->mem_root); field+= field_lens[i] + 1; } @@ -8904,7 +8906,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi) if (!(charset= get_charset(charset_number, MYF(MY_WME)))) DBUG_RETURN(1); - LEX_STRING user_var_name; + LEX_CSTRING user_var_name; user_var_name.str= name; user_var_name.length= name_len; double real_val; @@ -8947,7 +8949,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi) } } - Item_func_set_user_var *e= new (thd->mem_root) Item_func_set_user_var(thd, user_var_name, it); + Item_func_set_user_var *e= new (thd->mem_root) Item_func_set_user_var(thd, &user_var_name, it); /* Item_func_set_user_var can't substitute something else on its place => 0 can be passed as last argument (reference on item) @@ -8964,7 +8966,7 @@ int User_var_log_event::do_apply_event(rpl_group_info *rgi) a single record and with a single column. Thus, like a column value, it could always have IMPLICIT derivation. */ - e->update_hash(val, val_len, type, charset, + e->update_hash((void*) val, val_len, type, charset, (flags & User_var_log_event::UNSIGNED_F)); if (!is_deferred()) free_root(thd->mem_root, 0); @@ -9293,7 +9295,7 @@ int Create_file_log_event::do_apply_event(rpl_group_info *rgi) char *ext; int fd = -1; IO_CACHE file; - Log_event_writer lew(&file); + Log_event_writer lew(&file, 0); int error = 1; Relay_log_info const *rli= rgi->rli; @@ -11324,7 +11326,9 @@ Annotate_rows_log_event::Annotate_rows_log_event(THD *thd, bool direct) : Log_event(thd, 0, using_trans), m_save_thd_query_txt(0), - m_save_thd_query_len(0), m_saved_thd_query(false) + m_save_thd_query_len(0), + m_saved_thd_query(false), + m_used_query_txt(0) { m_query_txt= thd->query(); m_query_len= thd->query_length(); @@ -11338,7 +11342,9 @@ Annotate_rows_log_event::Annotate_rows_log_event(const char *buf, const Format_description_log_event *desc) : Log_event(buf, desc), m_save_thd_query_txt(0), - m_save_thd_query_len(0), m_saved_thd_query(false) + m_save_thd_query_len(0), + m_saved_thd_query(false), + m_used_query_txt(0) { m_query_len= event_len - desc->common_header_len; m_query_txt= (char*) buf + desc->common_header_len; @@ -11346,10 +11352,14 @@ Annotate_rows_log_event::Annotate_rows_log_event(const char *buf, Annotate_rows_log_event::~Annotate_rows_log_event() { + DBUG_ENTER("Annotate_rows_log_event::~Annotate_rows_log_event"); #ifndef MYSQL_CLIENT if (m_saved_thd_query) thd->set_query(m_save_thd_query_txt, m_save_thd_query_len); + else if (m_used_query_txt) + thd->reset_query(); #endif + DBUG_VOID_RETURN; } int Annotate_rows_log_event::get_data_size() @@ -11435,6 +11445,7 @@ int Annotate_rows_log_event::do_apply_event(rpl_group_info *rgi) m_save_thd_query_txt= thd->query(); m_save_thd_query_len= thd->query_length(); m_saved_thd_query= true; + m_used_query_txt= 1; thd->set_query(m_query_txt, m_query_len); return 0; } @@ -11986,7 +11997,7 @@ int Table_map_log_event::do_apply_event(rpl_group_info *rgi) table_list->table_id= DBUG_EVALUATE_IF("inject_tblmap_same_id_maps_diff_table", 0, m_table_id); table_list->updating= 1; - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; DBUG_PRINT("debug", ("table: %s is mapped to %u", table_list->table_name, table_list->table_id)); @@ -12511,7 +12522,9 @@ Rows_log_event::write_row(rpl_group_info *rgi, TODO: Add safety measures against infinite looping. */ - while ((error= table->file->ha_write_row(table->record[0]))) + if (table->s->sequence) + error= update_sequence(); + else while ((error= table->file->ha_write_row(table->record[0]))) { if (error == HA_ERR_LOCK_DEADLOCK || error == HA_ERR_LOCK_WAIT_TIMEOUT || @@ -12678,6 +12691,33 @@ Rows_log_event::write_row(rpl_group_info *rgi, DBUG_RETURN(error); } + +int Rows_log_event::update_sequence() +{ + TABLE *table= m_table; // pointer to event's table + + if (!bitmap_is_set(table->rpl_write_set, MIN_VALUE_FIELD_NO)) + { + /* This event come from a setval function executed on the master. + Update the sequence next_number and round, like we do with setval() + */ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, + table->read_set); + longlong nextval= table->field[NEXT_FIELD_NO]->val_int(); + longlong round= table->field[ROUND_FIELD_NO]->val_int(); + dbug_tmp_restore_column_map(table->read_set, old_map); + + return table->s->sequence->set_value(table, nextval, round, 0); + } + + /* + Update all fields in table and update the active sequence, like with + ALTER SEQUENCE + */ + return table->file->ha_write_row(table->record[0]); +} + + #endif int diff --git a/sql/log_event.h b/sql/log_event.h index 34000a5f9de..428616fcb0d 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -716,6 +716,21 @@ enum Log_event_type ENUM_END_EVENT /* end marker */ }; + +/* + Bit flags for what has been writting to cache. Used to + discard logs with table map events but not row events and + nothing else important. This is stored by cache. +*/ + +enum enum_logged_status +{ + LOGGED_TABLE_MAP= 1, + LOGGED_ROW_EVENT= 2, + LOGGED_NO_DATA= 4, + LOGGED_CRITICAL= 8 +}; + static inline bool LOG_EVENT_IS_QUERY(enum Log_event_type type) { return type == QUERY_EVENT || type == QUERY_COMPRESSED_EVENT; @@ -785,6 +800,7 @@ class THD; class Format_description_log_event; class Relay_log_info; +class binlog_cache_data; #ifdef MYSQL_CLIENT enum enum_base64_output_mode { @@ -896,6 +912,7 @@ typedef struct st_print_event_info This class encapsulates writing of Log_event objects to IO_CACHE. Automatically calculates the checksum and encrypts the data, if necessary. */ + class Log_event_writer { public: @@ -907,13 +924,16 @@ public: int write_data(const uchar *pos, size_t len); int write_footer(); my_off_t pos() { return my_b_safe_tell(file); } + void add_status(enum_logged_status status); -Log_event_writer(IO_CACHE *file_arg, Binlog_crypt_data *cr= 0) + Log_event_writer(IO_CACHE *file_arg, binlog_cache_data *cache_data_arg, + Binlog_crypt_data *cr= 0) : bytes_written(0), ctx(0), - file(file_arg), crypto(cr) { } + file(file_arg), cache_data(cache_data_arg), crypto(cr) { } private: IO_CACHE *file; + binlog_cache_data *cache_data; /** Placeholder for event checksum while writing to binlog. */ @@ -1236,7 +1256,7 @@ public: bool is_more); void print_base64(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info, bool is_more); -#endif +#endif /* MYSQL_SERVER */ /* The following code used for Flashback */ #ifdef MYSQL_CLIENT @@ -1382,6 +1402,7 @@ public: } #endif virtual Log_event_type get_type_code() = 0; + virtual enum_logged_status logged_status() { return LOGGED_CRITICAL; } virtual bool is_valid() const = 0; virtual my_off_t get_header_len(my_off_t len) { return len; } void set_artificial_event() { flags |= LOG_EVENT_ARTIFICIAL_F; } @@ -1992,8 +2013,8 @@ protected: */ class Query_log_event: public Log_event { - LEX_STRING user; - LEX_STRING host; + LEX_CSTRING user; + LEX_CSTRING host; protected: Log_event::Byte* data_buf; public: @@ -3020,9 +3041,9 @@ public: UNDEF_F= 0, UNSIGNED_F= 1 }; - char *name; + const char *name; uint name_len; - char *val; + const char *val; ulong val_len; Item_result type; uint charset_number; @@ -3031,8 +3052,8 @@ public: #ifdef MYSQL_SERVER bool deferred; query_id_t query_id; - User_var_log_event(THD* thd_arg, char *name_arg, uint name_len_arg, - char *val_arg, ulong val_len_arg, Item_result type_arg, + User_var_log_event(THD* thd_arg, const char *name_arg, uint name_len_arg, + const char *val_arg, ulong val_len_arg, Item_result type_arg, uint charset_number_arg, uchar flags_arg, bool using_trans, bool direct) :Log_event(thd_arg, 0, using_trans), @@ -3361,6 +3382,7 @@ public: const Format_description_log_event *description_event); ~Gtid_log_event() { } Log_event_type get_type_code() { return GTID_EVENT; } + enum_logged_status logged_status() { return LOGGED_NO_DATA; } int get_data_size() { return GTID_HEADER_LEN + ((flags2 & FL_GROUP_COMMIT_ID) ? 2 : 0); @@ -3437,7 +3459,7 @@ public: The three elements in the body repeat COUNT times to form the GTID list. - At the time of writing, only one flag bit is in use. + At the time of writing, only two flag bit are in use. Bit 28 of `count' is used for flag FLAG_UNTIL_REACHED, which is sent in a Gtid_list event from the master to the slave to indicate that the START @@ -3455,9 +3477,12 @@ public: uint64 *sub_id_list; static const uint element_size= 4+4+8; - static const uint32 FLAG_UNTIL_REACHED= (1<<28); - static const uint32 FLAG_IGN_GTIDS= (1<<29); - + /* Upper bits stored in 'count'. See comment above */ + enum gtid_flags + { + FLAG_UNTIL_REACHED= (1<<28), + FLAG_IGN_GTIDS= (1<<29), + }; #ifdef MYSQL_SERVER Gtid_list_log_event(rpl_binlog_state *gtid_set, uint32 gl_flags); Gtid_list_log_event(slave_connection_state *gtid_set, uint32 gl_flags); @@ -3857,6 +3882,7 @@ public: virtual int get_data_size(); virtual Log_event_type get_type_code(); + enum_logged_status logged_status() { return LOGGED_NO_DATA; } virtual bool is_valid() const; virtual bool is_part_of_group() { return 1; } @@ -3886,6 +3912,7 @@ private: char *m_save_thd_query_txt; uint m_save_thd_query_len; bool m_saved_thd_query; + bool m_used_query_txt; }; /** @@ -4272,6 +4299,7 @@ public: const char *get_db_name() const { return m_dbnam; } virtual Log_event_type get_type_code() { return TABLE_MAP_EVENT; } + virtual enum_logged_status logged_status() { return LOGGED_TABLE_MAP; } virtual bool is_valid() const { return m_memory != NULL; /* we check malloc */ } virtual bool is_part_of_group() { return 1; } @@ -4400,6 +4428,7 @@ public: void update_flags() { int2store(temp_buf + m_flags_pos, m_flags); } Log_event_type get_type_code() { return m_type; } /* Specific type (_V1 etc) */ + enum_logged_status logged_status() { return LOGGED_ROW_EVENT; } virtual Log_event_type get_general_type_code() = 0; /* General rows op type, no version */ #if defined(MYSQL_SERVER) && defined(HAVE_REPLICATION) @@ -4579,6 +4608,7 @@ protected: int find_key(); // Find a best key to use in find_row() int find_row(rpl_group_info *); int write_row(rpl_group_info *, const bool); + int update_sequence(); // Unpack the current row into m_table->record[0], but with // a different columns bitmap. @@ -5163,6 +5193,7 @@ inline int Log_event_writer::write(Log_event *ev) ev->writer= this; int res= ev->write(); IF_DBUG(ev->writer= 0,); // writer must be set before every Log_event::write + add_status(ev->logged_status()); return res; } diff --git a/sql/mdl.cc b/sql/mdl.cc index 25809c3d9cc..f751a8e298e 100644 --- a/sql/mdl.cc +++ b/sql/mdl.cc @@ -2320,13 +2320,6 @@ MDL_context::upgrade_shared_lock(MDL_ticket *mdl_ticket, if (mdl_ticket->has_stronger_or_equal_type(new_type)) DBUG_RETURN(FALSE); - /* Only allow upgrades from UPGRADABLE/NO_WRITE/NO_READ_WRITE/READ/WRITE */ - DBUG_ASSERT(mdl_ticket->m_type == MDL_SHARED_UPGRADABLE || - mdl_ticket->m_type == MDL_SHARED_NO_WRITE || - mdl_ticket->m_type == MDL_SHARED_NO_READ_WRITE || - mdl_ticket->m_type == MDL_SHARED_READ || - mdl_ticket->m_type == MDL_SHARED_WRITE); - mdl_xlock_request.init(&mdl_ticket->m_lock->key, new_type, MDL_TRANSACTION); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c784fb23898..d0993151f76 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -302,6 +302,7 @@ static TYPELIB tc_heuristic_recover_typelib= const char *first_keyword= "first", *binary_keyword= "BINARY"; const char *my_localhost= "localhost", *delayed_user= "DELAYED"; +const char *quoted_string= "%`s"; bool opt_large_files= sizeof(my_off_t) > 4; static my_bool opt_autocommit; ///< for --autocommit command-line option @@ -311,14 +312,6 @@ static my_bool opt_autocommit; ///< for --autocommit command-line option */ static my_bool opt_verbose= 0; -arg_cmp_func Arg_comparator::comparator_matrix[6][2] = -{{&Arg_comparator::compare_string, &Arg_comparator::compare_e_string}, - {&Arg_comparator::compare_real, &Arg_comparator::compare_e_real}, - {&Arg_comparator::compare_int_signed, &Arg_comparator::compare_e_int}, - {&Arg_comparator::compare_row, &Arg_comparator::compare_e_row}, - {&Arg_comparator::compare_decimal, &Arg_comparator::compare_e_decimal}, - {&Arg_comparator::compare_datetime, &Arg_comparator::compare_e_datetime}}; - /* Timer info to be used by the SQL layer */ MY_TIMER_INFO sys_timer_info; @@ -644,10 +637,10 @@ my_bool encrypt_binlog; my_bool encrypt_tmp_disk_tables, encrypt_tmp_files; /** name of reference on left expression in rewritten IN subquery */ -const char *in_left_expr_name= "<left expr>"; +const LEX_CSTRING in_left_expr_name= {STRING_WITH_LEN("<left expr>") }; /** name of additional condition */ -const char *in_additional_cond= "<IN COND>"; -const char *in_having_cond= "<IN HAVING>"; +const LEX_CSTRING in_having_cond= {STRING_WITH_LEN("<IN HAVING>") }; +const LEX_CSTRING in_additional_cond= {STRING_WITH_LEN("<IN COND>") }; /** Number of connection errors when selecting on the listening port */ ulong connection_errors_select= 0; @@ -1027,7 +1020,9 @@ static PSI_mutex_info all_server_mutexes[]= PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave, - key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock; + key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock, + key_LOCK_SEQUENCE; + static PSI_rwlock_info all_server_rwlocks[]= { @@ -1038,6 +1033,7 @@ static PSI_rwlock_info all_server_rwlocks[]= { &key_rwlock_LOCK_logger, "LOGGER::LOCK_logger", 0}, { &key_rwlock_LOCK_sys_init_connect, "LOCK_sys_init_connect", PSI_FLAG_GLOBAL}, { &key_rwlock_LOCK_sys_init_slave, "LOCK_sys_init_slave", PSI_FLAG_GLOBAL}, + { &key_LOCK_SEQUENCE, "LOCK_SEQUENCE", 0}, { &key_rwlock_LOCK_system_variables_hash, "LOCK_system_variables_hash", PSI_FLAG_GLOBAL}, { &key_rwlock_query_cache_query_lock, "Query_cache_query::lock", 0} }; @@ -2246,6 +2242,7 @@ void clean_up(bool print_message) #endif wsrep_thr_deinit(); my_uuid_end(); + delete type_handler_data; delete binlog_filter; delete global_rpl_filter; end_ssl(); @@ -3784,6 +3781,7 @@ SHOW_VAR com_status_vars[]= { {"alter_function", STMT_STATUS(SQLCOM_ALTER_FUNCTION)}, {"alter_procedure", STMT_STATUS(SQLCOM_ALTER_PROCEDURE)}, {"alter_server", STMT_STATUS(SQLCOM_ALTER_SERVER)}, + {"alter_sequence", STMT_STATUS(SQLCOM_ALTER_SEQUENCE)}, {"alter_table", STMT_STATUS(SQLCOM_ALTER_TABLE)}, {"alter_tablespace", STMT_STATUS(SQLCOM_ALTER_TABLESPACE)}, {"alter_user", STMT_STATUS(SQLCOM_ALTER_USER)}, @@ -3804,6 +3802,7 @@ SHOW_VAR com_status_vars[]= { {"create_index", STMT_STATUS(SQLCOM_CREATE_INDEX)}, {"create_procedure", STMT_STATUS(SQLCOM_CREATE_PROCEDURE)}, {"create_role", STMT_STATUS(SQLCOM_CREATE_ROLE)}, + {"create_sequence", STMT_STATUS(SQLCOM_CREATE_SEQUENCE)}, {"create_server", STMT_STATUS(SQLCOM_CREATE_SERVER)}, {"create_table", STMT_STATUS(SQLCOM_CREATE_TABLE)}, {"create_temporary_table", COM_STATUS(com_create_tmp_table)}, @@ -3822,6 +3821,7 @@ SHOW_VAR com_status_vars[]= { {"drop_procedure", STMT_STATUS(SQLCOM_DROP_PROCEDURE)}, {"drop_role", STMT_STATUS(SQLCOM_DROP_ROLE)}, {"drop_server", STMT_STATUS(SQLCOM_DROP_SERVER)}, + {"drop_sequence", STMT_STATUS(SQLCOM_DROP_SEQUENCE)}, {"drop_table", STMT_STATUS(SQLCOM_DROP_TABLE)}, {"drop_temporary_table", COM_STATUS(com_drop_tmp_table)}, {"drop_trigger", STMT_STATUS(SQLCOM_DROP_TRIGGER)}, @@ -4140,6 +4140,13 @@ static int init_common_variables() sf_malloc_dbug_id= mariadb_dbug_id; #endif + if (!(type_handler_data= new Type_handler_data) || + type_handler_data->init()) + { + sql_perror("Could not allocate type_handler_data"); + return 1; + } + max_system_variables.pseudo_thread_id= ~(my_thread_id) 0; server_start_time= flush_status_time= my_time(0); my_disable_copystat_in_redel= 1; @@ -4916,7 +4923,7 @@ static int init_default_storage_engine_impl(const char *opt_name, return 0; } - LEX_STRING name= { engine_name, strlen(engine_name) }; + LEX_CSTRING name= { engine_name, strlen(engine_name) }; plugin_ref plugin; handlerton *hton; if ((plugin= ha_resolve_by_name(0, &name, false))) @@ -5361,7 +5368,7 @@ static int init_server_components() else { /* fall back to the log files if tables are not present */ - LEX_STRING csv_name={C_STRING_WITH_LEN("csv")}; + LEX_CSTRING csv_name={STRING_WITH_LEN("csv")}; if (!plugin_is_ready(&csv_name, MYSQL_STORAGE_ENGINE_PLUGIN)) { /* purecov: begin inspected */ @@ -7320,13 +7327,6 @@ struct my_option my_long_options[]= &max_binlog_dump_events, &max_binlog_dump_events, 0, GET_INT, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, #endif /* HAVE_REPLICATION */ -#ifdef SAFE_MUTEX - {"debug-mutex-deadlock-detector", 0, - "Enable checking of wrong mutex usage.", - &safe_mutex_deadlock_detector, - &safe_mutex_deadlock_detector, - 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, -#endif {"debug-no-sync", 0, "Disables system sync calls. Only for running tests or debugging!", &my_disable_sync, &my_disable_sync, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, @@ -8449,6 +8449,7 @@ SHOW_VAR status_vars[]= { {"Handler_rollback", (char*) offsetof(STATUS_VAR, ha_rollback_count), SHOW_LONG_STATUS}, {"Handler_savepoint", (char*) offsetof(STATUS_VAR, ha_savepoint_count), SHOW_LONG_STATUS}, {"Handler_savepoint_rollback",(char*) offsetof(STATUS_VAR, ha_savepoint_rollback_count), SHOW_LONG_STATUS}, + {"Handler_tmp_delete", (char*) offsetof(STATUS_VAR, ha_tmp_delete_count), SHOW_LONG_STATUS}, {"Handler_tmp_update", (char*) offsetof(STATUS_VAR, ha_tmp_update_count), SHOW_LONG_STATUS}, {"Handler_tmp_write", (char*) offsetof(STATUS_VAR, ha_tmp_write_count), SHOW_LONG_STATUS}, {"Handler_update", (char*) offsetof(STATUS_VAR, ha_update_count), SHOW_LONG_STATUS}, diff --git a/sql/mysqld.h b/sql/mysqld.h index 912fe3696ae..aa08f77d468 100644 --- a/sql/mysqld.h +++ b/sql/mysqld.h @@ -237,7 +237,7 @@ extern const char *first_keyword, *delayed_user, *binary_keyword; extern MYSQL_PLUGIN_IMPORT const char *my_localhost; extern MYSQL_PLUGIN_IMPORT const char **errmesg; /* Error messages */ extern const char *myisam_recover_options_str; -extern const char *in_left_expr_name, *in_additional_cond, *in_having_cond; +extern const LEX_CSTRING in_left_expr_name, in_additional_cond, in_having_cond; extern SHOW_VAR status_vars[]; extern struct system_variables max_system_variables; extern struct system_status_var global_status_var; @@ -270,6 +270,7 @@ extern my_bool encrypt_binlog; extern my_bool encrypt_tmp_disk_tables, encrypt_tmp_files; extern ulong encryption_algorithm; extern const char *encryption_algorithm_names[]; +extern const char *quoted_string; #ifdef HAVE_PSI_INTERFACE #ifdef HAVE_MMAP @@ -313,8 +314,8 @@ extern PSI_mutex_key key_LOCK_gtid_waiting; extern PSI_rwlock_key key_rwlock_LOCK_grant, key_rwlock_LOCK_logger, key_rwlock_LOCK_sys_init_connect, key_rwlock_LOCK_sys_init_slave, - key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock; - + key_rwlock_LOCK_system_variables_hash, key_rwlock_query_cache_query_lock, + key_LOCK_SEQUENCE; #ifdef HAVE_MMAP extern PSI_cond_key key_PAGE_cond, key_COND_active, key_COND_pool; #endif /* HAVE_MMAP */ @@ -692,11 +693,13 @@ enum enum_query_type QT_ITEM_CACHE_WRAPPER_SKIP_DETAILS | QT_ITEM_SUBSELECT_ID_ONLY, - /// This is used for EXPLAIN EXTENDED extra warnings + QT_SHOW_SELECT_NUMBER= (1<<10), + /// This is used for EXPLAIN EXTENDED extra warnings / Be more detailed /// Be more detailed than QT_EXPLAIN. /// Perhaps we should eventually include QT_ITEM_IDENT_SKIP_CURRENT_DATABASE /// here, as it would give better readable results - QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET, + QT_EXPLAIN_EXTENDED= QT_TO_SYSTEM_CHARSET| + QT_SHOW_SELECT_NUMBER, // If an expression is constant, print the expression, not the value // it evaluates to. Should be used for error messages, so that they diff --git a/sql/opt_range.cc b/sql/opt_range.cc index f111ecf8999..9846583a83c 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -4398,7 +4398,7 @@ static void print_partitioning_index(KEY_PART *parts, KEY_PART *parts_end) fprintf(DBUG_FILE, "partitioning INDEX("); for (KEY_PART *p=parts; p != parts_end; p++) { - fprintf(DBUG_FILE, "%s%s", p==parts?"":" ,", p->field->field_name); + fprintf(DBUG_FILE, "%s%s", p==parts?"":" ,", p->field->field_name.str); } fputs(");\n", DBUG_FILE); DBUG_UNLOCK_FILE; @@ -4437,7 +4437,7 @@ static void dbug_print_segment_range(SEL_ARG *arg, KEY_PART *part) fputs(" <= ", DBUG_FILE); } - fprintf(DBUG_FILE, "%s", part->field->field_name); + fprintf(DBUG_FILE, "%s", part->field->field_name.str); if (!(arg->max_flag & NO_MAX_RANGE)) { @@ -4476,7 +4476,7 @@ static void dbug_print_singlepoint_range(SEL_ARG **start, uint num) for (SEL_ARG **arg= start; arg != end; arg++) { Field *field= (*arg)->field; - fprintf(DBUG_FILE, "%s%s=", (arg==start)?"":", ", field->field_name); + fprintf(DBUG_FILE, "%s%s=", (arg==start)?"":", ", field->field_name.str); dbug_print_field(field); } fputs("\n", DBUG_FILE); @@ -7279,9 +7279,19 @@ SEL_TREE *Item_func_in::get_func_row_mm_tree(RANGE_OPT_PARAM *param, param->current_table); uint row_cols= key_row->cols(); Dynamic_array <Key_col_info> key_cols_info(row_cols); - cmp_item_row *row_cmp_item= (cmp_item_row *) - (array ? ((in_row *) array)->get_cmp_item() : - cmp_items[(uint) ROW_RESULT]); + cmp_item_row *row_cmp_item; + + if (array) + { + in_row *row= static_cast<in_row*>(array); + row_cmp_item= static_cast<cmp_item_row*>(row->get_cmp_item()); + } + else + { + DBUG_ASSERT(get_comparator_type_handler(0) == &type_handler_row); + row_cmp_item= static_cast<cmp_item_row*>(get_comparator_cmp_item(0)); + } + DBUG_ASSERT(row_cmp_item); Item **key_col_ptr= key_row->addr(0); for(uint i= 0; i < row_cols; i++, key_col_ptr++) @@ -7494,6 +7504,8 @@ SEL_TREE *Item_bool_func::get_full_func_mm_tree(RANGE_OPT_PARAM *param, param->current_table); #ifdef HAVE_SPATIAL Field::geometry_type sav_geom_type; + LINT_INIT_STRUCT(sav_geom_type); + if (field_item->field->type() == MYSQL_TYPE_GEOMETRY) { sav_geom_type= ((Field_geom*) field_item->field)->geom_type; @@ -7919,7 +7931,9 @@ Item_func_like::get_mm_leaf(RANGE_OPT_PARAM *param, if (!(res= value->val_str(&tmp))) DBUG_RETURN(&null_element); - if (field->cmp_type() != STRING_RESULT) + if (field->cmp_type() != STRING_RESULT || + field->type_handler() == &type_handler_enum || + field->type_handler() == &type_handler_set) DBUG_RETURN(0); /* @@ -8015,27 +8029,59 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param, goto end; err= value->save_in_field_no_warnings(field, 1); - if (err == 2 && field->cmp_type() == STRING_RESULT) + if (err > 0) { - if (type == EQ_FUNC || type == EQUAL_FUNC) + if (field->type_handler() == &type_handler_enum || + field->type_handler() == &type_handler_set) { - tree= new (alloc) SEL_ARG(field, 0, 0); - tree->type= SEL_ARG::IMPOSSIBLE; + if (type == EQ_FUNC || type == EQUAL_FUNC) + tree= new (alloc) SEL_ARG_IMPOSSIBLE(field); + goto end; } - else - tree= NULL; /* Cannot infer anything */ - goto end; - } - if (err > 0) - { - if (field->cmp_type() != value->result_type()) + + if (err == 2 && field->cmp_type() == STRING_RESULT) + { + if (type == EQ_FUNC || type == EQUAL_FUNC) + tree= new (alloc) SEL_ARG_IMPOSSIBLE(field); + else + tree= NULL; /* Cannot infer anything */ + goto end; + } + + if (err == 3 && field->type() == FIELD_TYPE_DATE) + { + /* + We were saving DATETIME into a DATE column, the conversion went ok + but a non-zero time part was cut off. + + In MySQL's SQL dialect, DATE and DATETIME are compared as datetime + values. Index over a DATE column uses DATE comparison. Changing + from one comparison to the other is possible: + + datetime(date_col)< '2007-12-10 12:34:55' -> date_col<='2007-12-10' + datetime(date_col)<='2007-12-10 12:34:55' -> date_col<='2007-12-10' + + datetime(date_col)> '2007-12-10 12:34:55' -> date_col>='2007-12-10' + datetime(date_col)>='2007-12-10 12:34:55' -> date_col>='2007-12-10' + + but we'll need to convert '>' to '>=' and '<' to '<='. This will + be done together with other types at the end of this function + (grep for stored_field_cmp_to_item) + */ + if (type == EQ_FUNC || type == EQUAL_FUNC) + { + tree= new (alloc) SEL_ARG_IMPOSSIBLE(field); + goto end; + } + // Continue with processing non-equality ranges + } + else if (field->cmp_type() != value->result_type()) { if ((type == EQ_FUNC || type == EQUAL_FUNC) && value->result_type() == item_cmp_type(field->result_type(), value->result_type())) { - tree= new (alloc) SEL_ARG(field, 0, 0); - tree->type= SEL_ARG::IMPOSSIBLE; + tree= new (alloc) SEL_ARG_IMPOSSIBLE(field); goto end; } else @@ -8045,31 +8091,7 @@ Item_bool_func::get_mm_leaf(RANGE_OPT_PARAM *param, for the cases like int_field > 999999999999999999999999 as well. */ tree= 0; - if (err == 3 && field->type() == FIELD_TYPE_DATE && - (type == GT_FUNC || type == GE_FUNC || - type == LT_FUNC || type == LE_FUNC) ) - { - /* - We were saving DATETIME into a DATE column, the conversion went ok - but a non-zero time part was cut off. - - In MySQL's SQL dialect, DATE and DATETIME are compared as datetime - values. Index over a DATE column uses DATE comparison. Changing - from one comparison to the other is possible: - - datetime(date_col)< '2007-12-10 12:34:55' -> date_col<='2007-12-10' - datetime(date_col)<='2007-12-10 12:34:55' -> date_col<='2007-12-10' - - datetime(date_col)> '2007-12-10 12:34:55' -> date_col>='2007-12-10' - datetime(date_col)>='2007-12-10 12:34:55' -> date_col>='2007-12-10' - - but we'll need to convert '>' to '>=' and '<' to '<='. This will - be done together with other types at the end of this function - (grep for stored_field_cmp_to_item) - */ - } - else - goto end; + goto end; } } diff --git a/sql/opt_range.h b/sql/opt_range.h index c1f7079ce7f..95e231433d2 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -594,6 +594,17 @@ public: }; +class SEL_ARG_IMPOSSIBLE: public SEL_ARG +{ +public: + SEL_ARG_IMPOSSIBLE(Field *field) + :SEL_ARG(field, 0, 0) + { + type= SEL_ARG::IMPOSSIBLE; + } +}; + + class RANGE_OPT_PARAM { public: diff --git a/sql/opt_subselect.cc b/sql/opt_subselect.cc index f37db88957a..4024ae26b8b 100644 --- a/sql/opt_subselect.cc +++ b/sql/opt_subselect.cc @@ -843,34 +843,9 @@ bool subquery_types_allow_materialization(Item_in_subselect *in_subs) all_are_fields &= (outer->real_item()->type() == Item::FIELD_ITEM && inner->real_item()->type() == Item::FIELD_ITEM); total_key_length += inner->max_length; - if (outer->cmp_type() != inner->cmp_type()) + if (!inner->type_handler()->subquery_type_allows_materialization(inner, + outer)) DBUG_RETURN(FALSE); - switch (outer->cmp_type()) { - case STRING_RESULT: - if (!(outer->collation.collation == inner->collation.collation)) - DBUG_RETURN(FALSE); - // Materialization does not work with BLOB columns - if (inner->field_type() == MYSQL_TYPE_BLOB || - inner->field_type() == MYSQL_TYPE_GEOMETRY) - DBUG_RETURN(FALSE); - /* - Materialization also is unable to work when create_tmp_table() will - create a blob column because item->max_length is too big. - The following check is copied from Item::make_string_field(): - */ - if (inner->too_big_for_varchar()) - { - DBUG_RETURN(FALSE); - } - break; - case TIME_RESULT: - if (mysql_type_to_time_type(outer->field_type()) != - mysql_type_to_time_type(inner->field_type())) - DBUG_RETURN(FALSE); - default: - /* suitable for materialization */ - break; - } } /* @@ -4126,12 +4101,13 @@ SJ_TMP_TABLE::create_sj_weedout_tmp_table(THD *thd) /* Create the field */ { + LEX_CSTRING field_name= {STRING_WITH_LEN("rowids") }; /* For the sake of uniformity, always use Field_varstring (altough we could use Field_string for shorter keys) */ - field= new Field_varstring(uniq_tuple_length_arg, FALSE, "rowids", share, - &my_charset_bin); + field= new Field_varstring(uniq_tuple_length_arg, FALSE, &field_name, + share, &my_charset_bin); if (!field) DBUG_RETURN(0); field->table= table; @@ -4995,13 +4971,13 @@ int rewrite_to_index_subquery_engine(JOIN *join) join->unit->item && join->unit->item->substype() == Item_subselect::IN_SUBS && join->table_count == 1 && join->conds && - !join->unit->is_union()) + !join->unit->is_unit_op()) { if (!join->having) { Item *where= join->conds; if (join_tab[0].type == JT_EQ_REF && - join_tab[0].ref.items[0]->name == in_left_expr_name) + join_tab[0].ref.items[0]->name.str == in_left_expr_name.str) { remove_subq_pushed_predicates(join, &where); save_index_subquery_explain_info(join_tab, where); @@ -5015,7 +4991,7 @@ int rewrite_to_index_subquery_engine(JOIN *join) where))); } else if (join_tab[0].type == JT_REF && - join_tab[0].ref.items[0]->name == in_left_expr_name) + join_tab[0].ref.items[0]->name.str == in_left_expr_name.str) { remove_subq_pushed_predicates(join, &where); save_index_subquery_explain_info(join_tab, where); @@ -5031,8 +5007,8 @@ int rewrite_to_index_subquery_engine(JOIN *join) 0))); } } else if (join_tab[0].type == JT_REF_OR_NULL && - join_tab[0].ref.items[0]->name == in_left_expr_name && - join->having->name == in_having_cond) + join_tab[0].ref.items[0]->name.str == in_left_expr_name.str && + join->having->name.str == in_having_cond.str) { join_tab[0].type= JT_INDEX_SUBQUERY; join->error= 0; @@ -5063,7 +5039,7 @@ int rewrite_to_index_subquery_engine(JOIN *join) static Item *remove_additional_cond(Item* conds) { - if (conds->name == in_additional_cond) + if (conds->name.str == in_additional_cond.str) return 0; if (conds->type() == Item::COND_ITEM) { @@ -5072,7 +5048,7 @@ static Item *remove_additional_cond(Item* conds) Item *item; while ((item= li++)) { - if (item->name == in_additional_cond) + if (item->name.str == in_additional_cond.str) { li.remove(); if (cnd->argument_list()->elements == 1) @@ -5335,7 +5311,7 @@ int select_value_catcher::setup(List<Item> *items) List_iterator<Item> li(*items); for (uint i= 0; (sel_item= li++); i++) { - if (!(row[i]= Item_cache::get_cache(thd, sel_item))) + if (!(row[i]= sel_item->get_cache(thd))) return TRUE; row[i]->setup(thd, sel_item); } diff --git a/sql/opt_table_elimination.cc b/sql/opt_table_elimination.cc index b7010d1f8ed..d3d1bc97a70 100644 --- a/sql/opt_table_elimination.cc +++ b/sql/opt_table_elimination.cc @@ -1843,7 +1843,7 @@ void Dep_analysis_context::dbug_print_deps() (long)(eq_mod - equality_mods), str.c_ptr(), eq_mod->field->table->table->alias.c_ptr(), - eq_mod->field->field->field_name); + eq_mod->field->field->field_name.str); } else { @@ -1867,7 +1867,7 @@ void Dep_analysis_context::dbug_print_deps() { fprintf(DBUG_FILE, " field %s.%s ->", table_dep->table->alias.c_ptr(), - field_dep->field->field_name); + field_dep->field->field_name.str); uint ofs= field_dep->bitmap_offset; for (uint bit= ofs; bit < ofs + n_equality_mods; bit++) { diff --git a/sql/parse_file.cc b/sql/parse_file.cc index 0c44f6a9820..196feabb235 100644 --- a/sql/parse_file.cc +++ b/sql/parse_file.cc @@ -136,7 +136,7 @@ static ulonglong view_algo_from_frm(ulonglong val) static my_bool -write_parameter(IO_CACHE *file, uchar* base, File_option *parameter) +write_parameter(IO_CACHE *file, const uchar* base, File_option *parameter) { char num_buf[20]; // buffer for numeric operations // string for numeric operations @@ -248,8 +248,9 @@ write_parameter(IO_CACHE *file, uchar* base, File_option *parameter) my_bool -sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, - const LEX_STRING *type, +sql_create_definition_file(const LEX_CSTRING *dir, + const LEX_CSTRING *file_name, + const LEX_CSTRING *type, uchar* base, File_option *parameters) { File handler; @@ -399,7 +400,7 @@ my_bool rename_in_schema_file(THD *thd, */ File_parser * -sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root, +sql_parse_prepare(const LEX_CSTRING *file_name, MEM_ROOT *mem_root, bool bad_format_errors) { MY_STAT stat_info; @@ -598,13 +599,13 @@ read_escaped_string(const char *ptr, const char *eol, LEX_STRING *str) const char * parse_escaped_string(const char *ptr, const char *end, MEM_ROOT *mem_root, - LEX_STRING *str) + LEX_CSTRING *str) { const char *eol= strchr(ptr, '\n'); if (eol == 0 || eol >= end || !(str->str= (char*) alloc_root(mem_root, (eol - ptr) + 1)) || - read_escaped_string(ptr, eol, str)) + read_escaped_string(ptr, eol, (LEX_STRING*) str)) return 0; return eol+1; @@ -802,7 +803,7 @@ File_parser::parse(uchar* base, MEM_ROOT *mem_root, case FILE_OPTIONS_ESTRING: { if (!(ptr= parse_escaped_string(ptr, end, mem_root, - (LEX_STRING *) + (LEX_CSTRING *) (base + parameter->offset)))) { my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), diff --git a/sql/parse_file.h b/sql/parse_file.h index 87917dbd71b..3f48b2072db 100644 --- a/sql/parse_file.h +++ b/sql/parse_file.h @@ -42,7 +42,7 @@ enum file_opt_type { struct File_option { - LEX_STRING name; /**< Name of the option */ + LEX_CSTRING name; /**< Name of the option */ my_ptrdiff_t offset; /**< offset to base address of value */ file_opt_type type; /**< Option type */ }; @@ -82,15 +82,16 @@ bool get_file_options_ulllist(const char *&ptr, const char *end, const char * parse_escaped_string(const char *ptr, const char *end, MEM_ROOT *mem_root, - LEX_STRING *str); + LEX_CSTRING *str); class File_parser; -File_parser *sql_parse_prepare(const LEX_STRING *file_name, +File_parser *sql_parse_prepare(const LEX_CSTRING *file_name, MEM_ROOT *mem_root, bool bad_format_errors); my_bool -sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name, - const LEX_STRING *type, +sql_create_definition_file(const LEX_CSTRING *dir, + const LEX_CSTRING *file_name, + const LEX_CSTRING *type, uchar* base, File_option *parameters); my_bool rename_in_schema_file(THD *thd, const char *schema, const char *old_name, @@ -99,19 +100,19 @@ my_bool rename_in_schema_file(THD *thd, class File_parser: public Sql_alloc { char *start, *end; - LEX_STRING file_type; + LEX_CSTRING file_type; bool content_ok; public: File_parser() :start(0), end(0), content_ok(0) { file_type.str= 0; file_type.length= 0; } bool ok() { return content_ok; } - const LEX_STRING *type() const { return &file_type; } + const LEX_CSTRING *type() const { return &file_type; } my_bool parse(uchar* base, MEM_ROOT *mem_root, struct File_option *parameters, uint required, Unknown_key_hook *hook) const; - friend File_parser *sql_parse_prepare(const LEX_STRING *file_name, + friend File_parser *sql_parse_prepare(const LEX_CSTRING *file_name, MEM_ROOT *mem_root, bool bad_format_errors); }; diff --git a/sql/partition_element.h b/sql/partition_element.h index b979b7a58e6..c774994b7f5 100644 --- a/sql/partition_element.h +++ b/sql/partition_element.h @@ -96,14 +96,14 @@ public: ha_rows part_max_rows; ha_rows part_min_rows; longlong range_value; - char *partition_name; - char *tablespace_name; + const char *partition_name; + const char *tablespace_name; struct st_ddl_log_memory_entry *log_entry; - char* part_comment; - char* data_file_name; - char* index_file_name; + const char* part_comment; + const char* data_file_name; + const char* index_file_name; handlerton *engine_type; - LEX_STRING connect_string; + LEX_CSTRING connect_string; enum partition_state part_state; uint16 nodegroup_id; bool has_null_value; @@ -115,7 +115,7 @@ public: partition_name(NULL), tablespace_name(NULL), log_entry(NULL), part_comment(NULL), data_file_name(NULL), index_file_name(NULL), - engine_type(NULL), connect_string(null_lex_str), part_state(PART_NORMAL), + engine_type(NULL), connect_string(null_clex_str), part_state(PART_NORMAL), nodegroup_id(UNDEF_NODEGROUP), has_null_value(FALSE), signed_flag(FALSE), max_value(FALSE) { @@ -129,7 +129,7 @@ public: data_file_name(part_elem->data_file_name), index_file_name(part_elem->index_file_name), engine_type(part_elem->engine_type), - connect_string(null_lex_str), + connect_string(null_clex_str), part_state(part_elem->part_state), nodegroup_id(part_elem->nodegroup_id), has_null_value(FALSE) diff --git a/sql/partition_info.cc b/sql/partition_info.cc index 535ed4612e0..31e48a85104 100644 --- a/sql/partition_info.cc +++ b/sql/partition_info.cc @@ -562,10 +562,11 @@ bool partition_info::set_up_defaults_for_partitioning(THD *thd, handler *file, Check that the user haven't defined the same field twice in key or column list partitioning. */ -char* partition_info::find_duplicate_field() + +const char* partition_info::find_duplicate_field() { - char *field_name_outer, *field_name_inner; - List_iterator<char> it_outer(part_field_list); + const char *field_name_outer, *field_name_inner; + List_iterator<const char> it_outer(part_field_list); uint num_fields= part_field_list.elements; uint i,j; DBUG_ENTER("partition_info::find_duplicate_field"); @@ -573,7 +574,7 @@ char* partition_info::find_duplicate_field() for (i= 0; i < num_fields; i++) { field_name_outer= it_outer++; - List_iterator<char> it_inner(part_field_list); + List_iterator<const char> it_inner(part_field_list); for (j= 0; j < num_fields; j++) { field_name_inner= it_inner++; @@ -1384,7 +1385,7 @@ bool partition_info::check_partition_info(THD *thd, handlerton **eng_type, handlerton *table_engine= default_engine_type; uint i, tot_partitions; bool result= TRUE, table_engine_set; - char *same_name; + const char *same_name; DBUG_ENTER("partition_info::check_partition_info"); DBUG_ASSERT(default_engine_type != partition_hton); @@ -1919,7 +1920,7 @@ void partition_info::report_part_expr_error(bool use_subpart_expr) !(type == HASH_PARTITION && list_of_fields)) { my_error(ER_FIELD_TYPE_NOT_ALLOWED_AS_PARTITION_FIELD, MYF(0), - item_field->name); + item_field->name.str); DBUG_VOID_RETURN; } } @@ -2647,9 +2648,9 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info) } /* Check that it will use the same fields in KEY (fields) list. */ - List_iterator<char> old_field_name_it(part_field_list); - List_iterator<char> new_field_name_it(new_part_info->part_field_list); - char *old_name, *new_name; + List_iterator<const char> old_field_name_it(part_field_list); + List_iterator<const char> new_field_name_it(new_part_info->part_field_list); + const char *old_name, *new_name; while ((old_name= old_field_name_it++)) { new_name= new_field_name_it++; @@ -2662,9 +2663,9 @@ bool partition_info::has_same_partitioning(partition_info *new_part_info) if (is_sub_partitioned()) { /* Check that it will use the same fields in KEY subpart fields list. */ - List_iterator<char> old_field_name_it(subpart_field_list); - List_iterator<char> new_field_name_it(new_part_info->subpart_field_list); - char *old_name, *new_name; + List_iterator<const char> old_field_name_it(subpart_field_list); + List_iterator<const char> new_field_name_it(new_part_info->subpart_field_list); + const char *old_name, *new_name; while ((old_name= old_field_name_it++)) { new_name= new_field_name_it++; diff --git a/sql/partition_info.h b/sql/partition_info.h index 91ceee70ea4..38a353c8507 100644 --- a/sql/partition_info.h +++ b/sql/partition_info.h @@ -43,8 +43,8 @@ public: List<partition_element> partitions; List<partition_element> temp_partitions; - List<char> part_field_list; - List<char> subpart_field_list; + List<const char> part_field_list; + List<const char> subpart_field_list; /* If there is no subpartitioning, use only this func to get partition ids. @@ -299,7 +299,7 @@ public: bool set_up_defaults_for_partitioning(THD *thd, handler *file, HA_CREATE_INFO *info, uint start_no); - char *find_duplicate_field(); + const char *find_duplicate_field(); char *find_duplicate_name(); bool check_engine_mix(handlerton *engine_type, bool default_engine); bool check_range_constants(THD *thd); diff --git a/sql/procedure.cc b/sql/procedure.cc index 8f9d6c0a7f3..3d36b7adfa3 100644 --- a/sql/procedure.cc +++ b/sql/procedure.cc @@ -89,7 +89,7 @@ setup_procedure(THD *thd,ORDER *param,select_result *result, for (i=0 ; i < array_elements(sql_procs) ; i++) { if (!my_strcasecmp(system_charset_info, - (*param->item)->name,sql_procs[i].name)) + (*param->item)->name.str, sql_procs[i].name)) { Procedure *proc=(*sql_procs[i].init)(thd,param,result,field_list); *error= !proc; diff --git a/sql/procedure.h b/sql/procedure.h index b9d5066bb3d..a1c9b95f20b 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -40,13 +40,14 @@ class Item_proc :public Item public: Item_proc(THD *thd, const char *name_par): Item(thd) { - this->name=(char*) name_par; + this->name.str= name_par; + this->name.length= strlen(name_par); } enum Type type() const { return Item::PROC_ITEM; } virtual void set(double nr)=0; virtual void set(const char *str,uint length,CHARSET_INFO *cs)=0; virtual void set(longlong nr)=0; - virtual enum_field_types field_type() const=0; + const Type_handler *type_handler() const=0; void set(const char *str) { set(str,(uint) strlen(str), default_charset()); } void make_field(THD *thd, Send_field *tmp_field) { @@ -70,9 +71,7 @@ public: { decimals=dec; max_length=float_length(dec); } - enum Item_result result_type () const { return REAL_RESULT; } - enum Item_result cmp_type () const { return REAL_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + const Type_handler *type_handler() const { return &type_handler_double; } void set(double nr) { value=nr; } void set(longlong nr) { value=(double) nr; } void set(const char *str,uint length,CHARSET_INFO *cs) @@ -98,9 +97,7 @@ class Item_proc_int :public Item_proc public: Item_proc_int(THD *thd, const char *name_par): Item_proc(thd, name_par) { max_length=11; } - enum Item_result result_type () const { return INT_RESULT; } - enum Item_result cmp_type () const { return INT_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + const Type_handler *type_handler() const { return &type_handler_longlong; } void set(double nr) { value=(longlong) nr; } void set(longlong nr) { value=nr; } void set(const char *str,uint length, CHARSET_INFO *cs) @@ -118,9 +115,7 @@ class Item_proc_string :public Item_proc public: Item_proc_string(THD *thd, const char *name_par, uint length): Item_proc(thd, name_par) { this->max_length=length; } - enum Item_result result_type () const { return STRING_RESULT; } - enum Item_result cmp_type () const { return STRING_RESULT; } - enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } + const Type_handler *type_handler() const { return &type_handler_varchar; } void set(double nr) { str_value.set_real(nr, 2, default_charset()); } void set(longlong nr) { str_value.set(nr, default_charset()); } void set(const char *str, uint length, CHARSET_INFO *cs) diff --git a/sql/protocol.cc b/sql/protocol.cc index b9d9f28831e..33742dc01a2 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -791,8 +791,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) { List_iterator_fast<Item> it(*list); Item *item; - uchar buff[MAX_FIELD_WIDTH]; - String tmp((char*) buff,sizeof(buff),&my_charset_bin); + ValueBuffer<MAX_FIELD_WIDTH> tmp; Protocol_text prot(thd); String *local_packet= prot.storage_packet(); CHARSET_INFO *thd_charset= thd->variables.character_set_results; @@ -800,7 +799,9 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) if (flags & SEND_NUM_ROWS) { // Packet with number of elements + uchar buff[MAX_INT_WIDTH]; uchar *pos= net_store_length(buff, list->elements); + DBUG_ASSERT(pos <= buff + sizeof(buff)); if (my_net_write(&thd->net, buff, (size_t) (pos-buff))) DBUG_RETURN(1); } @@ -840,9 +841,9 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) cs, thd_charset) || prot.store(field.org_table_name, (uint) strlen(field.org_table_name), cs, thd_charset) || - prot.store(field.col_name, (uint) strlen(field.col_name), + prot.store(field.col_name.str, (uint) field.col_name.length, cs, thd_charset) || - prot.store(field.org_col_name, (uint) strlen(field.org_col_name), + prot.store(field.org_col_name.str, (uint) field.org_col_name.length, cs, thd_charset) || local_packet->realloc(local_packet->length()+12)) goto err; @@ -898,7 +899,7 @@ bool Protocol::send_result_set_metadata(List<Item> *list, uint flags) { if (prot.store(field.table_name, (uint) strlen(field.table_name), cs, thd_charset) || - prot.store(field.col_name, (uint) strlen(field.col_name), + prot.store(field.col_name.str, (uint) field.col_name.length, cs, thd_charset) || local_packet->realloc(local_packet->length()+10)) goto err; @@ -968,15 +969,20 @@ bool Protocol::write() bool Protocol::send_result_set_row(List<Item> *row_items) { - char buffer[MAX_FIELD_WIDTH]; - String str_buffer(buffer, sizeof (buffer), &my_charset_bin); List_iterator_fast<Item> it(*row_items); DBUG_ENTER("Protocol::send_result_set_row"); for (Item *item= it++; item; item= it++) { - if (item->send(this, &str_buffer)) + /* + ValueBuffer::m_string can be altered during Item::send(). + It's important to declare value_buffer inside the loop, + to have ValueBuffer::m_string point to ValueBuffer::buffer + on every iteration. + */ + ValueBuffer<MAX_FIELD_WIDTH> value_buffer; + if (item->send(this, &value_buffer)) { // If we're out of memory, reclaim some, to help us recover. this->free(); @@ -985,12 +991,6 @@ bool Protocol::send_result_set_row(List<Item> *row_items) /* Item::send() may generate an error. If so, abort the loop. */ if (thd->is_error()) DBUG_RETURN(TRUE); - - /* - Reset str_buffer to its original state, as it may have been altered in - Item::send(). - */ - str_buffer.set(buffer, sizeof(buffer), &my_charset_bin); } DBUG_RETURN(FALSE); @@ -1116,7 +1116,7 @@ bool Protocol_text::store(const char *from, size_t length, #ifndef DBUG_OFF DBUG_PRINT("info", ("Protocol_text::store field %u (%u): %.*s", field_pos, field_count, (int) length, (length == 0 ? "" : from))); - DBUG_ASSERT(field_pos < field_count); + DBUG_ASSERT(field_types == 0 || field_pos < field_count); DBUG_ASSERT(field_types == 0 || field_types[field_pos] == MYSQL_TYPE_DECIMAL || field_types[field_pos] == MYSQL_TYPE_BIT || diff --git a/sql/rpl_gtid.cc b/sql/rpl_gtid.cc index 30b8b62b607..c385434e41e 100644 --- a/sql/rpl_gtid.cc +++ b/sql/rpl_gtid.cc @@ -981,24 +981,24 @@ rpl_slave_state::domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid) Returns 0 on ok, non-zero on parse error. */ static int -gtid_parser_helper(char **ptr, char *end, rpl_gtid *out_gtid) +gtid_parser_helper(const char **ptr, const char *end, rpl_gtid *out_gtid) { char *q; - char *p= *ptr; + const char *p= *ptr; uint64 v1, v2, v3; int err= 0; - q= end; + q= (char*) end; v1= (uint64)my_strtoll10(p, &q, &err); if (err != 0 || v1 > (uint32)0xffffffff || q == end || *q != '-') return 1; p= q+1; - q= end; + q= (char*) end; v2= (uint64)my_strtoll10(p, &q, &err); if (err != 0 || v2 > (uint32)0xffffffff || q == end || *q != '-') return 1; p= q+1; - q= end; + q= (char*) end; v3= (uint64)my_strtoll10(p, &q, &err); if (err != 0) return 1; @@ -1014,8 +1014,8 @@ gtid_parser_helper(char **ptr, char *end, rpl_gtid *out_gtid) rpl_gtid * gtid_parse_string_to_list(const char *str, size_t str_len, uint32 *out_len) { - char *p= const_cast<char *>(str); - char *end= p + str_len; + const char *p= const_cast<char *>(str); + const char *end= p + str_len; uint32 len= 0, alloc_len= 5; rpl_gtid *list= NULL; @@ -1060,10 +1060,10 @@ gtid_parse_string_to_list(const char *str, size_t str_len, uint32 *out_len) Returns 0 if ok, non-zero if error. */ int -rpl_slave_state::load(THD *thd, char *state_from_master, size_t len, +rpl_slave_state::load(THD *thd, const char *state_from_master, size_t len, bool reset, bool in_statement) { - char *end= state_from_master + len; + const char *end= state_from_master + len; if (reset) { @@ -1500,7 +1500,7 @@ rpl_binlog_state::read_from_iocache(IO_CACHE *src) { /* 10-digit - 10-digit - 20-digit \n \0 */ char buf[10+1+10+1+20+1+1]; - char *p, *end; + const char *p, *end; rpl_gtid gtid; int res= 0; @@ -1763,9 +1763,9 @@ slave_connection_state::~slave_connection_state() */ int -slave_connection_state::load(char *slave_request, size_t len) +slave_connection_state::load(const char *slave_request, size_t len) { - char *p, *end; + const char *p, *end; uchar *rec; rpl_gtid *gtid; const entry *e; @@ -1779,7 +1779,7 @@ slave_connection_state::load(char *slave_request, size_t len) { if (!(rec= (uchar *)my_malloc(sizeof(entry), MYF(MY_WME)))) { - my_error(ER_OUTOFMEMORY, MYF(0), sizeof(*gtid)); + my_error(ER_OUTOFMEMORY, MYF(0), (int) sizeof(*gtid)); return 1; } gtid= &((entry *)rec)->gtid; @@ -2399,7 +2399,7 @@ gtid_waiting::get_entry(uint32 domain_id) if (!(e= (hash_element *)my_malloc(sizeof(*e), MYF(MY_WME)))) { - my_error(ER_OUTOFMEMORY, MYF(0), sizeof(*e)); + my_error(ER_OUTOFMEMORY, MYF(0), (int) sizeof(*e)); return NULL; } diff --git a/sql/rpl_gtid.h b/sql/rpl_gtid.h index f638a084e38..5dfac7a3c6f 100644 --- a/sql/rpl_gtid.h +++ b/sql/rpl_gtid.h @@ -181,7 +181,7 @@ struct rpl_slave_state bool sort); int tostring(String *dest, rpl_gtid *extra_gtids, uint32 num_extra); bool domain_to_gtid(uint32 domain_id, rpl_gtid *out_gtid); - int load(THD *thd, char *state_from_master, size_t len, bool reset, + int load(THD *thd, const char *state_from_master, size_t len, bool reset, bool in_statement); bool is_empty(); @@ -270,8 +270,12 @@ struct slave_connection_state rpl_gtid gtid; uint32 flags; }; - static const uint32 START_OWN_SLAVE_POS= 0x1; - static const uint32 START_ON_EMPTY_DOMAIN= 0x2; + /* Bits for 'flags' */ + enum start_flags + { + START_OWN_SLAVE_POS= 0x1, + START_ON_EMPTY_DOMAIN= 0x2 + }; /* Mapping from domain_id to the entry with GTID requested for that domain. */ HASH hash; @@ -283,7 +287,7 @@ struct slave_connection_state ~slave_connection_state(); void reset() { my_hash_reset(&hash); } - int load(char *slave_request, size_t len); + int load(const char *slave_request, size_t len); int load(const rpl_gtid *gtid_list, uint32 count); int load(rpl_slave_state *state, rpl_gtid *extra_gtids, uint32 num_extra); rpl_gtid *find(uint32 domain_id); diff --git a/sql/rpl_mi.cc b/sql/rpl_mi.cc index 3b0de3deb1c..96d9ee4a1b4 100644 --- a/sql/rpl_mi.cc +++ b/sql/rpl_mi.cc @@ -28,7 +28,7 @@ static void init_master_log_pos(Master_info* mi); -Master_info::Master_info(LEX_STRING *connection_name_arg, +Master_info::Master_info(LEX_CSTRING *connection_name_arg, bool is_slave_recovery) :Slave_reporting_capability("I/O"), ssl(0), ssl_verify_server_cert(1), fd(-1), io_thd(0), @@ -44,6 +44,7 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, in_start_all_slaves(0), in_stop_all_slaves(0), in_flush_all_relay_logs(0), users(0), killed(0) { + char *tmp; host[0] = 0; user[0] = 0; password[0] = 0; ssl_ca[0]= 0; ssl_capath[0]= 0; ssl_cert[0]= 0; ssl_cipher[0]= 0; ssl_key[0]= 0; @@ -55,16 +56,14 @@ Master_info::Master_info(LEX_STRING *connection_name_arg, */ connection_name.length= cmp_connection_name.length= connection_name_arg->length; - if ((connection_name.str= (char*) my_malloc(connection_name_arg->length*2+2, - MYF(MY_WME)))) + if ((connection_name.str= tmp= (char*) + my_malloc(connection_name_arg->length*2+2, MYF(MY_WME)))) { - cmp_connection_name.str= (connection_name.str + - connection_name_arg->length+1); - strmake(connection_name.str, connection_name_arg->str, - connection_name.length); - memcpy(cmp_connection_name.str, connection_name_arg->str, - connection_name.length+1); - my_casedn_str(system_charset_info, cmp_connection_name.str); + strmake(tmp, connection_name_arg->str, connection_name.length); + tmp+= connection_name_arg->length+1; + cmp_connection_name.str= tmp; + memcpy(tmp, connection_name_arg->str, connection_name.length+1); + my_casedn_str(system_charset_info, tmp); } /* When MySQL restarted, all Rpl_filter settings which aren't in the my.cnf @@ -124,7 +123,7 @@ Master_info::~Master_info() #endif rpl_filters.delete_element(connection_name.str, connection_name.length, (void (*)(const char*, uchar*)) free_rpl_filter); - my_free(connection_name.str); + my_free(const_cast<char*>(connection_name.str)); delete_dynamic(&ignore_server_ids); mysql_mutex_destroy(&run_lock); mysql_mutex_destroy(&data_lock); @@ -901,7 +900,7 @@ void free_key_master_info(Master_info *mi) 1 error */ -bool check_master_connection_name(LEX_STRING *name) +bool check_master_connection_name(LEX_CSTRING *name) { if (name->length >= MAX_CONNECTION_NAME) return 1; @@ -931,7 +930,7 @@ bool check_master_connection_name(LEX_STRING *name) void create_logfile_name_with_suffix(char *res_file_name, size_t length, const char *info_file, bool append, - LEX_STRING *suffix) + LEX_CSTRING *suffix) { char buff[MAX_CONNECTION_NAME+1], res[MAX_CONNECTION_NAME * MAX_FILENAME_MBWIDTH+1], *p; @@ -1124,7 +1123,7 @@ bool Master_info_index::init_all_master_info() while (!init_strvar_from_file(sign, sizeof(sign), &index_file, NULL)) { - LEX_STRING connection_name; + LEX_CSTRING connection_name; Master_info *mi; char buf_master_info_file[FN_REFLEN]; char buf_relay_log_info_file[FN_REFLEN]; @@ -1253,7 +1252,7 @@ error: /* Write new master.info to master.info.index File */ -bool Master_info_index::write_master_name_to_index_file(LEX_STRING *name, +bool Master_info_index::write_master_name_to_index_file(LEX_CSTRING *name, bool do_sync) { DBUG_ASSERT(my_b_inited(&index_file) != 0); @@ -1290,7 +1289,7 @@ bool Master_info_index::write_master_name_to_index_file(LEX_STRING *name, WARN_LEVEL_ERROR-> Issue error if not exists */ -Master_info *get_master_info(const LEX_STRING *connection_name, +Master_info *get_master_info(const LEX_CSTRING *connection_name, Sql_condition::enum_warning_level warning) { Master_info *mi; @@ -1356,7 +1355,7 @@ void Master_info::release() */ Master_info * -Master_info_index::get_master_info(const LEX_STRING *connection_name, +Master_info_index::get_master_info(const LEX_CSTRING *connection_name, Sql_condition::enum_warning_level warning) { Master_info *mi; @@ -1387,7 +1386,7 @@ Master_info_index::get_master_info(const LEX_STRING *connection_name, /* Check Master_host & Master_port is duplicated or not */ -bool Master_info_index::check_duplicate_master_info(LEX_STRING *name_arg, +bool Master_info_index::check_duplicate_master_info(LEX_CSTRING *name_arg, const char *host, uint port) { @@ -1917,7 +1916,7 @@ char *Domain_id_filter::as_string(enum_list_type type) { ulong domain_id; get_dynamic(ids, (void *) &domain_id, i); - cur_len+= my_snprintf(buf + cur_len, sz, " %u", domain_id); + cur_len+= my_snprintf(buf + cur_len, sz, " %lu", domain_id); sz-= cur_len; } return buf; diff --git a/sql/rpl_mi.h b/sql/rpl_mi.h index d0f6171815c..ccc1be6e5ce 100644 --- a/sql/rpl_mi.h +++ b/sql/rpl_mi.h @@ -172,7 +172,7 @@ class Master_info : public Slave_reporting_capability USE_GTID_NO= 0, USE_GTID_CURRENT_POS= 1, USE_GTID_SLAVE_POS= 2 }; - Master_info(LEX_STRING *connection_name, bool is_slave_recovery); + Master_info(LEX_CSTRING *connection_name, bool is_slave_recovery); ~Master_info(); bool shall_ignore_server_id(ulong s_id); void clear_in_memory_info(bool all); @@ -197,8 +197,8 @@ class Master_info : public Slave_reporting_capability char host[HOSTNAME_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1]; char user[USERNAME_LENGTH+1]; char password[MAX_PASSWORD_LENGTH*SYSTEM_CHARSET_MBMAXLEN+1]; - LEX_STRING connection_name; /* User supplied connection name */ - LEX_STRING cmp_connection_name; /* Connection name in lower case */ + LEX_CSTRING connection_name; /* User supplied connection name */ + LEX_CSTRING cmp_connection_name; /* Connection name in lower case */ bool ssl; // enables use of SSL connection if true char ssl_ca[FN_REFLEN], ssl_capath[FN_REFLEN], ssl_cert[FN_REFLEN]; char ssl_cipher[FN_REFLEN], ssl_key[FN_REFLEN]; @@ -343,14 +343,14 @@ public: HASH master_info_hash; bool init_all_master_info(); - bool write_master_name_to_index_file(LEX_STRING *connection_name, + bool write_master_name_to_index_file(LEX_CSTRING *connection_name, bool do_sync); - bool check_duplicate_master_info(LEX_STRING *connection_name, + bool check_duplicate_master_info(LEX_CSTRING *connection_name, const char *host, uint port); bool add_master_info(Master_info *mi, bool write_to_file); bool remove_master_info(Master_info *mi); - Master_info *get_master_info(const LEX_STRING *connection_name, + Master_info *get_master_info(const LEX_CSTRING *connection_name, Sql_condition::enum_warning_level warning); bool start_all_slaves(THD *thd); bool stop_all_slaves(THD *thd); @@ -368,13 +368,13 @@ public: }; -Master_info *get_master_info(const LEX_STRING *connection_name, +Master_info *get_master_info(const LEX_CSTRING *connection_name, Sql_condition::enum_warning_level warning); -bool check_master_connection_name(LEX_STRING *name); +bool check_master_connection_name(LEX_CSTRING *name); void create_logfile_name_with_suffix(char *res_file_name, size_t length, const char *info_file, bool append, - LEX_STRING *suffix); + LEX_CSTRING *suffix); uchar *get_key_master_info(Master_info *mi, size_t *length, my_bool not_used __attribute__((unused))); diff --git a/sql/rpl_parallel.h b/sql/rpl_parallel.h index a0faeae815c..4579d0da9bc 100644 --- a/sql/rpl_parallel.h +++ b/sql/rpl_parallel.h @@ -68,23 +68,27 @@ struct group_commit_orderer { */ bool installed; - /* - This flag is set for a GCO in which we have event groups with multiple - different commit_id values from the master. This happens when we - optimistically try to execute in parallel transactions not known to be - conflict-free. - - When this flag is set, in case of DDL we need to start a new GCO regardless - of current commit_id, as DDL is not safe to speculatively apply in parallel - with prior event groups. - */ - static const uint8 MULTI_BATCH = 0x01; - /* - This flag is set for a GCO that contains DDL. If set, it forces a switch to - a new GCO upon seeing a new commit_id, as DDL is not safe to speculatively - replicate in parallel with subsequent transactions. - */ - static const uint8 FORCE_SWITCH = 0x02; + enum force_switch_bits + { + /* + This flag is set for a GCO in which we have event groups with multiple + different commit_id values from the master. This happens when we + optimistically try to execute in parallel transactions not known to be + conflict-free. + + When this flag is set, in case of DDL we need to start a new GCO + regardless of current commit_id, as DDL is not safe to + speculatively apply in parallel with prior event groups. + */ + MULTI_BATCH= 1, + /* + This flag is set for a GCO that contains DDL. If set, it forces + a switch to a new GCO upon seeing a new commit_id, as DDL is not + safe to speculatively replicate in parallel with subsequent + transactions. + */ + FORCE_SWITCH= 2 + }; uint8 flags; }; diff --git a/sql/rpl_record.cc b/sql/rpl_record.cc index 3147baf7d73..51e93003c56 100644 --- a/sql/rpl_record.cc +++ b/sql/rpl_record.cc @@ -107,7 +107,7 @@ pack_row(TABLE *table, MY_BITMAP const* cols, field->max_data_length()); DBUG_PRINT("debug", ("field: %s; real_type: %d, pack_ptr: %p;" " pack_ptr':%p; bytes: %d", - field->field_name, field->real_type(), + field->field_name.str, field->real_type(), old_pack_ptr,pack_ptr, (int) (pack_ptr - old_pack_ptr))); DBUG_DUMP("packed_data", old_pack_ptr, pack_ptr - old_pack_ptr); @@ -254,7 +254,7 @@ unpack_row(rpl_group_info *rgi, conv_field ? conv_field : *field_ptr; DBUG_PRINT("debug", ("Conversion %srequired for field '%s' (#%ld)", conv_field ? "" : "not ", - (*field_ptr)->field_name, + (*field_ptr)->field_name.str, (long) (field_ptr - begin_ptr))); DBUG_ASSERT(f != NULL); @@ -305,7 +305,7 @@ unpack_row(rpl_group_info *rgi, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_NULL_ERROR, ER_THD(thd, ER_BAD_NULL_ERROR), - f->field_name); + f->field_name.str); } } else @@ -323,7 +323,7 @@ unpack_row(rpl_group_info *rgi, pack_ptr= f->unpack(f->ptr, pack_ptr, row_end, metadata); DBUG_PRINT("debug", ("field: %s; metadata: 0x%x;" " pack_ptr: %p; pack_ptr': %p; bytes: %d", - f->field_name, metadata, + f->field_name.str, metadata, old_pack_ptr, pack_ptr, (int) (pack_ptr - old_pack_ptr))); if (!pack_ptr) @@ -338,7 +338,7 @@ unpack_row(rpl_group_info *rgi, WSREP_WARN("ROW event unpack field: %s metadata: 0x%x;" " pack_ptr: %p; conv_table %p conv_field %p table %s" " row_end: %p", - f->field_name, metadata, + f->field_name.str, metadata, old_pack_ptr, conv_table, conv_field, (table_found) ? "found" : "not found", row_end ); @@ -347,7 +347,7 @@ unpack_row(rpl_group_info *rgi, rgi->rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT, rgi->gtid_info(), "Could not read field '%s' of table '%s.%s'", - f->field_name, table->s->db.str, + f->field_name.str, table->s->db.str, table->s->table_name.str); DBUG_RETURN(HA_ERR_CORRUPT_EVENT); } @@ -370,7 +370,7 @@ unpack_row(rpl_group_info *rgi, conv_field->sql_type(source_type); conv_field->val_str(&value_string); DBUG_PRINT("debug", ("Copying field '%s' of type '%s' with value '%s'", - (*field_ptr)->field_name, + (*field_ptr)->field_name.str, source_type.c_ptr_safe(), value_string.c_ptr_safe())); #endif copy.set(*field_ptr, f, TRUE); @@ -381,7 +381,7 @@ unpack_row(rpl_group_info *rgi, (*field_ptr)->sql_type(target_type); (*field_ptr)->val_str(&value_string); DBUG_PRINT("debug", ("Value of field '%s' of type '%s' is now '%s'", - (*field_ptr)->field_name, + (*field_ptr)->field_name.str, target_type.c_ptr_safe(), value_string.c_ptr_safe())); #endif } @@ -489,7 +489,7 @@ int prepare_record(TABLE *const table, const uint skip, const bool check) Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD, ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD), - f->field_name); + f->field_name.str); } } diff --git a/sql/rpl_record_old.cc b/sql/rpl_record_old.cc index 5b876373b9c..a252bbff0f5 100644 --- a/sql/rpl_record_old.cc +++ b/sql/rpl_record_old.cc @@ -143,7 +143,7 @@ unpack_row_old(rpl_group_info *rgi, { rgi->rli->report(ERROR_LEVEL, ER_SLAVE_CORRUPT_EVENT, NULL, "Could not read field `%s` of table `%s`.`%s`", - f->field_name, table->s->db.str, + f->field_name.str, table->s->db.str, table->s->table_name.str); return(ER_SLAVE_CORRUPT_EVENT); } @@ -186,7 +186,7 @@ unpack_row_old(rpl_group_info *rgi, rgi->rli->report(ERROR_LEVEL, ER_NO_DEFAULT_FOR_FIELD, NULL, "Field `%s` of table `%s`.`%s` " "has no default value and cannot be NULL", - (*field_ptr)->field_name, table->s->db.str, + (*field_ptr)->field_name.str, table->s->db.str, table->s->table_name.str); error = ER_NO_DEFAULT_FOR_FIELD; } diff --git a/sql/rpl_utility.cc b/sql/rpl_utility.cc index bdf5b7dea80..9a984c5c953 100644 --- a/sql/rpl_utility.cc +++ b/sql/rpl_utility.cc @@ -825,7 +825,7 @@ table_def::compatible_with(THD *thd, rpl_group_info *rgi, { DBUG_PRINT("debug", ("Checking column %d -" " field '%s' can be converted - order: %d", - col, field->field_name, order)); + col, field->field_name.str, order)); DBUG_ASSERT(order >= -1 && order <= 1); /* @@ -855,7 +855,7 @@ table_def::compatible_with(THD *thd, rpl_group_info *rgi, { DBUG_PRINT("debug", ("Checking column %d -" " field '%s' can not be converted", - col, field->field_name)); + col, field->field_name.str)); DBUG_ASSERT(col < size() && col < table->s->fields); DBUG_ASSERT(table->s->db.str && table->s->table_name.str); DBUG_ASSERT(table->in_use); @@ -891,7 +891,7 @@ table_def::compatible_with(THD *thd, rpl_group_info *rgi, table->field[col]->sql_type(target_type); DBUG_PRINT("debug", ("Field %s - conversion required." " Source type: '%s', Target type: '%s'", - tmp_table->field[col]->field_name, + tmp_table->field[col]->field_name.str, source_type.c_ptr_safe(), target_type.c_ptr_safe())); } } @@ -928,7 +928,7 @@ public: (int) sql_type, target_field->table->s->db.str, target_field->table->s->table_name.str, - target_field->field_name); + target_field->field_name.str); return true; } Field *tmp= handler->make_conversion_table_field(this, metadata, @@ -938,7 +938,7 @@ public: Virtual_tmp_table::add(tmp); DBUG_PRINT("debug", ("sql_type: %d, target_field: '%s', max_length: %d, decimals: %d," " maybe_null: %d, unsigned_flag: %d, pack_length: %u", - sql_type, target_field->field_name, + sql_type, target_field->field_name.str, tmp->field_length, tmp->decimals(), TRUE, tmp->flags, tmp->pack_length())); return false; @@ -980,7 +980,7 @@ TABLE *table_def::create_conversion_table(THD *thd, rpl_group_info *rgi, DBUG_PRINT("debug", ("binlog_type: %d, metadata: %04X, target_field: '%s'" " make_conversion_table_field() failed", binlog_type(col), field_metadata(col), - target_table->field[col]->field_name)); + target_table->field[col]->field_name.str)); goto err; } } diff --git a/sql/session_tracker.cc b/sql/session_tracker.cc index 4ca94b6cd60..4b449f3991a 100644 --- a/sql/session_tracker.cc +++ b/sql/session_tracker.cc @@ -396,7 +396,7 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, return false; } - if(!strcmp(var_list.str,(const char *)"*")) + if(!strcmp(var_list.str, "*")) { track_all= true; buffer_length= 2; @@ -418,7 +418,7 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, for (;;) { sys_var *svar; - LEX_STRING var; + LEX_CSTRING var; uint not_used; lasts= (char *) memchr(token, separator, rest); @@ -435,7 +435,7 @@ bool Session_sysvars_tracker::vars_list::parse_var_list(THD *thd, /* Remove leading/trailing whitespace. */ trim_whitespace(char_set, &var, ¬_used); - if(!strcmp(var.str,(const char *)"*")) + if(!strcmp(var.str, "*")) { track_all= true; } @@ -483,7 +483,7 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, size_t rest= var_list.length; if (!var_list.str || var_list.length == 0 || - !strcmp(var_list.str,(const char *)"*")) + !strcmp(var_list.str, "*")) { return false; } @@ -500,7 +500,7 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, mysql_mutex_lock(&LOCK_plugin); for (;;) { - LEX_STRING var; + LEX_CSTRING var; uint not_used; lasts= (char *) memchr(token, separator, rest); @@ -517,7 +517,7 @@ bool Session_sysvars_tracker::check_var_list(THD *thd, /* Remove leading/trailing whitespace. */ trim_whitespace(char_set, &var, ¬_used); - if(!strcmp(var.str,(const char *)"*") && + if(!strcmp(var.str, "*") && !find_sys_var_ex(thd, var.str, var.length, throw_error, true)) { if (throw_error && take_mutex && thd) diff --git a/sql/set_var.cc b/sql/set_var.cc index 07395e3e708..15f6bbdafc5 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -233,12 +233,12 @@ bool sys_var::update(THD *thd, set_var *var) } } -uchar *sys_var::session_value_ptr(THD *thd, const LEX_STRING *base) +uchar *sys_var::session_value_ptr(THD *thd, const LEX_CSTRING *base) { return session_var_ptr(thd); } -uchar *sys_var::global_value_ptr(THD *thd, const LEX_STRING *base) +uchar *sys_var::global_value_ptr(THD *thd, const LEX_CSTRING *base) { return global_var_ptr(); } @@ -271,7 +271,8 @@ bool sys_var::check(THD *thd, set_var *var) return false; } -uchar *sys_var::value_ptr(THD *thd, enum_var_type type, const LEX_STRING *base) +uchar *sys_var::value_ptr(THD *thd, enum_var_type type, + const LEX_CSTRING *base) { DBUG_ASSERT(base); if (type == OPT_GLOBAL || scope() == GLOBAL) @@ -327,7 +328,8 @@ do { \ break longlong sys_var::val_int(bool *is_null, - THD *thd, enum_var_type type, const LEX_STRING *base) + THD *thd, enum_var_type type, + const LEX_CSTRING *base) { LEX_STRING sval; AutoWLock lock(&PLock_global_system_variables); @@ -382,7 +384,7 @@ String *sys_var::val_str_nolock(String *str, THD *thd, const uchar *value) String *sys_var::val_str(String *str, - THD *thd, enum_var_type type, const LEX_STRING *base) + THD *thd, enum_var_type type, const LEX_CSTRING *base) { AutoWLock lock(&PLock_global_system_variables); const uchar *value= value_ptr(thd, type, base); @@ -391,7 +393,7 @@ String *sys_var::val_str(String *str, double sys_var::val_real(bool *is_null, - THD *thd, enum_var_type type, const LEX_STRING *base) + THD *thd, enum_var_type type, const LEX_CSTRING *base) { LEX_STRING sval; AutoWLock lock(&PLock_global_system_variables); @@ -686,6 +688,17 @@ sys_var *intern_find_sys_var(const char *str, uint length) } +bool find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp) +{ + tmp->var= find_sys_var(thd, tmp->base_name.str, tmp->base_name.length); + + if (tmp->var != NULL) + tmp->base_name= null_clex_str; + + return thd->is_error(); +} + + /** Execute update of all variables. @@ -827,7 +840,7 @@ int set_var::update(THD *thd) set_var::set_var(THD *thd, enum_var_type type_arg, sys_var *var_arg, - const LEX_STRING *base_name_arg, Item *value_arg) + const LEX_CSTRING *base_name_arg, Item *value_arg) :var(var_arg), type(type_arg), base(*base_name_arg) { /* @@ -838,7 +851,9 @@ set_var::set_var(THD *thd, enum_var_type type_arg, sys_var *var_arg, { Item_field *item= (Item_field*) value_arg; // names are utf8 - if (!(value= new (thd->mem_root) Item_string_sys(thd, item->field_name))) + if (!(value= new (thd->mem_root) Item_string_sys(thd, + item->field_name.str, + item->field_name.length))) value=value_arg; /* Give error message later */ } else @@ -1050,7 +1065,7 @@ static void store_var(Field *field, sys_var *var, enum_var_type scope, return; store_value_ptr(field, var, str, - var->value_ptr(field->table->in_use, scope, &null_lex_str)); + var->value_ptr(field->table->in_use, scope, &null_clex_str)); } int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond) diff --git a/sql/set_var.h b/sql/set_var.h index 97dc3b5ba51..17d1ff93ebc 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -112,7 +112,7 @@ public: virtual sys_var_pluginvar *cast_pluginvar() { return 0; } bool check(THD *thd, set_var *var); - uchar *value_ptr(THD *thd, enum_var_type type, const LEX_STRING *base); + uchar *value_ptr(THD *thd, enum_var_type type, const LEX_CSTRING *base); /** Update the system variable with the default value from either @@ -123,9 +123,9 @@ public: bool update(THD *thd, set_var *var); String *val_str_nolock(String *str, THD *thd, const uchar *value); - longlong val_int(bool *is_null, THD *thd, enum_var_type type, const LEX_STRING *base); - String *val_str(String *str, THD *thd, enum_var_type type, const LEX_STRING *base); - double val_real(bool *is_null, THD *thd, enum_var_type type, const LEX_STRING *base); + longlong val_int(bool *is_null, THD *thd, enum_var_type type, const LEX_CSTRING *base); + String *val_str(String *str, THD *thd, enum_var_type type, const LEX_CSTRING *base); + double val_real(bool *is_null, THD *thd, enum_var_type type, const LEX_CSTRING *base); SHOW_TYPE show_type() { return show_val_type; } int scope() const { return flags & SCOPE_MASK; } @@ -229,8 +229,8 @@ protected: It must be of show_val_type type (my_bool for SHOW_MY_BOOL, int for SHOW_INT, longlong for SHOW_LONGLONG, etc). */ - virtual uchar *session_value_ptr(THD *thd, const LEX_STRING *base); - virtual uchar *global_value_ptr(THD *thd, const LEX_STRING *base); + virtual uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base); + virtual uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base); /** A pointer to a storage area of the variable, to the raw data. @@ -290,10 +290,10 @@ public: LEX_STRING string_value; ///< for Sys_var_charptr and others const void *ptr; ///< for Sys_var_struct } save_result; - LEX_STRING base; /**< for structured variables, like keycache_name.variable_name */ + LEX_CSTRING base; /**< for structured variables, like keycache_name.variable_name */ set_var(THD *thd, enum_var_type type_arg, sys_var *var_arg, - const LEX_STRING *base_name_arg, Item *value_arg); + const LEX_CSTRING *base_name_arg, Item *value_arg); virtual bool is_system() { return 1; } int check(THD *thd); int update(THD *thd); @@ -330,10 +330,10 @@ public: class set_var_role: public set_var_base { - LEX_STRING role; + LEX_CSTRING role; ulonglong access; public: - set_var_role(LEX_STRING role_arg) : role(role_arg) {} + set_var_role(LEX_CSTRING role_arg) : role(role_arg) {} int check(THD *thd); int update(THD *thd); }; @@ -343,9 +343,9 @@ public: class set_var_default_role: public set_var_base { LEX_USER *user, *real_user; - LEX_STRING role; + LEX_CSTRING role; public: - set_var_default_role(LEX_USER *user_arg, LEX_STRING role_arg) : + set_var_default_role(LEX_USER *user_arg, LEX_CSTRING role_arg) : user(user_arg), role(role_arg) {} int check(THD *thd); int update(THD *thd); @@ -391,6 +391,7 @@ SHOW_VAR* enumerate_sys_vars(THD *thd, bool sorted, enum enum_var_type type); int fill_sysvars(THD *thd, TABLE_LIST *tables, COND *cond); sys_var *find_sys_var(THD *thd, const char *str, size_t length=0); +bool find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp); int sql_set_variables(THD *thd, List<set_var_base> *var_list, bool free); #define SYSVAR_AUTOSIZE(VAR,VAL) \ @@ -411,7 +412,8 @@ inline bool IS_SYSVAR_AUTOSIZE(void *ptr) bool fix_delay_key_write(sys_var *self, THD *thd, enum_var_type type); sql_mode_t expand_sql_mode(sql_mode_t sql_mode); -bool sql_mode_string_representation(THD *thd, sql_mode_t sql_mode, LEX_STRING *ls); +bool sql_mode_string_representation(THD *thd, sql_mode_t sql_mode, + LEX_CSTRING *ls); int default_regex_flags_pcre(const THD *thd); extern sys_var *Sys_autocommit_ptr; diff --git a/sql/share/errmsg-utf8.txt b/sql/share/errmsg-utf8.txt index 49b2c8b76ef..a6462cbca9b 100644 --- a/sql/share/errmsg-utf8.txt +++ b/sql/share/errmsg-utf8.txt @@ -5370,7 +5370,7 @@ ER_FRM_UNKNOWN_TYPE rus "Файл '%-.192s' содержит неизвестный тип '%-.64s' в заголовке" ukr "Файл '%-.192s' має невідомий тип '%-.64s' у заголовку" ER_WRONG_OBJECT - eng "'%-.192s.%-.192s' is not %s" + eng "'%-.192s.%-.192s' is not of type '%s'" ger "'%-.192s.%-.192s' ist nicht %s" rus "'%-.192s.%-.192s' - не %s" ukr "'%-.192s.%-.192s' не є %s" @@ -5425,9 +5425,9 @@ ER_SP_GOTO_IN_HNDLR eng "GOTO is not allowed in a stored procedure handler" ger "GOTO ist im Handler einer gespeicherten Prozedur nicht erlaubt" ER_TRG_ALREADY_EXISTS - eng "Trigger already exists" - ger "Trigger existiert bereits" - hindi "TRIGGER पहले से मौजूद है" + eng "Trigger '%s' already exists" + ger "Trigger '%s' existiert bereits" + hindi "TRIGGER '%s' पहले से मौजूद है" ER_TRG_DOES_NOT_EXIST eng "Trigger does not exist" ger "Trigger existiert nicht" @@ -6667,7 +6667,7 @@ ER_CANNOT_LOAD_FROM_TABLE_V2 ger "Kann %s.%s nicht einlesen. Tabelle ist wahrscheinlich beschädigt" ER_MASTER_DELAY_VALUE_OUT_OF_RANGE - eng "The requested value %u for the master delay exceeds the maximum %u" + eng "The requested value %lu for the master delay exceeds the maximum %lu" ER_ONLY_FD_AND_RBR_EVENTS_ALLOWED_IN_BINLOG_STATEMENT eng "Only Format_description_log_event and row events are allowed in BINLOG statements (but %s was provided)" @@ -7570,7 +7570,7 @@ ER_WITH_COL_WRONG_LIST ER_TOO_MANY_DEFINITIONS_IN_WITH_CLAUSE eng "Too many WITH elements in WITH clause" ER_DUP_QUERY_NAME - eng "Duplicate query name in WITH clause" + eng "Duplicate query name %`-.64s in WITH clause" ER_RECURSIVE_WITHOUT_ANCHORS eng "No anchors for recursive WITH element '%s'" ER_UNACCEPTABLE_MUTUAL_RECURSION @@ -7742,3 +7742,39 @@ ER_PER_INDEX_CF_DEPRECATED ER_SUM_FUNC_WITH_WINDOW_FUNC_AS_ARG eng "Window functions can not be used as arguments to group functions." + +ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION + eng "Illegal parameter data types %s and %s for operation '%s'" +ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION + eng "Illegal parameter data type %s for operation '%s'" +ER_WRONG_PARAMCOUNT_TO_CURSOR 42000 + eng "Incorrect parameter count to cursor '%-.192s'" + rus "Некорректное количество параметров для курсора '%-.192s'" +ER_UNKNOWN_STRUCTURED_VARIABLE + eng "Unknown structured system variable or ROW routine variable '%-.*s'" +ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD + eng "Row variable '%-.192s' does not have a field '%-.192s'" +ER_END_IDENTIFIER_DOES_NOT_MATCH + eng "END identifier '%-.192s' does not match '%-.192s'" +ER_SEQUENCE_RUN_OUT + eng "Sequence '%-.64s.%-.64s' has run out" +ER_SEQUENCE_INVALID_DATA + eng "Sequence '%-.64s.%-.64s' values are conflicting" +ER_SEQUENCE_INVALID_TABLE_STRUCTURE + eng "Sequence '%-.64s.%-.64s' table structure is invalid (%s)" +ER_SEQUENCE_ACCESS_ERROR + eng "Sequence '%-.64s.%-.64s' access error" +ER_SEQUENCE_BINLOG_FORMAT + eng "Sequences requires binlog_format mixed or row" +ER_NOT_SEQUENCE 42S02 + eng "'%-.64s.%-.64s' is not a SEQUENCE" +ER_NOT_SEQUENCE2 42S02 + eng "'%-.192s' is not a SEQUENCE" +ER_UNKNOWN_SEQUENCES 42S02 + eng "Unknown SEQUENCE: '%-.300s'" +ER_UNKNOWN_VIEW 42S02 + eng "Unknown VIEW: '%-.300s'" +ER_WRONG_INSERT_INTO_SEQUENCE + eng "Wrong INSERT into a SEQUENCE. One can only do single table INSERT into a squence object (like with mysqldump). If you want to change the SEQUENCE, use ALTER SEQUENCE instead." +ER_SP_STACK_TRACE + eng "At line %u in %s" diff --git a/sql/slave.cc b/sql/slave.cc index 1b1a597fa2a..86675b4f19c 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -82,7 +82,7 @@ ulonglong opt_read_binlog_speed_limit = 0; const char *relay_log_index= 0; const char *relay_log_basename= 0; -LEX_STRING default_master_connection_name= { (char*) "", 0 }; +LEX_CSTRING default_master_connection_name= { (char*) "", 0 }; /* When slave thread exits, we need to remember the temporary tables so we @@ -3729,10 +3729,7 @@ int apply_event_and_update_pos_for_parallel(Log_event* ev, THD* thd, rpl_group_info *rgi) { -#ifndef DBUG_OFF - Relay_log_info* rli= rgi->rli; -#endif - mysql_mutex_assert_not_owner(&rli->data_lock); + mysql_mutex_assert_not_owner(&rgi->rli->data_lock); int reason= apply_event_and_update_pos_setup(ev, thd, rgi); /* In parallel replication, sql_slave_skip_counter is handled in the SQL diff --git a/sql/slave.h b/sql/slave.h index ded9d76e49d..431e6847abe 100644 --- a/sql/slave.h +++ b/sql/slave.h @@ -273,7 +273,7 @@ extern bool volatile abort_loop; extern Master_info *active_mi; /* active_mi for multi-master */ extern Master_info *default_master_info; /* To replace active_mi */ extern Master_info_index *master_info_index; -extern LEX_STRING default_master_connection_name; +extern LEX_CSTRING default_master_connection_name; extern my_bool replicate_same_server_id; extern int disconnect_slave_event_count, abort_slave_event_count ; diff --git a/sql/sp.cc b/sql/sp.cc index fcbf4dc43d4..c8b9e02076b 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -20,6 +20,7 @@ #include "unireg.h" #include "sp.h" #include "sql_base.h" // close_thread_tables +#include "sql_lex.h" // empty_clex_str #include "sql_parse.h" // parse_sql #include "key.h" // key_copy #include "sql_show.h" // append_definer, append_identifier @@ -34,18 +35,59 @@ #include <my_user.h> -/* Used in error handling only */ -#define SP_TYPE_STRING(type) \ - (type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE") - -static int -db_load_routine(THD *thd, stored_procedure_type type, sp_name *name, - sp_head **sphp, - sql_mode_t sql_mode, const char *params, const char *returns, - const char *body, st_sp_chistics &chistics, - LEX_STRING *definer_user_name, LEX_STRING *definer_host_name, - longlong created, longlong modified, - Stored_program_creation_ctx *creation_ctx); + +sp_cache **Sp_handler_procedure::get_cache(THD *thd) const +{ + return &thd->sp_proc_cache; +} + +sp_cache **Sp_handler_function::get_cache(THD *thd) const +{ + return &thd->sp_func_cache; +} + +ulong Sp_handler_procedure::recursion_depth(THD *thd) const +{ + return thd->variables.max_sp_recursion_depth; +} + + +bool Sp_handler::add_instr_freturn(THD *thd, sp_head *sp, + sp_pcontext *spcont, + Item *item, LEX *lex) const +{ + my_error(ER_SP_BADRETURN, MYF(0)); + return true; +} + + +bool Sp_handler::add_instr_preturn(THD *thd, sp_head *sp, + sp_pcontext *spcont) const +{ + thd->parse_error(); + return true; +} + + +bool Sp_handler_function::add_instr_freturn(THD *thd, sp_head *sp, + sp_pcontext *spcont, + Item *item, LEX *lex) const +{ + return sp->add_instr_freturn(thd, spcont, item, lex); +} + + +bool Sp_handler_procedure::add_instr_preturn(THD *thd, sp_head *sp, + sp_pcontext *spcont) const +{ + return sp->add_instr_preturn(thd, spcont); +} + + +Sp_handler_procedure sp_handler_procedure; +Sp_handler_function sp_handler_function; +Sp_handler_trigger sp_handler_trigger; + static const TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] = @@ -176,7 +218,7 @@ class Stored_routine_creation_ctx : public Stored_program_creation_ctx, { public: static Stored_routine_creation_ctx * - load_from_db(THD *thd, const sp_name *name, TABLE *proc_tbl); + load_from_db(THD *thd, const Database_qualified_name *name, TABLE *proc_tbl); public: virtual Stored_program_creation_ctx *clone(MEM_ROOT *mem_root) @@ -214,15 +256,16 @@ bool load_charset(MEM_ROOT *mem_root, CHARSET_INFO *dflt_cs, CHARSET_INFO **cs) { - String cs_name; + LEX_CSTRING cs_name; - if (get_field(mem_root, field, &cs_name)) + if (field->val_str_nopad(mem_root, &cs_name)) { *cs= dflt_cs; return TRUE; } - *cs= get_charset_by_csname(cs_name.c_ptr(), MY_CS_PRIMARY, MYF(0)); + DBUG_ASSERT(cs_name.str[cs_name.length] == 0); + *cs= get_charset_by_csname(cs_name.str, MY_CS_PRIMARY, MYF(0)); if (*cs == NULL) { @@ -240,15 +283,16 @@ bool load_collation(MEM_ROOT *mem_root, CHARSET_INFO *dflt_cl, CHARSET_INFO **cl) { - String cl_name; + LEX_CSTRING cl_name; - if (get_field(mem_root, field, &cl_name)) + if (field->val_str_nopad(mem_root, &cl_name)) { *cl= dflt_cl; return TRUE; } - *cl= get_charset_by_name(cl_name.c_ptr(), MYF(0)); + DBUG_ASSERT(cl_name.str[cl_name.length] == 0); + *cl= get_charset_by_name(cl_name.str, MYF(0)); if (*cl == NULL) { @@ -263,8 +307,8 @@ bool load_collation(MEM_ROOT *mem_root, Stored_routine_creation_ctx * Stored_routine_creation_ctx::load_from_db(THD *thd, - const sp_name *name, - TABLE *proc_tbl) + const Database_qualified_name *name, + TABLE *proc_tbl) { /* Load character set/collation attributes. */ @@ -459,7 +503,6 @@ static TABLE *open_proc_table_for_update(THD *thd) Find row in open mysql.proc table representing stored routine. @param thd Thread context - @param type Type of routine to find (function or procedure) @param name Name of routine @param table TABLE object for open mysql.proc table. @@ -469,14 +512,16 @@ static TABLE *open_proc_table_for_update(THD *thd) SP_KEY_NOT_FOUND No routine with given name */ -static int -db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name, - TABLE *table) +int +Sp_handler::db_find_routine_aux(THD *thd, + const Database_qualified_name *name, + TABLE *table) const { uchar key[MAX_KEY_LENGTH]; // db, name, optional key length type DBUG_ENTER("db_find_routine_aux"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), + (int) name->m_name.length, name->m_name.str)); /* Create key to find row. We have to use field->store() to be able to @@ -487,10 +532,9 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name, */ if (name->m_name.length > table->field[1]->field_length) DBUG_RETURN(SP_KEY_NOT_FOUND); - table->field[0]->store(name->m_db.str, name->m_db.length, &my_charset_bin); - table->field[1]->store(name->m_name.str, name->m_name.length, - &my_charset_bin); - table->field[2]->store((longlong) type, TRUE); + table->field[0]->store(name->m_db, &my_charset_bin); + table->field[1]->store(name->m_name, &my_charset_bin); + table->field[2]->store((longlong) type(), true); key_copy(key, table->record[0], table->key_info, table->key_info->key_length); @@ -503,12 +547,67 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name, } +bool st_sp_chistics::read_from_mysql_proc_row(THD *thd, TABLE *table) +{ + LEX_CSTRING str; + + if (table->field[MYSQL_PROC_FIELD_ACCESS]->val_str_nopad(thd->mem_root, + &str)) + return true; + + switch (str.str[0]) { + case 'N': + daccess= SP_NO_SQL; + break; + case 'C': + daccess= SP_CONTAINS_SQL; + break; + case 'R': + daccess= SP_READS_SQL_DATA; + break; + case 'M': + daccess= SP_MODIFIES_SQL_DATA; + break; + default: + daccess= SP_DEFAULT_ACCESS_MAPPING; + } + + if (table->field[MYSQL_PROC_FIELD_DETERMINISTIC]->val_str_nopad(thd->mem_root, + &str)) + return true; + detistic= str.str[0] == 'N' ? false : true; + + if (table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]->val_str_nopad(thd->mem_root, + &str)) + return true; + suid= str.str[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID; + + if (table->field[MYSQL_PROC_FIELD_COMMENT]->val_str_nopad(thd->mem_root, + &comment)) + return true; + + return false; +} + + +bool AUTHID::read_from_mysql_proc_row(THD *thd, TABLE *table) +{ + LEX_CSTRING str; + if (table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, + &str)) + return true; + parse(str.str, str.length); + if (user.str[user.length]) + ((char *) user.str)[user.length]= '\0'; // 0-terminate if was truncated + return false; +} + + /** Find routine definition in mysql.proc table and create corresponding sp_head object for it. @param thd Thread context - @param type Type of routine (TYPE_ENUM_PROCEDURE/...) @param name Name of routine @param sphp Out parameter in which pointer to created sp_head object is returned (0 in case of error). @@ -523,33 +622,27 @@ db_find_routine_aux(THD *thd, stored_procedure_type type, sp_name *name, non-0 Error (may be one of special codes like SP_KEY_NOT_FOUND) */ -static int -db_find_routine(THD *thd, stored_procedure_type type, sp_name *name, - sp_head **sphp) +int +Sp_handler::db_find_routine(THD *thd, + const Database_qualified_name *name, + sp_head **sphp) const { TABLE *table; - const char *params, *returns, *body; + LEX_CSTRING params, returns, body; int ret; - const char *definer; longlong created; longlong modified; - st_sp_chistics chistics; - char *ptr; - uint length; - char buff[65]; - String str(buff, sizeof(buff), &my_charset_bin); + Sp_chistics chistics; bool saved_time_zone_used= thd->time_zone_used; sql_mode_t sql_mode, saved_mode= thd->variables.sql_mode; Open_tables_backup open_tables_state_backup; Stored_program_creation_ctx *creation_ctx; - char definer_user_name_holder[USERNAME_LENGTH + 1]; - LEX_STRING definer_user_name= { definer_user_name_holder, USERNAME_LENGTH }; - char definer_host_name_holder[HOSTNAME_LENGTH + 1]; - LEX_STRING definer_host_name= { definer_host_name_holder, HOSTNAME_LENGTH }; + AUTHID definer; DBUG_ENTER("db_find_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), + (int) name->m_name.length, name->m_name.str)); *sphp= 0; // In case of errors if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup))) @@ -558,7 +651,7 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name, /* Reset sql_mode during data dictionary operations. */ thd->variables.sql_mode= 0; - if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK) + if ((ret= db_find_routine_aux(thd, name, table)) != SP_OK) goto done; if (table->s->fields < MYSQL_PROC_FIELD_COUNT) @@ -567,106 +660,43 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name, goto done; } - bzero((char *)&chistics, sizeof(chistics)); - if ((ptr= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_ACCESS])) == NULL) + if (chistics.read_from_mysql_proc_row(thd, table) || + definer.read_from_mysql_proc_row(thd, table)) { ret= SP_GET_FIELD_FAILED; goto done; } - switch (ptr[0]) { - case 'N': - chistics.daccess= SP_NO_SQL; - break; - case 'C': - chistics.daccess= SP_CONTAINS_SQL; - break; - case 'R': - chistics.daccess= SP_READS_SQL_DATA; - break; - case 'M': - chistics.daccess= SP_MODIFIES_SQL_DATA; - break; - default: - chistics.daccess= SP_DEFAULT_ACCESS_MAPPING; - } - - if ((ptr= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_DETERMINISTIC])) == NULL) - { - ret= SP_GET_FIELD_FAILED; - goto done; - } - chistics.detistic= (ptr[0] == 'N' ? FALSE : TRUE); - - if ((ptr= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_SECURITY_TYPE])) == NULL) - { - ret= SP_GET_FIELD_FAILED; - goto done; - } - chistics.suid= (ptr[0] == 'I' ? SP_IS_NOT_SUID : SP_IS_SUID); - - if ((params= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_PARAM_LIST])) == NULL) - { - params= ""; - } - if (type == TYPE_ENUM_PROCEDURE) - returns= ""; - else if ((returns= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_RETURNS])) == NULL) + table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root, + ¶ms); + if (type() == TYPE_ENUM_PROCEDURE) + returns= empty_clex_str; + else if (table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, + &returns)) { ret= SP_GET_FIELD_FAILED; goto done; } - if ((body= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_BODY])) == NULL) + if (table->field[MYSQL_PROC_FIELD_BODY]->val_str_nopad(thd->mem_root, + &body)) { ret= SP_GET_FIELD_FAILED; goto done; } // Get additional information - if ((definer= get_field(thd->mem_root, - table->field[MYSQL_PROC_FIELD_DEFINER])) == NULL) - { - ret= SP_GET_FIELD_FAILED; - goto done; - } - modified= table->field[MYSQL_PROC_FIELD_MODIFIED]->val_int(); created= table->field[MYSQL_PROC_FIELD_CREATED]->val_int(); - sql_mode= (ulong) table->field[MYSQL_PROC_FIELD_SQL_MODE]->val_int(); - table->field[MYSQL_PROC_FIELD_COMMENT]->val_str(&str, &str); - - ptr= 0; - if ((length= str.length())) - ptr= thd->strmake(str.ptr(), length); - chistics.comment.str= ptr; - chistics.comment.length= length; - creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, name, table); close_system_tables(thd, &open_tables_state_backup); table= 0; - if (parse_user(definer, strlen(definer), - definer_user_name.str, &definer_user_name.length, - definer_host_name.str, &definer_host_name.length) && - definer_user_name.length && !definer_host_name.length) - { - // 'user@' -> 'user@%' - definer_host_name= host_not_specified; - } - - ret= db_load_routine(thd, type, name, sphp, - sql_mode, params, returns, body, chistics, - &definer_user_name, &definer_host_name, + ret= db_load_routine(thd, name, sphp, + sql_mode, params, returns, body, chistics, definer, created, modified, creation_ctx); done: /* @@ -681,6 +711,23 @@ db_find_routine(THD *thd, stored_procedure_type type, sp_name *name, } +int +Sp_handler::db_find_and_cache_routine(THD *thd, + const Database_qualified_name *name, + sp_head **sp) const +{ + int rc= db_find_routine(thd, name, sp); + if (rc == SP_OK) + { + sp_cache_insert(get_cache(thd), *sp); + DBUG_PRINT("info", ("added new: %p, level: %lu, flags %x", + sp[0], sp[0]->m_recursion_level, + sp[0]->m_flags)); + } + return rc; +} + + /** Silence DEPRECATED SYNTAX warnings when loading a stored procedure into the cache. @@ -809,14 +856,17 @@ Bad_db_error_handler::handle_condition(THD *thd, } -static int -db_load_routine(THD *thd, stored_procedure_type type, - sp_name *name, sp_head **sphp, - sql_mode_t sql_mode, const char *params, const char *returns, - const char *body, st_sp_chistics &chistics, - LEX_STRING *definer_user_name, LEX_STRING *definer_host_name, - longlong created, longlong modified, - Stored_program_creation_ctx *creation_ctx) +int +Sp_handler::db_load_routine(THD *thd, const Database_qualified_name *name, + sp_head **sphp, + sql_mode_t sql_mode, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + longlong created, longlong modified, + Stored_program_creation_ctx *creation_ctx) const { LEX *old_lex= thd->lex, newlex; String defstr; @@ -830,8 +880,6 @@ db_load_routine(THD *thd, stored_procedure_type type, thd->lex= &newlex; newlex.current_select= NULL; - // Resetting REPLACE and EXIST flags in create_info, for show_create_sp() - newlex.create_info.DDL_options_st::init(); defstr.set_charset(creation_ctx->get_client_cs()); @@ -841,15 +889,10 @@ db_load_routine(THD *thd, stored_procedure_type type, definition for SHOW CREATE PROCEDURE later. */ - if (!show_create_sp(thd, &defstr, - type, - NULL, 0, - name->m_name.str, name->m_name.length, - params, strlen(params), - returns, strlen(returns), - body, strlen(body), - &chistics, definer_user_name, definer_host_name, - sql_mode)) + if (show_create_sp(thd, &defstr, + null_clex_str, name->m_name, + params, returns, body, + chistics, definer, DDL_options(), sql_mode)) { ret= SP_INTERNAL_ERROR; goto end; @@ -886,7 +929,9 @@ db_load_routine(THD *thd, stored_procedure_type type, generate an error. */ - if (cur_db_changed && mysql_change_db(thd, &saved_cur_db_name, TRUE)) + if (cur_db_changed && mysql_change_db(thd, + (LEX_CSTRING*) &saved_cur_db_name, + TRUE)) { ret= SP_INTERNAL_ERROR; goto end; @@ -898,8 +943,8 @@ db_load_routine(THD *thd, stored_procedure_type type, goto end; } - (*sphp)->set_definer(definer_user_name, definer_host_name); - (*sphp)->set_info(created, modified, &chistics, sql_mode); + (*sphp)->set_definer(&definer.user, &definer.host); + (*sphp)->set_info(created, modified, chistics, sql_mode); (*sphp)->set_creation_ctx(creation_ctx); (*sphp)->optimize(); /* @@ -922,7 +967,7 @@ end: void -sp_returns_type(THD *thd, String &result, sp_head *sp) +sp_returns_type(THD *thd, String &result, const sp_head *sp) { TABLE table; TABLE_SHARE share; @@ -958,7 +1003,6 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) and invalidates the stored-routine cache. @param thd Thread context. - @param type Stored routine type (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @param table A pointer to the opened mysql.proc table @@ -966,9 +1010,10 @@ sp_returns_type(THD *thd, String &result, sp_head *sp) @return SP_OK on success, or SP_DELETE_ROW_FAILED on error. used to indicate about errors. */ -static int -sp_drop_routine_internal(THD *thd, stored_procedure_type type, - sp_name *name, TABLE *table) +int +Sp_handler::sp_drop_routine_internal(THD *thd, + const Database_qualified_name *name, + TABLE *table) const { DBUG_ENTER("sp_drop_routine_internal"); @@ -985,10 +1030,9 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type, local cache. */ sp_head *sp; - sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? - &thd->sp_func_cache : &thd->sp_proc_cache); - sp= sp_cache_lookup(spc, name); - if (sp) + sp_cache **spc= get_cache(thd); + DBUG_ASSERT(spc); + if ((sp= sp_cache_lookup(spc, name))) sp_cache_flush_obsolete(spc, &sp); DBUG_RETURN(SP_OK); } @@ -1001,8 +1045,6 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type, the mysql.proc. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION). @param sp Stored routine object to store. @note Opens and closes the thread tables. Therefore assumes @@ -1019,16 +1061,14 @@ sp_drop_routine_internal(THD *thd, stored_procedure_type type, */ bool -sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) +Sp_handler::sp_create_routine(THD *thd, const sp_head *sp) const { LEX *lex= thd->lex; bool ret= TRUE; TABLE *table; char definer_buf[USER_HOST_BUFF_SIZE]; - LEX_STRING definer; + LEX_CSTRING definer; sql_mode_t saved_mode= thd->variables.sql_mode; - MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? - MDL_key::FUNCTION : MDL_key::PROCEDURE; CHARSET_INFO *db_cs= get_default_db_collation(thd, sp->m_db.str); @@ -1036,15 +1076,15 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) bool store_failed= FALSE; DBUG_ENTER("sp_create_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", (int) type, + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), (int) sp->m_name.length, sp->m_name.str)); + MDL_key::enum_mdl_namespace mdl_type= get_mdl_type(); + LEX_CSTRING returns= empty_clex_str; String retstr(64); retstr.set_charset(system_charset_info); - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); - /* Grab an exclusive MDL lock. */ if (lock_object_name(thd, mdl_type, sp->m_db.str, sp->m_name.str)) { @@ -1072,17 +1112,17 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (!(table= open_proc_table_for_update(thd))) { - my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(type),sp->m_name.str); + my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str); goto done; } else { /* Checking if the routine already exists */ - if (db_find_routine_aux(thd, type, lex->spname, table) == SP_OK) + if (db_find_routine_aux(thd, sp, table) == SP_OK) { if (lex->create_info.or_replace()) { - if ((ret= sp_drop_routine_internal(thd, type, lex->spname, table))) + if ((ret= sp_drop_routine_internal(thd, sp, table))) goto done; } else if (lex->create_info.if_not_exists()) @@ -1090,20 +1130,21 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SP_ALREADY_EXISTS, ER_THD(thd, ER_SP_ALREADY_EXISTS), - SP_TYPE_STRING(type), - lex->spname->m_name.str); + type_str(), sp->m_name.str); ret= FALSE; // Setting retstr as it is used for logging. - if (sp->m_type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) + { sp_returns_type(thd, retstr, sp); + returns= retstr.lex_cstring(); + } goto log; } else { - my_error(ER_SP_ALREADY_EXISTS, MYF(0), - SP_TYPE_STRING(type), sp->m_name.str); + my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str); goto done; } } @@ -1115,8 +1156,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (table->s->fields < MYSQL_PROC_FIELD_COUNT) { - my_error(ER_SP_STORE_FAILED, MYF(0), - SP_TYPE_STRING(type), sp->m_name.str); + my_error(ER_SP_STORE_FAILED, MYF(0), type_str(), sp->m_name.str); goto done; } @@ -1136,45 +1176,46 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) store_failed= table->field[MYSQL_PROC_FIELD_DB]-> - store(sp->m_db.str, sp->m_db.length, system_charset_info); + store(sp->m_db, system_charset_info); store_failed= store_failed || table->field[MYSQL_PROC_FIELD_NAME]-> - store(sp->m_name.str, sp->m_name.length, system_charset_info); + store(sp->m_name, system_charset_info); store_failed= store_failed || table->field[MYSQL_PROC_MYSQL_TYPE]-> - store((longlong)type, TRUE); + store((longlong) type(), true); store_failed= store_failed || table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]-> - store(sp->m_name.str, sp->m_name.length, system_charset_info); + store(sp->m_name, system_charset_info); - if (sp->m_chistics->daccess != SP_DEFAULT_ACCESS) + if (sp->daccess() != SP_DEFAULT_ACCESS) { store_failed= store_failed || table->field[MYSQL_PROC_FIELD_ACCESS]-> - store((longlong)sp->m_chistics->daccess, TRUE); + store((longlong)sp->daccess(), TRUE); } store_failed= store_failed || table->field[MYSQL_PROC_FIELD_DETERMINISTIC]-> - store((longlong)(sp->m_chistics->detistic ? 1 : 2), TRUE); + store((longlong)(sp->detistic() ? 1 : 2), TRUE); - if (sp->m_chistics->suid != SP_IS_DEFAULT_SUID) + if (sp->suid() != SP_IS_DEFAULT_SUID) { store_failed= store_failed || table->field[MYSQL_PROC_FIELD_SECURITY_TYPE]-> - store((longlong)sp->m_chistics->suid, TRUE); + store((longlong)sp->suid(), TRUE); } store_failed= store_failed || table->field[MYSQL_PROC_FIELD_PARAM_LIST]-> - store(sp->m_params.str, sp->m_params.length, system_charset_info); + store(sp->m_params, system_charset_info); - if (sp->m_type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); + returns= retstr.lex_cstring(); store_failed= store_failed || table->field[MYSQL_PROC_FIELD_RETURNS]-> @@ -1183,11 +1224,11 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) store_failed= store_failed || table->field[MYSQL_PROC_FIELD_BODY]-> - store(sp->m_body.str, sp->m_body.length, system_charset_info); + store(sp->m_body, system_charset_info); store_failed= store_failed || table->field[MYSQL_PROC_FIELD_DEFINER]-> - store(definer.str, definer.length, system_charset_info); + store(definer, system_charset_info); ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time(); ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); @@ -1196,26 +1237,25 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) table->field[MYSQL_PROC_FIELD_SQL_MODE]-> store((longlong)saved_mode, TRUE); - if (sp->m_chistics->comment.str) + if (sp->comment().str) { store_failed= store_failed || table->field[MYSQL_PROC_FIELD_COMMENT]-> - store(sp->m_chistics->comment.str, sp->m_chistics->comment.length, - system_charset_info); + store(sp->comment(), system_charset_info); } - if ((sp->m_type == TYPE_ENUM_FUNCTION) && + if (type() == TYPE_ENUM_FUNCTION && !trust_function_creators && mysql_bin_log.is_open()) { - if (!sp->m_chistics->detistic) + if (!sp->detistic()) { /* Note that this test is not perfect; one could use a non-deterministic read-only function in an update statement. */ enum enum_sp_data_access access= - (sp->m_chistics->daccess == SP_DEFAULT_ACCESS) ? - SP_DEFAULT_ACCESS_MAPPING : sp->m_chistics->daccess; + (sp->daccess() == SP_DEFAULT_ACCESS) ? + SP_DEFAULT_ACCESS_MAPPING : sp->daccess(); if (access == SP_CONTAINS_SQL || access == SP_MODIFIES_SQL_DATA) { @@ -1252,7 +1292,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) table->field[MYSQL_PROC_FIELD_BODY_UTF8]->set_notnull(); store_failed= store_failed || table->field[MYSQL_PROC_FIELD_BODY_UTF8]->store( - sp->m_body_utf8.str, sp->m_body_utf8.length, system_charset_info); + sp->m_body_utf8, system_charset_info); if (store_failed) { @@ -1262,8 +1302,7 @@ sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp) if (table->file->ha_write_row(table->record[0])) { - my_error(ER_SP_ALREADY_EXISTS, MYF(0), - SP_TYPE_STRING(type), sp->m_name.str); + my_error(ER_SP_ALREADY_EXISTS, MYF(0), type_str(), sp->m_name.str); goto done; } /* Make change permanent and avoid 'table is marked as crashed' errors */ @@ -1280,16 +1319,13 @@ log: String log_query; log_query.set_charset(system_charset_info); - if (!show_create_sp(thd, &log_query, - sp->m_type, - (sp->m_explicit_name ? sp->m_db.str : NULL), - (sp->m_explicit_name ? sp->m_db.length : 0), - sp->m_name.str, sp->m_name.length, - sp->m_params.str, sp->m_params.length, - retstr.ptr(), retstr.length(), - sp->m_body.str, sp->m_body.length, - sp->m_chistics, &(thd->lex->definer->user), - &(thd->lex->definer->host), + if (show_create_sp(thd, &log_query, + sp->m_explicit_name ? sp->m_db : null_clex_str, + sp->m_name, + sp->m_params, returns, sp->m_body, + sp->chistics(), + thd->lex->definer[0], + thd->lex->create_info, saved_mode)) { my_error(ER_OUT_OF_RESOURCES, MYF(0)); @@ -1317,6 +1353,26 @@ done: } +static bool +append_suid(String *buf, enum_sp_suid_behaviour suid) +{ + return suid == SP_IS_NOT_SUID && + buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n")); +} + + +static bool +append_comment(String *buf, const LEX_CSTRING &comment) +{ + if (!comment.length) + return false; + if (buf->append(STRING_WITH_LEN(" COMMENT "))) + return true; + append_unescaped(buf, comment.str, comment.length); + return buf->append('\n'); +} + + /** Delete the record for the stored routine object from mysql.proc and do binary logging. @@ -1325,8 +1381,6 @@ done: from the mysql.proc table and invalidates the stored-routine cache. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @return Error code. SP_OK is returned on success. Other SP_ constants are @@ -1334,18 +1388,16 @@ done: */ int -sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name) +Sp_handler::sp_drop_routine(THD *thd, + const Database_qualified_name *name) const { TABLE *table; int ret; - MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? - MDL_key::FUNCTION : MDL_key::PROCEDURE; DBUG_ENTER("sp_drop_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - type, (int) name->m_name.length, name->m_name.str)); - - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), + (int) name->m_name.length, name->m_name.str)); + MDL_key::enum_mdl_namespace mdl_type= get_mdl_type(); /* Grab an exclusive MDL lock. */ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str)) @@ -1354,8 +1406,8 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name) if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) - ret= sp_drop_routine_internal(thd, type, name, table); + if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK) + ret= sp_drop_routine_internal(thd, name, table); if (ret == SP_OK && write_bin_log(thd, TRUE, thd->query(), thd->query_length())) @@ -1378,8 +1430,6 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name) successful update, the cache is invalidated. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @param chistics New values of stored routine attributes to write. @@ -1388,20 +1438,16 @@ sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name) */ int -sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name, - st_sp_chistics *chistics) +Sp_handler::sp_update_routine(THD *thd, const Database_qualified_name *name, + const st_sp_chistics *chistics) const { TABLE *table; int ret; - MDL_key::enum_mdl_namespace mdl_type= type == TYPE_ENUM_FUNCTION ? - MDL_key::FUNCTION : MDL_key::PROCEDURE; DBUG_ENTER("sp_update_routine"); - DBUG_PRINT("enter", ("type: %d name: %.*s", - (int) type, + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), (int) name->m_name.length, name->m_name.str)); - - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); + MDL_key::enum_mdl_namespace mdl_type= get_mdl_type(); /* Grab an exclusive MDL lock. */ if (lock_object_name(thd, mdl_type, name->m_db.str, name->m_name.str)) @@ -1410,9 +1456,9 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name, if (!(table= open_proc_table_for_update(thd))) DBUG_RETURN(SP_OPEN_TABLE_FAILED); - if ((ret= db_find_routine_aux(thd, type, name, table)) == SP_OK) + if ((ret= db_find_routine_aux(thd, name, table)) == SP_OK) { - if (type == TYPE_ENUM_FUNCTION && ! trust_function_creators && + if (type() == TYPE_ENUM_FUNCTION && ! trust_function_creators && mysql_bin_log.is_open() && (chistics->daccess == SP_CONTAINS_SQL || chistics->daccess == SP_MODIFIES_SQL_DATA)) @@ -1445,8 +1491,7 @@ sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name, table->field[MYSQL_PROC_FIELD_ACCESS]-> store((longlong)chistics->daccess, TRUE); if (chistics->comment.str) - table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment.str, - chistics->comment.length, + table->field[MYSQL_PROC_FIELD_COMMENT]->store(chistics->comment, system_charset_info); if ((ret= table->file->ha_update_row(table->record[1],table->record[0])) && ret != HA_ERR_RECORD_IS_THE_SAME) @@ -1503,7 +1548,7 @@ public: cases. */ -bool lock_db_routines(THD *thd, char *db) +bool lock_db_routines(THD *thd, const char *db) { TABLE *table; uint key_len; @@ -1589,7 +1634,7 @@ bool lock_db_routines(THD *thd, char *db) */ int -sp_drop_db_routines(THD *thd, char *db) +sp_drop_db_routines(THD *thd, const char *db) { TABLE *table; int ret; @@ -1662,8 +1707,6 @@ err: calls sp_head::show_create_routine() for the object. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) @param name Stored routine name. @return Error status. @@ -1672,18 +1715,16 @@ err: */ bool -sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name) +Sp_handler::sp_show_create_routine(THD *thd, + const Database_qualified_name *name) const { sp_head *sp; DBUG_ENTER("sp_show_create_routine"); - DBUG_PRINT("enter", ("name: %.*s", + DBUG_PRINT("enter", ("type: %s name: %.*s", + type_str(), (int) name->m_name.length, name->m_name.str)); - - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); - /* @todo: Consider using prelocking for this code as well. Currently SHOW CREATE PROCEDURE/FUNCTION is a dirty read of the data @@ -1691,18 +1732,16 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name) It is "safe" to do as long as it doesn't affect the results of the binary log or the query cache, which currently it does not. */ - if (sp_cache_routine(thd, type, name, FALSE, &sp)) + if (sp_cache_routine(thd, name, false, &sp)) DBUG_RETURN(TRUE); - if (sp == NULL || sp->show_create_routine(thd, type)) + if (sp == NULL || sp->show_create_routine(thd, this)) { /* If we have insufficient privileges, pretend the routine does not exist. */ - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), - type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE", - name->m_name.str); + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), type_str(), name->m_name.str); DBUG_RETURN(TRUE); } @@ -1710,12 +1749,90 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name) } +/* + In case of recursions, we create multiple copies of the same SP. + This methods checks the current recursion depth. + In case if the recursion limit exceeded, it throws an error + and returns NULL. + Otherwise, depending on the current recursion level, it: + - either returns the original SP, + - or makes and returns a new clone of SP +*/ +sp_head * +Sp_handler::sp_clone_and_link_routine(THD *thd, + const Database_qualified_name *name, + sp_head *sp) const +{ + DBUG_ENTER("sp_link_routine"); + ulong level; + sp_head *new_sp; + LEX_CSTRING returns= empty_clex_str; + + /* + String buffer for RETURNS data type must have system charset; + 64 -- size of "returns" column of mysql.proc. + */ + String retstr(64); + retstr.set_charset(sp->get_creation_ctx()->get_client_cs()); + + DBUG_PRINT("info", ("found: %p", sp)); + if (sp->m_first_free_instance) + { + DBUG_PRINT("info", ("first free: %p level: %lu flags %x", + sp->m_first_free_instance, + sp->m_first_free_instance->m_recursion_level, + sp->m_first_free_instance->m_flags)); + DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED)); + if (sp->m_first_free_instance->m_recursion_level > recursion_depth(thd)) + { + recursion_level_error(thd, sp); + DBUG_RETURN(0); + } + DBUG_RETURN(sp->m_first_free_instance); + } + /* + Actually depth could be +1 than the actual value in case a SP calls + SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more + instance. + */ + + level= sp->m_last_cached_sp->m_recursion_level + 1; + if (level > recursion_depth(thd)) + { + recursion_level_error(thd, sp); + DBUG_RETURN(0); + } + + if (type() == TYPE_ENUM_FUNCTION) + { + sp_returns_type(thd, retstr, sp); + returns= retstr.lex_cstring(); + } + if (db_load_routine(thd, name, &new_sp, + sp->m_sql_mode, sp->m_params, returns, + sp->m_body, sp->chistics(), + sp->m_definer, + sp->m_created, sp->m_modified, + sp->get_creation_ctx()) == SP_OK) + { + sp->m_last_cached_sp->m_next_cached_sp= new_sp; + new_sp->m_recursion_level= level; + new_sp->m_first_instance= sp; + sp->m_last_cached_sp= sp->m_first_free_instance= new_sp; + DBUG_PRINT("info", ("added level: %p, level: %lu, flags %x", + new_sp, new_sp->m_recursion_level, + new_sp->m_flags)); + DBUG_RETURN(new_sp); + } + DBUG_RETURN(0); +} + + /** Obtain object representing stored procedure/function by its name from stored procedures cache and looking into mysql.proc if needed. @param thd thread context - @param type type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE) @param name name of procedure @param cp hash to look routine in @param cache_only if true perform cache-only lookup @@ -1728,93 +1845,21 @@ sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name) */ sp_head * -sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name, - sp_cache **cp, bool cache_only) +Sp_handler::sp_find_routine(THD *thd, const Database_qualified_name *name, + bool cache_only) const { - sp_head *sp; - ulong depth= (type == TYPE_ENUM_PROCEDURE ? - thd->variables.max_sp_recursion_depth : - 0); DBUG_ENTER("sp_find_routine"); - DBUG_PRINT("enter", ("name: %.*s.%.*s type: %d cache only %d", + DBUG_PRINT("enter", ("name: %.*s.%.*s type: %s cache only %d", (int) name->m_db.length, name->m_db.str, (int) name->m_name.length, name->m_name.str, - type, cache_only)); + type_str(), cache_only)); + sp_cache **cp= get_cache(thd); + sp_head *sp; if ((sp= sp_cache_lookup(cp, name))) - { - ulong level; - sp_head *new_sp; - const char *returns= ""; - - /* - String buffer for RETURNS data type must have system charset; - 64 -- size of "returns" column of mysql.proc. - */ - String retstr(64); - retstr.set_charset(sp->get_creation_ctx()->get_client_cs()); - - DBUG_PRINT("info", ("found:%p", sp)); - if (sp->m_first_free_instance) - { - DBUG_PRINT("info", ("first free:%p level: %lu flags %x", - sp->m_first_free_instance, - sp->m_first_free_instance->m_recursion_level, - sp->m_first_free_instance->m_flags)); - DBUG_ASSERT(!(sp->m_first_free_instance->m_flags & sp_head::IS_INVOKED)); - if (sp->m_first_free_instance->m_recursion_level > depth) - { - sp->recursion_level_error(thd); - DBUG_RETURN(0); - } - DBUG_RETURN(sp->m_first_free_instance); - } - /* - Actually depth could be +1 than the actual value in case a SP calls - SHOW CREATE PROCEDURE. Hence, the linked list could hold up to one more - instance. - */ - - level= sp->m_last_cached_sp->m_recursion_level + 1; - if (level > depth) - { - sp->recursion_level_error(thd); - DBUG_RETURN(0); - } - - if (type == TYPE_ENUM_FUNCTION) - { - sp_returns_type(thd, retstr, sp); - returns= retstr.ptr(); - } - if (db_load_routine(thd, type, name, &new_sp, - sp->m_sql_mode, sp->m_params.str, returns, - sp->m_body.str, *sp->m_chistics, - &sp->m_definer_user, &sp->m_definer_host, - sp->m_created, sp->m_modified, - sp->get_creation_ctx()) == SP_OK) - { - sp->m_last_cached_sp->m_next_cached_sp= new_sp; - new_sp->m_recursion_level= level; - new_sp->m_first_instance= sp; - sp->m_last_cached_sp= sp->m_first_free_instance= new_sp; - DBUG_PRINT("info", ("added level:%p, level: %lu, flags %x", - new_sp, new_sp->m_recursion_level, - new_sp->m_flags)); - DBUG_RETURN(new_sp); - } - DBUG_RETURN(0); - } + DBUG_RETURN(sp_clone_and_link_routine(thd, name, sp)); if (!cache_only) - { - if (db_find_routine(thd, type, name, &sp) == SP_OK) - { - sp_cache_insert(cp, sp); - DBUG_PRINT("info", ("added new:%p, level: %lu, flags %x", - sp, sp->m_recursion_level, - sp->m_flags)); - } - } + db_find_and_cache_routine(thd, name, &sp); DBUG_RETURN(sp); } @@ -1825,8 +1870,6 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name, @param thd Thread handler @param routines List of needles in the hay stack - @param is_proc Indicates whether routines in the list are procedures - or functions. @return @retval FALSE Found. @@ -1834,7 +1877,7 @@ sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name, */ bool -sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc) +Sp_handler::sp_exist_routines(THD *thd, TABLE_LIST *routines) const { TABLE_LIST *routine; bool sp_object_found; @@ -1842,20 +1885,13 @@ sp_exist_routines(THD *thd, TABLE_LIST *routines, bool is_proc) for (routine= routines; routine; routine= routine->next_global) { sp_name *name; - LEX_STRING lex_db; - LEX_STRING lex_name; - lex_db.length= strlen(routine->db); - lex_name.length= strlen(routine->table_name); - lex_db.str= thd->strmake(routine->db, lex_db.length); - lex_name.str= thd->strmake(routine->table_name, lex_name.length); - name= new sp_name(lex_db, lex_name, true); - name->init_qname(thd); - sp_object_found= is_proc ? sp_find_routine(thd, TYPE_ENUM_PROCEDURE, - name, &thd->sp_proc_cache, - FALSE) != NULL : - sp_find_routine(thd, TYPE_ENUM_FUNCTION, - name, &thd->sp_func_cache, - FALSE) != NULL; + LEX_CSTRING lex_db; + LEX_CSTRING lex_name; + thd->make_lex_string(&lex_db, routine->db, strlen(routine->db)); + thd->make_lex_string(&lex_name, routine->table_name, + strlen(routine->table_name)); + name= new sp_name(&lex_db, &lex_name, true); + sp_object_found= sp_find_routine(thd, name, false) != NULL; thd->get_stmt_da()->clear_warning_info(thd->query_id); if (! sp_object_found) { @@ -1946,20 +1982,18 @@ bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, @param arena Arena in which memory for new element of the set will be allocated @param rt Routine name - @param rt_type Routine type (one of TYPE_ENUM_PROCEDURE/...) @note Will also add element to end of 'Query_tables_list::sroutines_list' list (and will take into account that this is an explicitly used routine). */ -void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, - sp_name *rt, enum stored_procedure_type rt_type) +void Sp_handler::add_used_routine(Query_tables_list *prelocking_ctx, + Query_arena *arena, + const Database_qualified_name *rt) const { - MDL_key key((rt_type == TYPE_ENUM_FUNCTION) ? MDL_key::FUNCTION : - MDL_key::PROCEDURE, - rt->m_db.str, rt->m_name.str); - (void)sp_add_used_routine(prelocking_ctx, arena, &key, 0); + MDL_key key(get_mdl_type(), rt->m_db.str, rt->m_name.str); + (void) sp_add_used_routine(prelocking_ctx, arena, &key, 0); prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next; prelocking_ctx->sroutines_list_own_elements= prelocking_ctx->sroutines_list.elements; @@ -2086,24 +2120,24 @@ void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx, prelocking until 'sp_name' is eradicated as a class. */ -int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, - bool lookup_only, sp_head **sp) +int Sroutine_hash_entry::sp_cache_routine(THD *thd, + bool lookup_only, + sp_head **sp) const { char qname_buff[NAME_LEN*2+1+1]; - sp_name name(&rt->mdl_request.key, qname_buff); - MDL_key::enum_mdl_namespace mdl_type= rt->mdl_request.key.mdl_namespace(); - stored_procedure_type type= ((mdl_type == MDL_key::FUNCTION) ? - TYPE_ENUM_FUNCTION : TYPE_ENUM_PROCEDURE); - + sp_name name(&mdl_request.key, qname_buff); + MDL_key::enum_mdl_namespace mdl_type= mdl_request.key.mdl_namespace(); + const Sp_handler *sph= Sp_handler::handler(mdl_type); + DBUG_ASSERT(sph); /* Check that we have an MDL lock on this routine, unless it's a top-level CALL. The assert below should be unambiguous: the first element in sroutines_list has an MDL lock unless it's a top-level call, or a trigger, but triggers can't occur here (see the preceding assert). */ - DBUG_ASSERT(rt->mdl_request.ticket || rt == thd->lex->sroutines_list.first); + DBUG_ASSERT(mdl_request.ticket || this == thd->lex->sroutines_list.first); - return sp_cache_routine(thd, type, &name, lookup_only, sp); + return sph->sp_cache_routine(thd, &name, lookup_only, sp); } @@ -2114,7 +2148,6 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, loading. @param[in] thd Thread context. - @param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE). @param[in] name Name of routine. @param[in] lookup_only Only check that the routine is in the cache. If it's not, don't try to load. If it is present, @@ -2127,16 +2160,17 @@ int sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, @retval non-0 Error while loading routine from mysql,proc table. */ -int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name, - bool lookup_only, sp_head **sp) +int Sp_handler::sp_cache_routine(THD *thd, + const Database_qualified_name *name, + bool lookup_only, + sp_head **sp) const { int ret= 0; - sp_cache **spc= (type == TYPE_ENUM_FUNCTION ? - &thd->sp_func_cache : &thd->sp_proc_cache); + sp_cache **spc= get_cache(thd); DBUG_ENTER("sp_cache_routine"); - DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE); + DBUG_ASSERT(spc); *sp= sp_cache_lookup(spc, name); @@ -2150,10 +2184,9 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name, DBUG_RETURN(SP_OK); } - switch ((ret= db_find_routine(thd, type, name, sp))) + switch ((ret= db_find_and_cache_routine(thd, name, sp))) { case SP_OK: - sp_cache_insert(spc, *sp); break; case SP_KEY_NOT_FOUND: ret= SP_OK; @@ -2176,20 +2209,8 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name, */ if (! thd->is_error()) { - /* - SP allows full NAME_LEN chars thus he have to allocate enough - size in bytes. Otherwise there is stack overrun could happen - if multibyte sequence is `name`. `db` is still safe because the - rest of the server checks agains NAME_LEN bytes and not chars. - Hence, the overrun happens only if the name is in length > 32 and - uses multibyte (cyrillic, greek, etc.) - */ - char n[NAME_LEN*2+2]; - - /* m_qname.str is not always \0 terminated */ - memcpy(n, name->m_qname.str, name->m_qname.length); - n[name->m_qname.length]= '\0'; - my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret); + my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), + ErrConvDQName(name).ptr(), ret); } break; } @@ -2201,56 +2222,57 @@ int sp_cache_routine(THD *thd, enum stored_procedure_type type, sp_name *name, Generates the CREATE... string from the table information. @return - Returns TRUE on success, FALSE on (alloc) failure. + Returns false on success, true on (alloc) failure. */ bool -show_create_sp(THD *thd, String *buf, - stored_procedure_type type, - const char *db, ulong dblen, - const char *name, ulong namelen, - const char *params, ulong paramslen, - const char *returns, ulong returnslen, - const char *body, ulong bodylen, - st_sp_chistics *chistics, - const LEX_STRING *definer_user, - const LEX_STRING *definer_host, - sql_mode_t sql_mode) +Sp_handler::show_create_sp(THD *thd, String *buf, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + const DDL_options_st ddl_options, + sql_mode_t sql_mode) const { sql_mode_t old_sql_mode= thd->variables.sql_mode; /* Make some room to begin with */ - if (buf->alloc(100 + dblen + 1 + namelen + paramslen + returnslen + bodylen + - chistics->comment.length + 10 /* length of " DEFINER= "*/ + + if (buf->alloc(100 + db.length + 1 + name.length + + params.length + returns.length + + chistics.comment.length + 10 /* length of " DEFINER= "*/ + USER_HOST_BUFF_SIZE)) - return FALSE; + return true; thd->variables.sql_mode= sql_mode; buf->append(STRING_WITH_LEN("CREATE ")); - if (thd->lex->create_info.or_replace()) + if (ddl_options.or_replace()) buf->append(STRING_WITH_LEN("OR REPLACE ")); - append_definer(thd, buf, definer_user, definer_host); - if (type == TYPE_ENUM_FUNCTION) - buf->append(STRING_WITH_LEN("FUNCTION ")); - else - buf->append(STRING_WITH_LEN("PROCEDURE ")); - if (thd->lex->create_info.if_not_exists()) + append_definer(thd, buf, &definer.user, &definer.host); + buf->append(type_lex_cstring()); + buf->append(STRING_WITH_LEN(" ")); + if (ddl_options.if_not_exists()) buf->append(STRING_WITH_LEN("IF NOT EXISTS ")); - if (dblen > 0) + if (db.length > 0) { - append_identifier(thd, buf, db, dblen); + append_identifier(thd, buf, db.str, db.length); buf->append('.'); } - append_identifier(thd, buf, name, namelen); + append_identifier(thd, buf, name.str, name.length); buf->append('('); - buf->append(params, paramslen); + buf->append(params); buf->append(')'); - if (type == TYPE_ENUM_FUNCTION) + if (type() == TYPE_ENUM_FUNCTION) { - buf->append(STRING_WITH_LEN(" RETURNS ")); - buf->append(returns, returnslen); + if (sql_mode & MODE_ORACLE) + buf->append(STRING_WITH_LEN(" RETURN ")); + else + buf->append(STRING_WITH_LEN(" RETURNS ")); + buf->append(returns); } buf->append('\n'); - switch (chistics->daccess) { + switch (chistics.daccess) { case SP_NO_SQL: buf->append(STRING_WITH_LEN(" NO SQL\n")); break; @@ -2265,19 +2287,13 @@ show_create_sp(THD *thd, String *buf, /* Do nothing */ break; } - if (chistics->detistic) + if (chistics.detistic) buf->append(STRING_WITH_LEN(" DETERMINISTIC\n")); - if (chistics->suid == SP_IS_NOT_SUID) - buf->append(STRING_WITH_LEN(" SQL SECURITY INVOKER\n")); - if (chistics->comment.length) - { - buf->append(STRING_WITH_LEN(" COMMENT ")); - append_unescaped(buf, chistics->comment.str, chistics->comment.length); - buf->append('\n'); - } - buf->append(body, bodylen); + append_suid(buf, chistics.suid); + append_comment(buf, chistics.comment); + buf->append(body); thd->variables.sql_mode= old_sql_mode; - return TRUE; + return false; } @@ -2302,28 +2318,19 @@ show_create_sp(THD *thd, String *buf, */ sp_head * -sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, - String *name, sql_mode_t sql_mode, - stored_procedure_type type, - const char *returns, const char *params, - bool *free_sp_head) +Sp_handler::sp_load_for_information_schema(THD *thd, TABLE *proc_table, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + sql_mode_t sql_mode, + bool *free_sp_head) const { - const char *sp_body; String defstr; - struct st_sp_chistics sp_chistics; - const LEX_STRING definer_user= {(char*)STRING_WITH_LEN("")}; - const LEX_STRING definer_host= {(char*)STRING_WITH_LEN("")}; - LEX_STRING sp_db_str; - LEX_STRING sp_name_str; + const AUTHID definer= {{STRING_WITH_LEN("")}, {STRING_WITH_LEN("")}}; sp_head *sp; - sp_cache **spc= ((type == TYPE_ENUM_PROCEDURE) ? - &thd->sp_proc_cache : &thd->sp_func_cache); - sp_db_str.str= db->c_ptr(); - sp_db_str.length= db->length(); - sp_name_str.str= name->c_ptr(); - sp_name_str.length= name->length(); - sp_name sp_name_obj(sp_db_str, sp_name_str, true); - sp_name_obj.init_qname(thd); + sp_cache **spc= get_cache(thd); + sp_name sp_name_obj(&db, &name, true); // This can change "name" *free_sp_head= 0; if ((sp= sp_cache_lookup(spc, &sp_name_obj))) { @@ -2333,16 +2340,11 @@ sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, LEX *old_lex= thd->lex, newlex; Stored_program_creation_ctx *creation_ctx= Stored_routine_creation_ctx::load_from_db(thd, &sp_name_obj, proc_table); - sp_body= (type == TYPE_ENUM_FUNCTION ? "RETURN NULL" : "BEGIN END"); - bzero((char*) &sp_chistics, sizeof(sp_chistics)); defstr.set_charset(creation_ctx->get_client_cs()); - if (!show_create_sp(thd, &defstr, type, - sp_db_str.str, sp_db_str.length, - sp_name_obj.m_name.str, sp_name_obj.m_name.length, - params, strlen(params), - returns, strlen(returns), - sp_body, strlen(sp_body), - &sp_chistics, &definer_user, &definer_host, sql_mode)) + if (show_create_sp(thd, &defstr, + sp_name_obj.m_db, sp_name_obj.m_name, + params, returns, empty_body_lex_cstring(), + Sp_chistics(), definer, DDL_options(), sql_mode)) return 0; thd->lex= &newlex; @@ -17,7 +17,10 @@ #ifndef _SP_H_ #define _SP_H_ +#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_string.h" // LEX_STRING +#include "sql_cmd.h" +#include "mdl.h" class Field; class Open_tables_backup; @@ -28,8 +31,10 @@ class Sroutine_hash_entry; class THD; class sp_cache; class sp_head; -class sp_name; +class sp_pcontext; +class Database_qualified_name; struct st_sp_chistics; +class Stored_program_creation_ctx; struct LEX; struct TABLE; struct TABLE_LIST; @@ -48,6 +53,283 @@ enum stored_procedure_type TYPE_ENUM_PROXY=4 }; + +class Sp_handler +{ + int db_find_routine_aux(THD *thd, const Database_qualified_name *name, + TABLE *table) const; + int db_find_routine(THD *thd, const Database_qualified_name *name, + sp_head **sphp) const; + int db_find_and_cache_routine(THD *thd, + const Database_qualified_name *name, + sp_head **sp) const; + int db_load_routine(THD *thd, const Database_qualified_name *name, + sp_head **sphp, + sql_mode_t sql_mode, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + longlong created, longlong modified, + Stored_program_creation_ctx *creation_ctx) const; + int sp_drop_routine_internal(THD *thd, + const Database_qualified_name *name, + TABLE *table) const; + + sp_head *sp_clone_and_link_routine(THD *thd, + const Database_qualified_name *name, + sp_head *sp) const; +public: + virtual ~Sp_handler() {} + static const Sp_handler *handler(enum enum_sql_command cmd); + static const Sp_handler *handler(stored_procedure_type type); + static const Sp_handler *handler(MDL_key::enum_mdl_namespace ns); + const char *type_str() const { return type_lex_cstring().str; } + virtual const char *show_create_routine_col1_caption() const + { + DBUG_ASSERT(0); + return ""; + } + virtual const char *show_create_routine_col3_caption() const + { + DBUG_ASSERT(0); + return ""; + } + virtual stored_procedure_type type() const= 0; + virtual LEX_CSTRING type_lex_cstring() const= 0; + virtual LEX_CSTRING empty_body_lex_cstring() const + { + static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("???")}; + DBUG_ASSERT(0); + return m_empty_body; + } + virtual MDL_key::enum_mdl_namespace get_mdl_type() const= 0; + virtual sp_cache **get_cache(THD *) const { return NULL; } +#ifndef NO_EMBEDDED_ACCESS_CHECKS + virtual HASH *get_priv_hash() const { return NULL; } +#endif + virtual ulong recursion_depth(THD *thd) const { return 0; } + /** + Return appropriate error about recursion limit reaching + + @param thd Thread handle + @param sp SP routine + + @remark For functions and triggers we return error about + prohibited recursion. For stored procedures we + return about reaching recursion limit. + */ + virtual void recursion_level_error(THD *thd, const sp_head *sp) const + { + my_error(ER_SP_NO_RECURSION, MYF(0)); + } + virtual bool add_instr_freturn(THD *thd, sp_head *sp, + sp_pcontext *spcont, + Item *item, LEX *lex) const; + virtual bool add_instr_preturn(THD *thd, sp_head *sp, + sp_pcontext *spcont) const; + + void add_used_routine(Query_tables_list *prelocking_ctx, + Query_arena *arena, + const Database_qualified_name *rt) const; + + sp_head *sp_find_routine(THD *thd, const Database_qualified_name *name, + bool cache_only) const; + int sp_cache_routine(THD *thd, const Database_qualified_name *name, + bool lookup_only, sp_head **sp) const; + + bool sp_exist_routines(THD *thd, TABLE_LIST *procs) const; + bool sp_show_create_routine(THD *thd, + const Database_qualified_name *name) const; + + bool sp_create_routine(THD *thd, const sp_head *sp) const; + + int sp_update_routine(THD *thd, const Database_qualified_name *name, + const st_sp_chistics *chistics) const; + + int sp_drop_routine(THD *thd, const Database_qualified_name *name) const; + + sp_head *sp_load_for_information_schema(THD *thd, TABLE *proc_table, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + sql_mode_t sql_mode, + bool *free_sp_head) const; + + /* + Make a SHOW CREATE statement. + @retval true on error + @retval false on success + */ + bool show_create_sp(THD *thd, String *buf, + const LEX_CSTRING &db, + const LEX_CSTRING &name, + const LEX_CSTRING ¶ms, + const LEX_CSTRING &returns, + const LEX_CSTRING &body, + const st_sp_chistics &chistics, + const AUTHID &definer, + const DDL_options_st ddl_options, + sql_mode_t sql_mode) const; +}; + + +class Sp_handler_procedure: public Sp_handler +{ +public: + stored_procedure_type type() const { return TYPE_ENUM_PROCEDURE; } + LEX_CSTRING type_lex_cstring() const + { + static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("PROCEDURE")}; + return m_type_str; + } + LEX_CSTRING empty_body_lex_cstring() const + { + static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("BEGIN END")}; + return m_empty_body; + } + const char *show_create_routine_col1_caption() const + { + return "Procedure"; + } + const char *show_create_routine_col3_caption() const + { + return "Create Procedure"; + } + MDL_key::enum_mdl_namespace get_mdl_type() const + { + return MDL_key::PROCEDURE; + } + sp_cache **get_cache(THD *) const; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + HASH *get_priv_hash() const; +#endif + ulong recursion_depth(THD *thd) const; + void recursion_level_error(THD *thd, const sp_head *sp) const; + bool add_instr_preturn(THD *thd, sp_head *sp, sp_pcontext *spcont) const; +}; + + +class Sp_handler_function: public Sp_handler +{ +public: + stored_procedure_type type() const { return TYPE_ENUM_FUNCTION; } + LEX_CSTRING type_lex_cstring() const + { + static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("FUNCTION")}; + return m_type_str; + } + LEX_CSTRING empty_body_lex_cstring() const + { + static LEX_CSTRING m_empty_body= {C_STRING_WITH_LEN("RETURN NULL")}; + return m_empty_body; + } + const char *show_create_routine_col1_caption() const + { + return "Function"; + } + const char *show_create_routine_col3_caption() const + { + return "Create Function"; + } + MDL_key::enum_mdl_namespace get_mdl_type() const + { + return MDL_key::FUNCTION; + } + sp_cache **get_cache(THD *) const; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + HASH *get_priv_hash() const; +#endif + bool add_instr_freturn(THD *thd, sp_head *sp, sp_pcontext *spcont, + Item *item, LEX *lex) const; +}; + + +class Sp_handler_trigger: public Sp_handler +{ +public: + stored_procedure_type type() const { return TYPE_ENUM_TRIGGER; } + LEX_CSTRING type_lex_cstring() const + { + static LEX_CSTRING m_type_str= {C_STRING_WITH_LEN("TRIGGER")}; + return m_type_str; + } + MDL_key::enum_mdl_namespace get_mdl_type() const + { + DBUG_ASSERT(0); + return MDL_key::TRIGGER; + } +}; + + +extern MYSQL_PLUGIN_IMPORT Sp_handler_function sp_handler_function; +extern MYSQL_PLUGIN_IMPORT Sp_handler_procedure sp_handler_procedure; +extern MYSQL_PLUGIN_IMPORT Sp_handler_trigger sp_handler_trigger; + + +inline const Sp_handler *Sp_handler::handler(enum_sql_command cmd) +{ + switch (cmd) { + case SQLCOM_CREATE_PROCEDURE: + case SQLCOM_ALTER_PROCEDURE: + case SQLCOM_DROP_PROCEDURE: + case SQLCOM_SHOW_PROC_CODE: + case SQLCOM_SHOW_CREATE_PROC: + case SQLCOM_SHOW_STATUS_PROC: + return &sp_handler_procedure; + case SQLCOM_CREATE_SPFUNCTION: + case SQLCOM_ALTER_FUNCTION: + case SQLCOM_DROP_FUNCTION: + case SQLCOM_SHOW_FUNC_CODE: + case SQLCOM_SHOW_CREATE_FUNC: + case SQLCOM_SHOW_STATUS_FUNC: + return &sp_handler_function; + default: + break; + } + return NULL; +} + + +inline const Sp_handler *Sp_handler::handler(stored_procedure_type type) +{ + switch (type) { + case TYPE_ENUM_PROCEDURE: + return &sp_handler_procedure; + case TYPE_ENUM_FUNCTION: + return &sp_handler_function; + case TYPE_ENUM_TRIGGER: + return &sp_handler_trigger; + case TYPE_ENUM_PROXY: + break; + } + return NULL; +} + + +inline const Sp_handler *Sp_handler::handler(MDL_key::enum_mdl_namespace type) +{ + switch (type) { + case MDL_key::FUNCTION: + return &sp_handler_function; + case MDL_key::PROCEDURE: + return &sp_handler_procedure; + case MDL_key::GLOBAL: + case MDL_key::SCHEMA: + case MDL_key::TABLE: + case MDL_key::TRIGGER: + case MDL_key::EVENT: + case MDL_key::COMMIT: + case MDL_key::USER_LOCK: + case MDL_key::NAMESPACE_END: + break; + } + return NULL; +} + + /* Tells what SP_DEFAULT_ACCESS should be mapped to */ #define SP_DEFAULT_ACCESS_MAPPING SP_CONTAINS_SQL @@ -93,7 +375,7 @@ enum /* Drop all routines in database 'db' */ int -sp_drop_db_routines(THD *thd, char *db); +sp_drop_db_routines(THD *thd, const char *db); /** Acquires exclusive metadata lock on all stored routines in the @@ -105,37 +387,7 @@ sp_drop_db_routines(THD *thd, char *db); @retval false Success @retval true Failure */ -bool lock_db_routines(THD *thd, char *db); - -sp_head * -sp_find_routine(THD *thd, stored_procedure_type type, sp_name *name, - sp_cache **cp, bool cache_only); - -int -sp_cache_routine(THD *thd, Sroutine_hash_entry *rt, - bool lookup_only, sp_head **sp); - - -int -sp_cache_routine(THD *thd, stored_procedure_type type, sp_name *name, - bool lookup_only, sp_head **sp); - -bool -sp_exist_routines(THD *thd, TABLE_LIST *procs, bool is_proc); - -bool -sp_show_create_routine(THD *thd, stored_procedure_type type, sp_name *name); - -bool -sp_create_routine(THD *thd, stored_procedure_type type, sp_head *sp); - -int -sp_update_routine(THD *thd, stored_procedure_type type, sp_name *name, - st_sp_chistics *chistics); - -int -sp_drop_routine(THD *thd, stored_procedure_type type, sp_name *name); - +bool lock_db_routines(THD *thd, const char *db); /** Structure that represents element in the set of stored routines @@ -171,14 +423,11 @@ public: changes. */ ulong m_sp_cache_version; + + int sp_cache_routine(THD *thd, bool lookup_only, sp_head **sp) const; }; -/* - Procedures for handling sets of stored routines used by statement or routine. -*/ -void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, - sp_name *rt, stored_procedure_type rt_type); bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena, const MDL_key *key, TABLE_LIST *belong_to_view); void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx); @@ -198,13 +447,6 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen, */ TABLE *open_proc_table_for_read(THD *thd, Open_tables_backup *backup); -sp_head * -sp_load_for_information_schema(THD *thd, TABLE *proc_table, String *db, - String *name, sql_mode_t sql_mode, - stored_procedure_type type, - const char *returns, const char *params, - bool *free_sp_head); - bool load_charset(MEM_ROOT *mem_root, Field *field, CHARSET_INFO *dflt_cs, @@ -217,17 +459,6 @@ bool load_collation(MEM_ROOT *mem_root, void sp_returns_type(THD *thd, String &result, - sp_head *sp); - -bool show_create_sp(THD *thd, String *buf, - stored_procedure_type type, - const char *db, ulong dblen, - const char *name, ulong namelen, - const char *params, ulong paramslen, - const char *returns, ulong returnslen, - const char *body, ulong bodylen, - st_sp_chistics *chistics, - const LEX_STRING *definer_user, - const LEX_STRING *definer_host, - sql_mode_t sql_mode); + const sp_head *sp); + #endif /* _SP_H_ */ diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc index bafd0f34ab6..70ba5084914 100644 --- a/sql/sp_cache.cc +++ b/sql/sp_cache.cc @@ -167,8 +167,7 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp) } /* Reading a ulong variable with no lock. */ sp->set_sp_cache_version(Cversion); - DBUG_PRINT("info",("sp_cache: inserting: %.*s", (int) sp->m_qname.length, - sp->m_qname.str)); + DBUG_PRINT("info",("sp_cache: inserting: %s", ErrConvDQName(sp).ptr())); c->insert(sp); *cp= c; // Update *cp if it was NULL } @@ -190,12 +189,13 @@ void sp_cache_insert(sp_cache **cp, sp_head *sp) NULL if the routine not found. */ -sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name) +sp_head *sp_cache_lookup(sp_cache **cp, const Database_qualified_name *name) { + char buf[NAME_LEN * 2 + 2]; sp_cache *c= *cp; if (! c) return NULL; - return c->lookup(name->m_qname.str, name->m_qname.length); + return c->lookup(buf, name->make_qname(buf, sizeof(buf))); } diff --git a/sql/sp_cache.h b/sql/sp_cache.h index b21d4c4bf25..51886a61ee9 100644 --- a/sql/sp_cache.h +++ b/sql/sp_cache.h @@ -32,7 +32,7 @@ class sp_head; class sp_cache; -class sp_name; +class Database_qualified_name; /* Cache usage scenarios: @@ -59,7 +59,7 @@ void sp_cache_init(); void sp_cache_end(); void sp_cache_clear(sp_cache **cp); void sp_cache_insert(sp_cache **cp, sp_head *sp); -sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name); +sp_head *sp_cache_lookup(sp_cache **cp, const Database_qualified_name *name); void sp_cache_invalidate(); void sp_cache_flush_obsolete(sp_cache **cp, sp_head **sp); ulong sp_cache_version(); diff --git a/sql/sp_head.cc b/sql/sp_head.cc index b3edcdca474..83884502342 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -23,8 +23,6 @@ #include "probes_mysql.h" #include "sql_show.h" // append_identifier #include "sql_db.h" // mysql_opt_change_db, mysql_change_db -#include "sql_table.h" // sp_prepare_create_field, - // prepare_create_field #include "sql_acl.h" // *_ACL #include "sql_array.h" // Dynamic_array #include "log_event.h" // Query_log_event @@ -94,67 +92,45 @@ sp_map_item_type(enum enum_field_types type) } -/** - Return a string representation of the Item value. - - @param thd thread handle - @param str string buffer for representation of the value - - @note - If the item has a string result type, the string is escaped - according to its character set. +bool Item_splocal::append_for_log(THD *thd, String *str) +{ + if (fix_fields(thd, NULL)) + return true; - @retval - NULL on error - @retval - non-NULL a pointer to valid a valid string on success -*/ + if (limit_clause_param) + return str->append_ulonglong(val_uint()); -static String * -sp_get_item_value(THD *thd, Item *item, String *str) -{ - switch (item->result_type()) { - case REAL_RESULT: - case INT_RESULT: - case DECIMAL_RESULT: - if (item->field_type() != MYSQL_TYPE_BIT) - return item->val_str(str); - else {/* Bit type is handled as binary string */} - /* fall through */ - case STRING_RESULT: - { - String *result= item->val_str(str); + /* + ROW variables are currently not allowed in select_list, e.g.: + SELECT row_variable; + ROW variables can appear in query parts where name is not important, e.g.: + SELECT ROW(1,2)=row_variable FROM t1; + So we can skip using NAME_CONST() and use ROW() constants directly. + */ + if (type_handler() == &type_handler_row) + return append_value_for_log(thd, str); - if (!result) - return NULL; + if (str->append(STRING_WITH_LEN(" NAME_CONST('")) || + str->append(&m_name) || + str->append(STRING_WITH_LEN("',"))) + return true; + return append_value_for_log(thd, str) || str->append(')'); +} - { - StringBuffer<STRING_BUFFER_USUAL_SIZE> buf(result->charset()); - CHARSET_INFO *cs= thd->variables.character_set_client; - - buf.append('_'); - buf.append(result->charset()->csname); - if (cs->escape_with_backslash_is_dangerous) - buf.append(' '); - append_query_string(cs, &buf, result->ptr(), result->length(), - thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES); - buf.append(" COLLATE '"); - buf.append(item->collation.collation->name); - buf.append('\''); - str->copy(buf); - - return str; - } - } - case ROW_RESULT: - default: - return NULL; - } +bool Item_splocal::append_value_for_log(THD *thd, String *str) +{ + StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1); + Item *item= this_item(); + String *str_value= item->type_handler()->print_item_value(thd, item, + &str_value_holder); + return str_value ? + str->append(*str_value) : + str->append(STRING_WITH_LEN("NULL")); } -bool Item_splocal::append_for_log(THD *thd, String *str) +bool Item_splocal_row_field::append_for_log(THD *thd, String *str) { if (fix_fields(thd, NULL)) return true; @@ -164,15 +140,11 @@ bool Item_splocal::append_for_log(THD *thd, String *str) if (str->append(STRING_WITH_LEN(" NAME_CONST('")) || str->append(&m_name) || + str->append(".") || + str->append(&m_field_name) || str->append(STRING_WITH_LEN("',"))) return true; - - StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1); - String *str_value= sp_get_item_value(thd, this_item(), &str_value_holder); - if (str_value) - return str->append(*str_value) || str->append(')'); - else - return str->append(STRING_WITH_LEN("NULL)")); + return append_value_for_log(thd, str) || str->append(')'); } @@ -262,12 +234,14 @@ sp_get_flags_for_command(LEX *lex) 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 @@ -286,6 +260,7 @@ sp_get_flags_for_command(LEX *lex) 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: @@ -368,14 +343,20 @@ sp_get_flags_for_command(LEX *lex) */ Item * -sp_prepare_func_item(THD* thd, Item **it_addr) +sp_prepare_func_item(THD* thd, Item **it_addr, uint cols) { DBUG_ENTER("sp_prepare_func_item"); + if (!(*it_addr)->fixed && + (*it_addr)->fix_fields(thd, it_addr)) + { + DBUG_PRINT("info", ("fix_fields() failed")); + DBUG_RETURN(NULL); + } it_addr= (*it_addr)->this_item_addr(thd, it_addr); - if (!(*it_addr)->fixed && - ((*it_addr)->fix_fields(thd, it_addr) || - (*it_addr)->check_cols(1))) + if ((!(*it_addr)->fixed && + (*it_addr)->fix_fields(thd, it_addr)) || + (*it_addr)->check_cols(cols)) { DBUG_PRINT("info", ("fix_fields() failed")); DBUG_RETURN(NULL); @@ -398,7 +379,8 @@ sp_prepare_func_item(THD* thd, Item **it_addr) */ bool -sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) +sp_eval_expr(THD *thd, Item *result_item, Field *result_field, + Item **expr_item_ptr) { Item *expr_item; enum_check_fields save_count_cuted_fields= thd->count_cuted_fields; @@ -411,10 +393,22 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr) if (!*expr_item_ptr) goto error; - if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr))) + if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr, + result_item ? result_item->cols() : 1))) goto error; /* + expr_item is now fixed, it's safe to call cmp_type() + If result_item is NULL, then we're setting the RETURN value. + */ + if ((!result_item || result_item->cmp_type() != ROW_RESULT) && + expr_item->cmp_type() == ROW_RESULT) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), 1); + goto error; + } + + /* Set THD flags to emit warnings/errors in case of overflow/type errors during saving the item into the field. @@ -461,42 +455,14 @@ error: */ sp_name::sp_name(const MDL_key *key, char *qname_buff) + :Database_qualified_name(key->db_name(), key->db_name_length(), + key->name(), key->name_length()), + m_explicit_name(false) { - m_db.str= (char*)key->db_name(); - m_db.length= key->db_name_length(); - m_name.str= (char*)key->name(); - m_name.length= key->name_length(); - m_qname.str= qname_buff; if (m_db.length) - { strxmov(qname_buff, m_db.str, ".", m_name.str, NullS); - m_qname.length= m_db.length + 1 + m_name.length; - } else - { strmov(qname_buff, m_name.str); - m_qname.length= m_name.length; - } - m_explicit_name= false; -} - - -/** - Init the qualified name from the db and name. -*/ -void -sp_name::init_qname(THD *thd) -{ - const uint dot= !!m_db.length; - /* m_qname format: [database + dot] + name + '\0' */ - m_qname.length= m_db.length + dot + m_name.length; - if (!(m_qname.str= (char*) thd->alloc(m_qname.length + 1))) - return; - sprintf(m_qname.str, "%.*s%.*s%.*s", - (int) m_db.length, (m_db.length ? m_db.str : ""), - dot, ".", - (int) m_name.length, m_name.str); - DBUG_ASSERT(ok_for_lower_case_names(m_db.str)); } @@ -514,7 +480,7 @@ sp_name::init_qname(THD *thd) */ bool -check_routine_name(LEX_STRING *ident) +check_routine_name(const LEX_CSTRING *ident) { DBUG_ASSERT(ident); DBUG_ASSERT(ident->str); @@ -574,14 +540,33 @@ sp_head::operator delete(void *ptr, size_t size) throw() } -sp_head::sp_head() +sp_head::sp_head(const Sp_handler *sph) :Query_arena(&main_mem_root, STMT_INITIALIZED_FOR_SP), + Database_qualified_name(&null_clex_str, &null_clex_str), + m_handler(sph), m_flags(0), + m_tmp_query(NULL), + m_explicit_name(false), + /* + FIXME: the only use case when name is NULL is events, and it should + be rewritten soon. Remove the else part and replace 'if' with + an assert when this is done. + */ + m_qname(null_clex_str), + m_params(null_clex_str), + m_body(null_clex_str), + m_body_utf8(null_clex_str), + m_defstr(null_clex_str), m_sp_cache_version(0), m_creation_ctx(0), unsafe_flags(0), m_select_number(1), + m_created(0), + m_modified(0), m_recursion_level(0), m_next_cached_sp(0), + m_param_begin(NULL), + m_param_end(NULL), + m_body_begin(NULL), m_cont_level(0) { m_first_instance= this; @@ -589,25 +574,17 @@ sp_head::sp_head() m_last_cached_sp= this; m_return_field_def.charset = NULL; - /* - FIXME: the only use case when name is NULL is events, and it should - be rewritten soon. Remove the else part and replace 'if' with - an assert when this is done. - */ - m_db= m_name= m_qname= null_lex_str; DBUG_ENTER("sp_head::sp_head"); m_backpatch.empty(); + m_backpatch_goto.empty(); m_cont_backpatch.empty(); m_lex.empty(); my_hash_init(&m_sptabs, system_charset_info, 0, 0, 0, sp_table_key, 0, 0); my_hash_init(&m_sroutines, system_charset_info, 0, 0, 0, sp_sroutine_key, 0, 0); - m_body_utf8.str= NULL; - m_body_utf8.length= 0; - DBUG_VOID_RETURN; } @@ -629,39 +606,12 @@ sp_head::init(LEX *lex) lex->trg_table_fields.empty(); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8, MYF(0)); - m_param_begin= NULL; - m_param_end= NULL; - - m_body_begin= NULL ; - - m_qname.str= NULL; - m_qname.length= 0; - - m_explicit_name= false; - - m_db.str= NULL; - m_db.length= 0; - - m_name.str= NULL; - m_name.length= 0; - - m_params.str= NULL; - m_params.length= 0; - - m_body.str= NULL; - m_body.length= 0; - - m_defstr.str= NULL; - m_defstr.length= 0; - - m_return_field_def.charset= NULL; - DBUG_VOID_RETURN; } void -sp_head::init_sp_name(THD *thd, sp_name *spname) +sp_head::init_sp_name(const sp_name *spname) { DBUG_ENTER("sp_head::init_sp_name"); @@ -670,23 +620,10 @@ sp_head::init_sp_name(THD *thd, sp_name *spname) DBUG_ASSERT(spname && spname->m_db.str && spname->m_db.length); /* We have to copy strings to get them into the right memroot. */ - - m_db.length= spname->m_db.length; - m_db.str= strmake_root(thd->mem_root, spname->m_db.str, spname->m_db.length); - - m_name.length= spname->m_name.length; - m_name.str= strmake_root(thd->mem_root, spname->m_name.str, - spname->m_name.length); - + Database_qualified_name::copy(&main_mem_root, spname->m_db, spname->m_name); m_explicit_name= spname->m_explicit_name; - if (spname->m_qname.length == 0) - spname->init_qname(thd); - - m_qname.length= spname->m_qname.length; - m_qname.str= (char*) memdup_root(thd->mem_root, - spname->m_qname.str, - spname->m_qname.length + 1); + spname->make_qname(&main_mem_root, &m_qname); DBUG_VOID_RETURN; } @@ -744,58 +681,6 @@ sp_head::set_stmt_end(THD *thd) } -static TYPELIB * -create_typelib(MEM_ROOT *mem_root, Column_definition *field_def, List<String> *src) -{ - TYPELIB *result= NULL; - CHARSET_INFO *cs= field_def->charset; - DBUG_ENTER("create_typelib"); - - if (src->elements) - { - result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB)); - result->count= src->elements; - result->name= ""; - if (!(result->type_names=(const char **) - alloc_root(mem_root,(sizeof(char *)+sizeof(int))*(result->count+1)))) - DBUG_RETURN(0); - result->type_lengths= (uint*)(result->type_names + result->count+1); - List_iterator<String> it(*src); - String conv; - for (uint i=0; i < result->count; i++) - { - uint32 dummy; - uint length; - String *tmp= it++; - - if (String::needs_conversion(tmp->length(), tmp->charset(), - cs, &dummy)) - { - uint cnv_errs; - conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs); - - length= conv.length(); - result->type_names[i]= (char*) strmake_root(mem_root, conv.ptr(), - length); - } - else - { - length= tmp->length(); - result->type_names[i]= strmake_root(mem_root, tmp->ptr(), length); - } - - // Strip trailing spaces. - length= cs->cset->lengthsp(cs, result->type_names[i], length); - result->type_lengths[i]= length; - ((uchar *)result->type_names[i])[length]= '\0'; - } - result->type_names[result->count]= 0; - result->type_lengths[result->count]= 0; - } - DBUG_RETURN(result); -} - - sp_head::~sp_head() { LEX *lex; @@ -841,10 +726,11 @@ sp_head::~sp_head() */ Field * -sp_head::create_result_field(uint field_max_length, const char *field_name, - TABLE *table) +sp_head::create_result_field(uint field_max_length, const LEX_CSTRING *field_name, + TABLE *table) const { Field *field; + LEX_CSTRING name; DBUG_ENTER("sp_head::create_result_field"); @@ -887,11 +773,13 @@ sp_head::create_result_field(uint field_max_length, const char *field_name, (m_return_field_def.pack_flag & (FIELDFLAG_BLOB|FIELDFLAG_GEOM)))); + if (field_name) + name= *field_name; + else + name= m_name; field= m_return_field_def.make_field(table->s, /* TABLE_SHARE ptr */ table->in_use->mem_root, - field_name ? - field_name : - (const char *) m_name.str); + &name); field->vcol_info= m_return_field_def.vcol_info; if (field) @@ -1066,30 +954,15 @@ subst_spvars(THD *thd, sp_instr *instr, LEX_STRING *query_str) } -/** - Return appropriate error about recursion limit reaching - - @param thd Thread handle - - @remark For functions and triggers we return error about - prohibited recursion. For stored procedures we - return about reaching recursion limit. -*/ - -void sp_head::recursion_level_error(THD *thd) +void Sp_handler_procedure::recursion_level_error(THD *thd, + const sp_head *sp) const { - if (m_type == TYPE_ENUM_PROCEDURE) - { - my_error(ER_SP_RECURSION_LIMIT, MYF(0), - static_cast<int>(thd->variables.max_sp_recursion_depth), - m_name.str); - } - else - my_error(ER_SP_NO_RECURSION, MYF(0)); + my_error(ER_SP_RECURSION_LIMIT, MYF(0), + static_cast<int>(thd->variables.max_sp_recursion_depth), + sp->m_name.str); } - /** Execute the routine. The main instruction jump loop is there. Assume the parameters already set. @@ -1265,11 +1138,10 @@ sp_head::execute(THD *thd, bool merge_da_on_success) /* Discard the initial part of executing routines. */ thd->profiling.discard_current_query(); #endif + sp_instr *i; DEBUG_SYNC(thd, "sp_head_execute_before_loop"); do { - sp_instr *i; - #if defined(ENABLED_PROFILING) /* Treat each "instr" of a routine as discrete unit that could be profiled. @@ -1429,6 +1301,13 @@ sp_head::execute(THD *thd, bool merge_da_on_success) da->opt_clear_warning_info(thd->query_id); da->copy_sql_conditions_from_wi(thd, &sp_wi); da->remove_marked_sql_conditions(); + if (i != NULL) + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_SP_STACK_TRACE, + ER_THD(thd, ER_SP_STACK_TRACE), + i->m_lineno, + m_qname.str != NULL ? m_qname.str : + "anonymous block"); } } @@ -1450,7 +1329,7 @@ sp_head::execute(THD *thd, bool merge_da_on_success) NULL. In this case, mysql_change_db() would generate an error. */ - err_status|= mysql_change_db(thd, &saved_cur_db_name, TRUE); + err_status|= mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE); } m_flags&= ~IS_INVOKED; DBUG_PRINT("info", @@ -1491,7 +1370,6 @@ sp_head::execute(THD *thd, bool merge_da_on_success) @param thd thread handle @param sp stored routine to change the context for - @param is_proc TRUE is procedure, FALSE if function @param save_ctx pointer to an old security context @todo @@ -1506,13 +1384,12 @@ sp_head::execute(THD *thd, bool merge_da_on_success) */ bool -set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, - Security_context **save_ctx) +set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx) { *save_ctx= 0; - if (sp->m_chistics->suid != SP_IS_NOT_SUID && - sp->m_security_ctx.change_security_context(thd, &sp->m_definer_user, - &sp->m_definer_host, + if (sp->suid() != SP_IS_NOT_SUID && + sp->m_security_ctx.change_security_context(thd, &sp->m_definer.user, + &sp->m_definer.host, &sp->m_db, save_ctx)) return TRUE; @@ -1529,7 +1406,7 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, */ if (*save_ctx && check_routine_access(thd, EXECUTE_ACL, - sp->m_db.str, sp->m_name.str, is_proc, FALSE)) + sp->m_db.str, sp->m_name.str, sp->m_handler, false)) { sp->m_security_ctx.restore_security_context(thd, *save_ctx); *save_ctx= 0; @@ -1541,6 +1418,73 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, #endif // ! NO_EMBEDDED_ACCESS_CHECKS +bool sp_head::check_execute_access(THD *thd) const +{ + return check_routine_access(thd, EXECUTE_ACL, + m_db.str, m_name.str, + m_handler, false); +} + + +/** + Create rcontext optionally using the routine security. + This is important for sql_mode=ORACLE to make sure that the invoker has + access to the tables mentioned in the %TYPE references. + + In non-Oracle sql_modes we do not need access to any tables, + so we can omit the security context switch for performance purposes. + + @param thd + @param ret_value + @retval NULL - error (access denided or EOM) + @retval !NULL - success (the invoker has rights to all %TYPE tables) +*/ +sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value, + Row_definition_list *defs, + bool switch_security_ctx) +{ + if (!(m_flags & HAS_COLUMN_TYPE_REFS)) + return sp_rcontext::create(thd, m_pcont, ret_value, *defs); + sp_rcontext *res= NULL; +#ifndef NO_EMBEDDED_ACCESS_CHECKS + Security_context *save_security_ctx; + if (switch_security_ctx && + set_routine_security_ctx(thd, this, &save_security_ctx)) + return NULL; +#endif + if (!defs->resolve_type_refs(thd)) + res= sp_rcontext::create(thd, m_pcont, ret_value, *defs); +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (switch_security_ctx) + m_security_ctx.restore_security_context(thd, save_security_ctx); +#endif + return res; +} + + +sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value, + List<Item> *args) +{ + DBUG_ASSERT(args); + Row_definition_list defs; + m_pcont->retrieve_field_definitions(&defs); + if (defs.adjust_formal_params_to_actual_params(thd, args)) + return NULL; + return rcontext_create(thd, ret_value, &defs, true); +} + + +sp_rcontext *sp_head::rcontext_create(THD *thd, Field *ret_value, + Item **args, uint arg_count) +{ + Row_definition_list defs; + m_pcont->retrieve_field_definitions(&defs); + if (defs.adjust_formal_params_to_actual_params(thd, args, arg_count)) + return NULL; + return rcontext_create(thd, ret_value, &defs, true); +} + + /** Execute trigger stored program. @@ -1569,8 +1513,8 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, bool sp_head::execute_trigger(THD *thd, - const LEX_STRING *db_name, - const LEX_STRING *table_name, + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name, GRANT_INFO *grant_info) { sp_rcontext *octx = thd->spcont; @@ -1587,10 +1531,10 @@ sp_head::execute_trigger(THD *thd, Security_context *save_ctx= NULL; - if (m_chistics->suid != SP_IS_NOT_SUID && + if (suid() != SP_IS_NOT_SUID && m_security_ctx.change_security_context(thd, - &m_definer_user, - &m_definer_host, + &m_definer.user, + &m_definer.host, &m_db, &save_ctx)) DBUG_RETURN(TRUE); @@ -1638,7 +1582,9 @@ sp_head::execute_trigger(THD *thd, init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0)); thd->set_n_backup_active_arena(&call_arena, &backup_arena); - if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL))) + Row_definition_list defs; + m_pcont->retrieve_field_definitions(&defs); + if (!(nctx= rcontext_create(thd, NULL, &defs, false))) { err_status= TRUE; goto err_with_cleanup; @@ -1735,7 +1681,8 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, invoking query properly. */ my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), - "FUNCTION", m_qname.str, m_pcont->context_var_count(), argcount); + "FUNCTION", ErrConvDQName(this).ptr(), + m_pcont->context_var_count(), argcount); DBUG_RETURN(TRUE); } /* @@ -1752,7 +1699,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0)); thd->set_n_backup_active_arena(&call_arena, &backup_arena); - if (!(nctx= sp_rcontext::create(thd, m_pcont, return_value_fld))) + if (!(nctx= rcontext_create(thd, return_value_fld, argp, argcount))) { thd->restore_active_arena(&call_arena, &backup_arena); err_status= TRUE; @@ -1809,9 +1756,9 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, if (arg_no) binlog_buf.append(','); - str_value= sp_get_item_value(thd, nctx->get_item(arg_no), - &str_value_holder); - + Item *item= nctx->get_item(arg_no); + str_value= item->type_handler()->print_item_value(thd, item, + &str_value_holder); if (str_value) binlog_buf.append(*str_value); else @@ -1823,7 +1770,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_security_ctx; - if (set_routine_security_ctx(thd, this, FALSE, &save_security_ctx)) + if (set_routine_security_ctx(thd, this, &save_security_ctx)) { err_status= TRUE; goto err_with_cleanup; @@ -1959,7 +1906,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (args->elements != params) { my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0), "PROCEDURE", - m_qname.str, params, args->elements); + ErrConvDQName(this).ptr(), params, args->elements); DBUG_RETURN(TRUE); } @@ -1967,7 +1914,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (! octx) { /* Create a temporary old context. */ - if (!(octx= sp_rcontext::create(thd, m_pcont, NULL))) + if (!(octx= rcontext_create(thd, NULL, args))) { DBUG_PRINT("error", ("Could not create octx")); DBUG_RETURN(TRUE); @@ -1982,7 +1929,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) thd->spcont->callers_arena= thd; } - if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL))) + if (!(nctx= rcontext_create(thd, NULL, args))) { delete nctx; /* Delete nctx if it was init() that failed. */ thd->spcont= save_spcont; @@ -2017,7 +1964,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) if (!srp) { - my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, m_qname.str); + my_error(ER_SP_NOT_VAR_ARG, MYF(0), i+1, ErrConvDQName(this).ptr()); err_status= TRUE; break; } @@ -2105,7 +2052,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) #ifndef NO_EMBEDDED_ACCESS_CHECKS Security_context *save_security_ctx= 0; if (!err_status) - err_status= set_routine_security_ctx(thd, this, TRUE, &save_security_ctx); + err_status= set_routine_security_ctx(thd, this, &save_security_ctx); #endif if (!err_status) @@ -2180,8 +2127,8 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) out_param_info->db_name= m_db.str; out_param_info->table_name= m_name.str; out_param_info->org_table_name= m_name.str; - out_param_info->col_name= spvar->name.str; - out_param_info->org_col_name= spvar->name.str; + out_param_info->col_name= spvar->name; + out_param_info->org_col_name= spvar->name; srp->set_out_param_info(out_param_info); } @@ -2225,34 +2172,23 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) */ bool -sp_head::reset_lex(THD *thd) +sp_head::reset_lex(THD *thd, sp_lex_local *sublex) { DBUG_ENTER("sp_head::reset_lex"); - LEX *sublex; LEX *oldlex= thd->lex; - sublex= new (thd->mem_root)st_lex_local; - if (sublex == 0) - DBUG_RETURN(TRUE); - - thd->lex= sublex; - (void)m_lex.push_front(oldlex); - - /* Reset most stuff. */ - lex_start(thd); + thd->set_local_lex(sublex); - /* And keep the SP stuff too */ - sublex->sphead= oldlex->sphead; - sublex->spcont= oldlex->spcont; - /* And trigger related stuff too */ - sublex->trg_chistics= oldlex->trg_chistics; - sublex->trg_table_fields.empty(); - sublex->sp_lex_in_use= FALSE; + DBUG_RETURN(m_lex.push_front(oldlex)); +} - /* Reset part of parser state which needs this. */ - thd->m_parser_state->m_yacc.reset_before_substatement(); - DBUG_RETURN(FALSE); +bool +sp_head::reset_lex(THD *thd) +{ + DBUG_ENTER("sp_head::reset_lex"); + sp_lex_local *sublex= new (thd->mem_root) sp_lex_local(thd, thd->lex); + DBUG_RETURN(sublex ? reset_lex(thd, sublex) : true); } @@ -2260,6 +2196,8 @@ sp_head::reset_lex(THD *thd) Restore lex during parsing, after we have parsed a sub statement. @param thd Thread handle + @param oldlex The upper level lex we're near to restore to + @param sublex The local lex we're near to restore from @return @retval TRUE failure @@ -2267,23 +2205,17 @@ sp_head::reset_lex(THD *thd) */ bool -sp_head::restore_lex(THD *thd) +sp_head::merge_lex(THD *thd, LEX *oldlex, LEX *sublex) { - DBUG_ENTER("sp_head::restore_lex"); - LEX *sublex= thd->lex; - LEX *oldlex; + DBUG_ENTER("sp_head::merge_lex"); sublex->set_trg_event_type_for_tables(); - oldlex= (LEX *)m_lex.pop(); - if (! oldlex) - DBUG_RETURN(FALSE); // Nothing to restore - oldlex->trg_table_fields.push_back(&sublex->trg_table_fields); /* If this substatement is unsafe, the entire routine is too. */ - DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags: 0x%x", - thd->lex->get_stmt_unsafe_flags())); + DBUG_PRINT("info", ("sublex->get_stmt_unsafe_flags: 0x%x", + sublex->get_stmt_unsafe_flags())); unsafe_flags|= sublex->get_stmt_unsafe_flags(); /* @@ -2305,13 +2237,6 @@ sp_head::restore_lex(THD *thd) /* Merge lists of PS parameters. */ oldlex->param_list.append(&sublex->param_list); - if (! sublex->sp_lex_in_use) - { - sublex->sphead= NULL; - lex_end(sublex); - delete sublex; - } - thd->lex= oldlex; DBUG_RETURN(FALSE); } @@ -2319,7 +2244,8 @@ sp_head::restore_lex(THD *thd) Put the instruction on the backpatch list, associated with the label. */ int -sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab) +sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab, + List<bp_t> *list, backpatch_instr_type itype) { bp_t *bp= (bp_t *) thd->alloc(sizeof(bp_t)); @@ -2327,7 +2253,45 @@ sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab) return 1; bp->lab= lab; bp->instr= i; - return m_backpatch.push_front(bp); + bp->instr_type= itype; + return list->push_front(bp); +} + +int +sp_head::push_backpatch(THD *thd, sp_instr *i, sp_label *lab) +{ + return push_backpatch(thd, i, lab, &m_backpatch, GOTO); +} + +int +sp_head::push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab) +{ + uint ip= instructions(); + + /* + 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; } /** @@ -2354,44 +2318,98 @@ sp_head::backpatch(sp_label *lab) DBUG_VOID_RETURN; } -/** - Prepare an instance of Column_definition for field creation - (fill all necessary attributes). - - @param[in] thd Thread handle - @param[in] lex Yacc parsing context - @param[out] field_def An instance of create_field to be filled +void +sp_head::backpatch_goto(THD *thd, sp_label *lab,sp_label *lab_begin_block) +{ + bp_t *bp; + uint dest= instructions(); + List_iterator<bp_t> li(m_backpatch_goto); - @retval - FALSE on success - @retval - TRUE on error -*/ + DBUG_ENTER("sp_head::backpatch_goto"); + while ((bp= li++)) + { + if (bp->instr->m_ip < lab_begin_block->ip || bp->instr->m_ip > lab->ip) + { + /* + Update only jump target from the beginning of the block where the + label is defined. + */ + continue; + } + if (my_strcasecmp(system_charset_info, + bp->lab->name.str, + lab->name.str) == 0) + { + if (bp->instr_type == GOTO) + { + DBUG_PRINT("info", + ("backpatch_goto: (m_ip %d, label 0x%lx <%s>) to dest %d", + bp->instr->m_ip, (ulong) lab, lab->name.str, dest)); + bp->instr->backpatch(dest, lab->ctx); + // Jump resolved, remove from the list + li.remove(); + continue; + } + if (bp->instr_type == CPOP) + { + int n= lab->ctx->diff_cursors(lab_begin_block->ctx, true); + if (n == 0) + { + // Remove cpop instr + replace_instr_to_nop(thd,bp->instr->m_ip); + } + else + { + // update count of cpop + static_cast<sp_instr_cpop*>(bp->instr)->update_count(n); + n= 1; + } + li.remove(); + continue; + } + if (bp->instr_type == HPOP) + { + int n= lab->ctx->diff_handlers(lab_begin_block->ctx, true); + if (n == 0) + { + // Remove hpop instr + replace_instr_to_nop(thd,bp->instr->m_ip); + } + else + { + // update count of cpop + static_cast<sp_instr_hpop*>(bp->instr)->update_count(n); + n= 1; + } + li.remove(); + continue; + } + } + } + DBUG_VOID_RETURN; +} bool -sp_head::fill_field_definition(THD *thd, LEX *lex, - Column_definition *field_def) +sp_head::check_unresolved_goto() { - uint unused1= 0; - - if (field_def->check(thd)) - return TRUE; - - if (field_def->interval_list.elements) - field_def->interval= create_typelib(mem_root, field_def, - &field_def->interval_list); - - sp_prepare_create_field(thd, field_def); - - if (prepare_create_field(field_def, &unused1, HA_CAN_GEOMETRY)) + DBUG_ENTER("sp_head::check_unresolved_goto"); + bool has_unresolved_label=false; + if (m_backpatch_goto.elements > 0) { - return TRUE; + List_iterator_fast<bp_t> li(m_backpatch_goto); + bp_t *bp; + while ((bp= li++)) + { + if ((bp->instr_type == GOTO)) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "GOTO", bp->lab->name.str); + has_unresolved_label=true; + } + } } - - return FALSE; + DBUG_RETURN(has_unresolved_label); } - int sp_head::new_cont_backpatch(sp_instr_opt_meta *i) { @@ -2427,53 +2445,44 @@ sp_head::do_cont_backpatch() } } -void -sp_head::set_info(longlong created, longlong modified, - st_sp_chistics *chistics, sql_mode_t sql_mode) + +bool +sp_head::sp_add_instr_cpush_for_cursors(THD *thd, sp_pcontext *pcontext) { - m_created= created; - m_modified= modified; - m_chistics= (st_sp_chistics *) memdup_root(mem_root, (char*) chistics, - sizeof(*chistics)); - if (m_chistics->comment.length == 0) - m_chistics->comment.str= 0; - else - m_chistics->comment.str= strmake_root(mem_root, - m_chistics->comment.str, - m_chistics->comment.length); - m_sql_mode= sql_mode; + for (uint i= 0; i < pcontext->frame_cursor_count(); i++) + { + const sp_pcursor *c= pcontext->get_cursor_by_local_frame_offset(i); + sp_instr_cpush *instr= new (thd->mem_root) + sp_instr_cpush(instructions(), pcontext, c->lex(), + pcontext->cursor_offset() + i); + if (instr == NULL || add_instr(instr)) + return true; + } + return false; } void -sp_head::set_definer(const char *definer, uint definerlen) +sp_head::set_chistics(const st_sp_chistics &chistics) { - char user_name_holder[USERNAME_LENGTH + 1]; - LEX_STRING user_name= { user_name_holder, USERNAME_LENGTH }; - - char host_name_holder[HOSTNAME_LENGTH + 1]; - LEX_STRING host_name= { host_name_holder, HOSTNAME_LENGTH }; - - if (parse_user(definer, definerlen, user_name.str, &user_name.length, - host_name.str, &host_name.length) && - user_name.length && !host_name.length) - { - // 'user@' -> 'user@%' - host_name= host_not_specified; - } - - set_definer(&user_name, &host_name); + m_chistics.set(chistics); + if (m_chistics.comment.length == 0) + m_chistics.comment.str= 0; + else + m_chistics.comment.str= strmake_root(mem_root, + m_chistics.comment.str, + m_chistics.comment.length); } void -sp_head::set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name) +sp_head::set_info(longlong created, longlong modified, + const st_sp_chistics &chistics, sql_mode_t sql_mode) { - m_definer_user.str= strmake_root(mem_root, user_name->str, user_name->length); - m_definer_user.length= user_name->length; - - m_definer_host.str= strmake_root(mem_root, host_name->str, host_name->length); - m_definer_host.length= host_name->length; + m_created= created; + m_modified= modified; + set_chistics(chistics); + m_sql_mode= sql_mode; } @@ -2545,13 +2554,13 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) *full_access= ((!check_table_access(thd, SELECT_ACL, &tables, FALSE, 1, TRUE) && (tables.grant.privilege & SELECT_ACL) != 0) || - (!strcmp(sp->m_definer_user.str, + (!strcmp(sp->m_definer.user.str, thd->security_ctx->priv_user) && - !strcmp(sp->m_definer_host.str, + !strcmp(sp->m_definer.host.str, thd->security_ctx->priv_host))); if (!*full_access) return check_some_routine_access(thd, sp->m_db.str, sp->m_name.str, - sp->m_type == TYPE_ENUM_PROCEDURE); + sp->m_handler); return 0; } @@ -2560,9 +2569,8 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) Collect metadata for SHOW CREATE statement for stored routines. @param thd Thread context. - @param type Stored routine type - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) + @param sph Stored routine handler + @param fields Item list to populate @return Error status. @retval FALSE on success @@ -2570,13 +2578,11 @@ bool check_show_routine_access(THD *thd, sp_head *sp, bool *full_access) */ void -sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields) +sp_head::show_create_routine_get_fields(THD *thd, const Sp_handler *sph, + List<Item> *fields) { - const char *col1_caption= type == TYPE_ENUM_PROCEDURE ? - "Procedure" : "Function"; - - const char *col3_caption= type == TYPE_ENUM_PROCEDURE ? - "Create Procedure" : "Create Function"; + const char *col1_caption= sph->show_create_routine_col1_caption(); + const char *col3_caption= sph->show_create_routine_col3_caption(); MEM_ROOT *mem_root= thd->mem_root; @@ -2623,8 +2629,7 @@ sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields) Implement SHOW CREATE statement for stored routines. @param thd Thread context. - @param type Stored routine type - (TYPE_ENUM_PROCEDURE or TYPE_ENUM_FUNCTION) + @param sph Stored routine handler @return Error status. @retval FALSE on success @@ -2632,20 +2637,17 @@ sp_head::show_create_routine_get_fields(THD *thd, int type, List<Item> *fields) */ bool -sp_head::show_create_routine(THD *thd, int type) +sp_head::show_create_routine(THD *thd, const Sp_handler *sph) { - const char *col1_caption= type == TYPE_ENUM_PROCEDURE ? - "Procedure" : "Function"; - - const char *col3_caption= type == TYPE_ENUM_PROCEDURE ? - "Create Procedure" : "Create Function"; + const char *col1_caption= sph->show_create_routine_col1_caption(); + const char *col3_caption= sph->show_create_routine_col3_caption(); bool err_status; Protocol *protocol= thd->protocol; List<Item> fields; - LEX_STRING sql_mode; + LEX_CSTRING sql_mode; bool full_access; MEM_ROOT *mem_root= thd->mem_root; @@ -2653,9 +2655,6 @@ sp_head::show_create_routine(THD *thd, int type) DBUG_ENTER("sp_head::show_create_routine"); DBUG_PRINT("info", ("routine %s", m_name.str)); - DBUG_ASSERT(type == TYPE_ENUM_PROCEDURE || - type == TYPE_ENUM_FUNCTION); - if (check_show_routine_access(thd, this, &full_access)) DBUG_RETURN(TRUE); @@ -2750,10 +2749,89 @@ int sp_head::add_instr(sp_instr *instr) 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; 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_jump(THD *thd, sp_pcontext *spcont, uint dest) +{ + sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(), + spcont, dest); + return i == NULL || add_instr(i); +} + + +bool sp_head::add_instr_jump_forward_with_backpatch(THD *thd, + sp_pcontext *spcont, + sp_label *lab) +{ + sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(instructions(), spcont); + if (i == NULL || add_instr(i)) + return true; + push_backpatch(thd, i, lab); + return false; +} + + +bool sp_head::add_instr_freturn(THD *thd, sp_pcontext *spcont, + Item *item, LEX *lex) +{ + sp_instr_freturn *i= new (thd->mem_root) + sp_instr_freturn(instructions(), spcont, item, + m_return_field_def.type_handler(), thd->lex); + if (i == NULL || add_instr(i)) + return true; + m_flags|= sp_head::HAS_RETURN; + return false; +} + + +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. +*/ +bool sp_head::replace_instr_to_nop(THD *thd, uint ip) +{ + sp_instr *instr= get_instr(ip); + sp_instr_jump *nop= new (thd->mem_root) sp_instr_jump(instr->m_ip, + instr->m_ctx, + instr->m_ip + 1); + if (!nop) + return true; + delete instr; + set_dynamic(&m_instr, (uchar *) &nop, ip); + return false; +} + + /** Do some minimal optimization of the code: -# Mark used instructions @@ -2911,7 +2989,7 @@ sp_head::show_routine_code(THD *thd) push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, tmp); } protocol->prepare_for_resend(); - protocol->store((longlong)ip); + protocol->store_long(ip); buffer.set("", 0, system_charset_info); i->print(&buffer); @@ -3101,6 +3179,25 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp, } +int sp_lex_keeper::cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, + bool open_tables, + sp_instr *instr) +{ + Query_arena *old_arena= thd->stmt_arena; + /* + Get the Query_arena from the cursor statement LEX, which contains + the free_list of the query, so new items (if any) are stored in + the right free_list, and we can cleanup after each cursor operation, + e.g. open or cursor_copy_struct (for cursor%ROWTYPE variables). + */ + thd->stmt_arena= m_lex->query_arena(); + int res= reset_lex_and_exec_core(thd, nextp, open_tables, instr); + cleanup_items(thd->stmt_arena->free_list); + thd->stmt_arena= old_arena; + return res; +} + + /* sp_instr class functions */ @@ -3318,6 +3415,114 @@ sp_instr_set::print(String *str) /* + sp_instr_set_field class functions +*/ + +int +sp_instr_set_row_field::exec_core(THD *thd, uint *nextp) +{ + int res= thd->spcont->set_variable_row_field(thd, m_offset, m_field_offset, + &m_value); + if (res) + { + /* Failed to evaluate the value. Reset the variable to NULL. */ + thd->spcont->set_variable_row_field_to_null(thd, m_offset, m_field_offset); + } + delete_explain_query(thd->lex); + *nextp= m_ip + 1; + return res; +} + + +void +sp_instr_set_row_field::print(String *str) +{ + /* set name@offset[field_offset] ... */ + int rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3; + sp_variable *var= m_ctx->find_variable(m_offset); + DBUG_ASSERT(var); + DBUG_ASSERT(var->field_def.is_row()); + const Column_definition *def= + var->field_def.row_field_definitions()->elem(m_field_offset); + DBUG_ASSERT(def); + + rsrv+= var->name.length + def->field_name.length; + if (str->reserve(rsrv)) + return; + str->qs_append(STRING_WITH_LEN("set ")); + str->qs_append(var->name.str, var->name.length); + str->qs_append('.'); + str->qs_append(def->field_name.str, def->field_name.length); + str->qs_append('@'); + str->qs_append(m_offset); + str->qs_append('['); + str->qs_append(m_field_offset); + str->qs_append(']'); + str->qs_append(' '); + m_value->print(str, enum_query_type(QT_ORDINARY | + QT_ITEM_ORIGINAL_FUNC_NULLIF)); +} + + +/* + sp_instr_set_field_by_name class functions +*/ + +int +sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp) +{ + int res; + uint idx; + Item_field_row *row= (Item_field_row*) thd->spcont->get_item(m_offset); + if ((res= row->element_index_by_name(&idx, m_field_name))) + { + sp_variable *var= m_ctx->find_variable(m_offset); + my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0), + var->name.str, m_field_name.str); + goto error; + } + res= thd->spcont->set_variable_row_field(thd, m_offset, idx, &m_value); + if (res) + { + /* Failed to evaluate the value. Reset the variable to NULL. */ + thd->spcont->set_variable_row_field_to_null(thd, m_offset, idx); + } +error: + delete_explain_query(thd->lex); + *nextp= m_ip + 1; + return res; +} + + +void +sp_instr_set_row_field_by_name::print(String *str) +{ + /* set name.field@offset["field"] ... */ + int rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2; + sp_variable *var= m_ctx->find_variable(m_offset); + DBUG_ASSERT(var); + DBUG_ASSERT(var->field_def.is_table_rowtype_ref() || + var->field_def.is_cursor_rowtype_ref()); + + rsrv+= var->name.length + 2 * m_field_name.length; + if (str->reserve(rsrv)) + return; + str->qs_append(STRING_WITH_LEN("set ")); + str->qs_append(var->name.str, var->name.length); + str->qs_append('.'); + str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append('@'); + str->qs_append(m_offset); + str->qs_append("[\"",2); + str->qs_append(m_field_name.str, m_field_name.length); + str->qs_append("\"]",2); + str->qs_append(' '); + m_value->print(str, enum_query_type(QT_ORDINARY | + QT_ITEM_ORIGINAL_FUNC_NULLIF)); +} + + +/* sp_instr_set_trigger_field class functions */ @@ -3542,8 +3747,21 @@ sp_instr_freturn::exec_core(THD *thd, uint *nextp) That means, Diagnostics Area should be clean before its execution. */ - Diagnostics_area *da= thd->get_stmt_da(); - da->clear_warning_info(da->warning_info_id()); + if (!(thd->variables.sql_mode & MODE_ORACLE)) + { + /* + Don't clean warnings in ORACLE mode, + as they are needed for SQLCODE and SQLERRM: + BEGIN + SELECT a INTO a FROM t1; + RETURN 'No exception ' || SQLCODE || ' ' || SQLERRM; + EXCEPTION WHEN NO_DATA_FOUND THEN + RETURN 'Exception ' || SQLCODE || ' ' || SQLERRM; + END; + */ + Diagnostics_area *da= thd->get_stmt_da(); + da->clear_warning_info(da->warning_info_id()); + } /* Change <next instruction pointer>, so that this will be the last @@ -3570,7 +3788,7 @@ sp_instr_freturn::print(String *str) if (str->reserve(1024+8+32)) // Add some for the expr. too return; str->qs_append(STRING_WITH_LEN("freturn ")); - str->qs_append((uint)m_type); + str->qs_append(m_type_handler->name().ptr()); str->qs_append(' '); m_value->print(str, enum_query_type(QT_ORDINARY | QT_ITEM_ORIGINAL_FUNC_NULLIF)); @@ -3744,7 +3962,7 @@ sp_instr_cpush::execute(THD *thd, uint *nextp) { DBUG_ENTER("sp_instr_cpush::execute"); - int ret= thd->spcont->push_cursor(thd, &m_lex_keeper, this); + int ret= thd->spcont->push_cursor(thd, &m_lex_keeper); *nextp= m_ip+1; @@ -3755,7 +3973,7 @@ sp_instr_cpush::execute(THD *thd, uint *nextp) void sp_instr_cpush::print(String *str) { - const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor); + const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor); /* cpush name@offset */ uint rsrv= SP_INSTR_UINT_MAXLEN+7; @@ -3824,19 +4042,7 @@ sp_instr_copen::execute(THD *thd, uint *nextp) else { sp_lex_keeper *lex_keeper= c->get_lex_keeper(); - Query_arena *old_arena= thd->stmt_arena; - - /* - Get the Query_arena from the cpush instruction, which contains - the free_list of the query, so new items (if any) are stored in - the right free_list, and we can cleanup after each open. - */ - thd->stmt_arena= c->get_instr(); - res= lex_keeper->reset_lex_and_exec_core(thd, nextp, FALSE, this); - /* Cleanup the query's items */ - if (thd->stmt_arena->free_list) - cleanup_items(thd->stmt_arena->free_list); - thd->stmt_arena= old_arena; + res= lex_keeper->cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this); /* TODO: Assert here that we either have an error or a cursor */ } DBUG_RETURN(res); @@ -3855,7 +4061,7 @@ sp_instr_copen::exec_core(THD *thd, uint *nextp) void sp_instr_copen::print(String *str) { - const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor); + const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor); /* copen name@offset */ uint rsrv= SP_INSTR_UINT_MAXLEN+7; @@ -3897,7 +4103,7 @@ sp_instr_cclose::execute(THD *thd, uint *nextp) void sp_instr_cclose::print(String *str) { - const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor); + const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor); /* cclose name@offset */ uint rsrv= SP_INSTR_UINT_MAXLEN+8; @@ -3940,7 +4146,7 @@ sp_instr_cfetch::print(String *str) { List_iterator_fast<sp_variable> li(m_varlist); sp_variable *pv; - const LEX_STRING *cursor_name= m_ctx->find_cursor(m_cursor); + const LEX_CSTRING *cursor_name= m_ctx->find_cursor(m_cursor); /* cfetch name@offset vars... */ uint rsrv= SP_INSTR_UINT_MAXLEN+8; @@ -3969,6 +4175,84 @@ sp_instr_cfetch::print(String *str) /* + sp_instr_cursor_copy_struct class functions +*/ + +/** + This methods processes cursor %ROWTYPE declarations, e.g.: + CURSOR cur IS SELECT * FROM t1; + rec cur%ROWTYPE; + and does the following: + - opens the cursor without copying data (materialization). + - copies the cursor structure to the associated %ROWTYPE variable. +*/ +int +sp_instr_cursor_copy_struct::exec_core(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_cursor_copy_struct::exec_core"); + int ret= 0; + Item_field_row *row= (Item_field_row*) thd->spcont->get_item(m_var); + DBUG_ASSERT(row->type_handler() == &type_handler_row); + + /* + Copy structure only once. If the cursor%ROWTYPE variable is declared + inside a LOOP block, it gets its structure on the first loop interation + and remembers the structure for all consequent loop iterations. + It we recreated the structure on every iteration, we would get + potential memory leaks, and it would be less efficient. + */ + if (!row->arguments()) + { + sp_cursor tmp(thd, &m_lex_keeper); + // Open the cursor without copying data + if (!(ret= tmp.open_view_structure_only(thd))) + { + Row_definition_list defs; + if (!(ret= tmp.export_structure(thd, &defs))) + { + /* + Create row elements on the caller arena. + It's the same arena that was used during sp_rcontext::create(). + This puts cursor%ROWTYPE elements on the same mem_root + where explicit ROW elements and table%ROWTYPE reside. + */ + Query_arena current_arena; + thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena); + row->row_create_items(thd, &defs); + thd->restore_active_arena(thd->spcont->callers_arena, ¤t_arena); + } + tmp.close(thd); + } + } + *nextp= m_ip + 1; + DBUG_RETURN(ret); +} + + +int +sp_instr_cursor_copy_struct::execute(THD *thd, uint *nextp) +{ + DBUG_ENTER("sp_instr_cursor_copy_struct::execute"); + int ret= m_lex_keeper.cursor_reset_lex_and_exec_core(thd, nextp, FALSE, this); + DBUG_RETURN(ret); +} + + +void +sp_instr_cursor_copy_struct::print(String *str) +{ + sp_variable *var= m_ctx->find_variable(m_var); + const LEX_CSTRING *name= m_lex_keeper.cursor_name(); + str->append(STRING_WITH_LEN("cursor_copy_struct ")); + str->append(name); + str->append(' '); + str->append(&var->name); + str->append('@'); + str->append_ulonglong(m_var); +} + + +/* sp_instr_error class functions */ @@ -4132,7 +4416,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) { SP_TABLE *tab; - if (lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE && + if ((lex_for_tmp_check->sql_command == SQLCOM_DROP_TABLE || + lex_for_tmp_check->sql_command == SQLCOM_DROP_SEQUENCE) && lex_for_tmp_check->tmp_table()) return TRUE; @@ -4196,7 +4481,8 @@ sp_head::merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check) { if (!(tab= (SP_TABLE *)thd->calloc(sizeof(SP_TABLE)))) return FALSE; - if (lex_for_tmp_check->sql_command == SQLCOM_CREATE_TABLE && + 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()) { @@ -4328,3 +4614,219 @@ sp_add_to_query_tables(THD *thd, LEX *lex, return table; } + +Item *sp_head::adjust_assignment_source(THD *thd, Item *val, Item *val2) +{ + return val ? val : val2 ? val2 : new (thd->mem_root) Item_null(thd); +} + +/** + Helper action for a SET statement. + Used to push a SP local variable into the assignment list. + + @param var_type the SP local variable + @param val the value being assigned to the variable + + @return TRUE if error, FALSE otherwise. +*/ + +bool +sp_head::set_local_variable(THD *thd, sp_pcontext *spcont, + sp_variable *spv, Item *val, LEX *lex, + bool responsible_to_free_lex) +{ + if (!(val= adjust_assignment_source(thd, val, spv->default_value))) + return true; + + sp_instr_set *sp_set= new (thd->mem_root) + sp_instr_set(instructions(), spcont, + spv->offset, val, lex, + responsible_to_free_lex); + + return sp_set == NULL || add_instr(sp_set); +} + + +/** + Similar to set_local_variable(), but for ROW variable fields. +*/ +bool +sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont, + sp_variable *spv, uint field_idx, + Item *val, LEX *lex) +{ + if (!(val= adjust_assignment_source(thd, val, NULL))) + return true; + + sp_instr_set_row_field *sp_set= new (thd->mem_root) + sp_instr_set_row_field(instructions(), + spcont, + spv->offset, + field_idx, val, + lex, true); + return sp_set == NULL || add_instr(sp_set); +} + + +bool +sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont, + sp_variable *spv, + const LEX_CSTRING *field_name, + Item *val, LEX *lex) +{ + if (!(val= adjust_assignment_source(thd, val, NULL))) + return true; + + sp_instr_set_row_field_by_name *sp_set= + new (thd->mem_root) sp_instr_set_row_field_by_name(instructions(), + spcont, + spv->offset, + *field_name, + val, + lex, true); + return sp_set == NULL || add_instr(sp_set); +} + + +bool sp_head::add_open_cursor(THD *thd, sp_pcontext *spcont, uint offset, + sp_pcontext *param_spcont, + List<sp_assignment_lex> *parameters) +{ + /* + 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)); + + if (parameters && + add_set_cursor_param_variables(thd, param_spcont, parameters)) + 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_for_loop_open_cursor(THD *thd, sp_pcontext *spcont, + sp_variable *index, + const sp_pcursor *pcursor, uint coffset, + sp_assignment_lex *param_lex, + Item_args *parameters) +{ + if (parameters && + add_set_for_loop_cursor_param_variables(thd, pcursor->param_context(), + param_lex, parameters)) + return true; + + sp_instr *instr_copy_struct= + new (thd->mem_root) sp_instr_cursor_copy_struct(instructions(), + spcont, pcursor->lex(), + index->offset); + if (instr_copy_struct == NULL || add_instr(instr_copy_struct)) + return true; + + sp_instr_copen *instr_copen= + new (thd->mem_root) sp_instr_copen(instructions(), spcont, coffset); + if (instr_copen == NULL || add_instr(instr_copen)) + return true; + + sp_instr_cfetch *instr_cfetch= + new (thd->mem_root) sp_instr_cfetch(instructions(), + spcont, coffset); + if (instr_cfetch == NULL || add_instr(instr_cfetch)) + return true; + instr_cfetch->add_to_varlist(index); + return false; +} + + +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, + spvar, parameters->arguments()[idx], + param_lex, last)) + return true; + } + return false; +} + + +bool sp_head::spvar_fill_row(THD *thd, + sp_variable *spvar, + Row_definition_list *defs) +{ + spvar->field_def.field_name= spvar->name; + if (fill_spvar_definition(thd, &spvar->field_def)) + return true; + row_fill_field_definitions(thd, defs); + spvar->field_def.set_row_field_definitions(defs); + return false; +} + + +bool sp_head::spvar_fill_type_reference(THD *thd, + sp_variable *spvar, + const LEX_CSTRING &table, + const LEX_CSTRING &col) +{ + Qualified_column_ident *ref; + if (!(ref= new (thd->mem_root) Qualified_column_ident(&table, &col))) + return true; + fill_spvar_using_type_reference(spvar, ref); + return false; +} + + +bool sp_head::spvar_fill_type_reference(THD *thd, + sp_variable *spvar, + const LEX_CSTRING &db, + const LEX_CSTRING &table, + const LEX_CSTRING &col) +{ + Qualified_column_ident *ref; + if (!(ref= new (thd->mem_root) Qualified_column_ident(thd, &db, &table, &col))) + return true; + fill_spvar_using_type_reference(spvar, ref); + return false; +} + + +bool sp_head::spvar_fill_table_rowtype_reference(THD *thd, + sp_variable *spvar, + const LEX_CSTRING &table) +{ + Table_ident *ref; + if (!(ref= new (thd->mem_root) Table_ident(&table))) + return true; + fill_spvar_using_table_rowtype_reference(thd, spvar, ref); + return false; +} + + +bool sp_head::spvar_fill_table_rowtype_reference(THD *thd, + sp_variable *spvar, + const LEX_CSTRING &db, + const LEX_CSTRING &table) +{ + Table_ident *ref; + if (!(ref= new (thd->mem_root) Table_ident(thd, &db, &table, false))) + return true; + fill_spvar_using_table_rowtype_reference(thd, spvar, ref); + return false; +} diff --git a/sql/sp_head.h b/sql/sp_head.h index ed9fef5116f..272bc5d445c 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -40,11 +40,6 @@ @{ */ -// Values for the type enum. This reflects the order of the enum declaration -// in the CREATE TABLE command. -//#define TYPE_ENUM_FUNCTION 1 #define TYPE_ENUM_PROCEDURE 2 #define -//TYPE_ENUM_TRIGGER 3 #define TYPE_ENUM_PROXY 4 - Item::Type sp_map_item_type(enum enum_field_types type); @@ -107,39 +102,33 @@ protected: /*************************************************************************/ -class sp_name : public Sql_alloc +class sp_name : public Sql_alloc, + public Database_qualified_name { public: - - LEX_STRING m_db; - LEX_STRING m_name; - LEX_STRING m_qname; bool m_explicit_name; /**< Prepend the db name? */ - sp_name(LEX_STRING db, LEX_STRING name, bool use_explicit_name) - : m_db(db), m_name(name), m_explicit_name(use_explicit_name) + sp_name(const LEX_CSTRING *db, const LEX_CSTRING *name, + bool use_explicit_name) + : Database_qualified_name(db, name), m_explicit_name(use_explicit_name) { if (lower_case_table_names && m_db.str) - m_db.length= my_casedn_str(files_charset_info, m_db.str); - m_qname.str= 0; - m_qname.length= 0; + m_db.length= my_casedn_str(files_charset_info, (char*) m_db.str); } - /** Create temporary sp_name object from MDL key. */ + /** Create temporary sp_name object from MDL key. Store in qname_buff */ sp_name(const MDL_key *key, char *qname_buff); - // Init. the qualified name from the db and name. - void init_qname(THD *thd); // thd for memroot allocation - ~sp_name() {} }; bool -check_routine_name(LEX_STRING *ident); +check_routine_name(const LEX_CSTRING *ident); -class sp_head :private Query_arena +class sp_head :private Query_arena, + public Database_qualified_name { sp_head(const sp_head &); /**< Prevent use of these */ void operator=(sp_head &); @@ -172,28 +161,43 @@ public: b) because in CONTAINS SQL case they don't provide enough information anyway. */ - MODIFIES_DATA= 4096 + MODIFIES_DATA= 4096, + /* + Marks routines that have column type references: DECLARE a t1.a%TYPE; + */ + HAS_COLUMN_TYPE_REFS= 8192 }; - stored_procedure_type m_type; + const Sp_handler *m_handler; uint m_flags; // Boolean attributes of a stored routine Column_definition m_return_field_def; /**< This is used for FUNCTIONs only. */ const char *m_tmp_query; ///< Temporary pointer to sub query string - st_sp_chistics *m_chistics; +private: + /* + Private to guarantee that m_chistics.comment is properly set to: + - a string which is alloced on this->mem_root + - or (NULL,0) + set_chistics() makes sure this. + */ + Sp_chistics m_chistics; +public: sql_mode_t m_sql_mode; ///< For SHOW CREATE and execution - LEX_STRING m_qname; ///< db.name - bool m_explicit_name; ///< Prepend the db name? */ - LEX_STRING m_db; - LEX_STRING m_name; - LEX_STRING m_params; - LEX_STRING m_body; - LEX_STRING m_body_utf8; - LEX_STRING m_defstr; - LEX_STRING m_definer_user; - LEX_STRING m_definer_host; - + bool m_explicit_name; /**< Prepend the db name? */ + LEX_CSTRING m_qname; ///< db.name + LEX_CSTRING m_params; + LEX_CSTRING m_body; + LEX_CSTRING m_body_utf8; + LEX_CSTRING m_defstr; + AUTHID m_definer; + + const st_sp_chistics &chistics() const { return m_chistics; } + const LEX_CSTRING &comment() const { return m_chistics.comment; } + void set_suid(enum_sp_suid_behaviour suid) { m_chistics.suid= suid; } + enum_sp_suid_behaviour suid() const { return m_chistics.suid; } + bool detistic() const { return m_chistics.detistic; } + enum_sp_data_access daccess() const { return m_chistics.daccess; } /** Is this routine being executed? */ @@ -210,6 +214,13 @@ public: { m_sp_cache_version= version_arg; } + + sp_rcontext *rcontext_create(THD *thd, Field *retval, List<Item> *args); + sp_rcontext *rcontext_create(THD *thd, Field *retval, + Item **args, uint arg_count); + sp_rcontext *rcontext_create(THD *thd, Field *retval, + Row_definition_list *list, + bool switch_security_ctx); private: /** Version of the stored routine cache at the moment when the @@ -306,7 +317,7 @@ public: static void operator delete(void *ptr, size_t size) throw (); - sp_head(); + sp_head(const Sp_handler *handler); /// Initialize after we have reset mem_root void @@ -314,7 +325,7 @@ public: /** Copy sp name from parser. */ void - init_sp_name(THD *thd, sp_name *spname); + init_sp_name(const sp_name *spname); /** Set the body-definition start position. */ void @@ -328,8 +339,8 @@ public: bool execute_trigger(THD *thd, - const LEX_STRING *db_name, - const LEX_STRING *table_name, + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name, GRANT_INFO *grant_info); bool @@ -339,14 +350,174 @@ public: execute_procedure(THD *thd, List<Item> *args); static void - show_create_routine_get_fields(THD *thd, int type, List<Item> *fields); + show_create_routine_get_fields(THD *thd, const Sp_handler *sph, + List<Item> *fields); bool - show_create_routine(THD *thd, int type); + show_create_routine(THD *thd, const Sp_handler *sph); + + MEM_ROOT *get_main_mem_root() { return &main_mem_root; } int add_instr(sp_instr *instr); + bool + add_instr_jump(THD *thd, sp_pcontext *spcont); + + bool + add_instr_jump(THD *thd, sp_pcontext *spcont, uint dest); + + bool + add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont, + sp_label *lab); + bool + add_instr_jump_forward_with_backpatch(THD *thd, sp_pcontext *spcont) + { + return add_instr_jump_forward_with_backpatch(thd, spcont, + spcont->last_label()); + } + + bool + add_instr_freturn(THD *thd, sp_pcontext *spcont, Item *item, LEX *lex); + + bool + add_instr_preturn(THD *thd, sp_pcontext *spcont); + + Item *adjust_assignment_source(THD *thd, Item *val, Item *val2); + /** + @param thd - the current thd + @param spcont - the current parse context + @param spv - the SP variable + @param val - the value to be assigned to the variable + @param lex - the LEX that was used to create "val" + @param responsible_to_free_lex - if the generated sp_instr_set should + free "lex". + @retval true - on error + @retval false - on success + */ + bool set_local_variable(THD *thd, sp_pcontext *spcont, + sp_variable *spv, Item *val, LEX *lex, + bool responsible_to_free_lex); + bool set_local_variable_row_field(THD *thd, sp_pcontext *spcont, + sp_variable *spv, uint field_idx, + Item *val, LEX *lex); + bool set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont, + sp_variable *spv, + const LEX_CSTRING *field_name, + Item *val, LEX *lex); +private: + /** + Generate a code to set a single cursor parameter variable. + @param thd - current thd, for mem_root allocations. + @param param_spcont - the context of the parameter block + @param idx - the index of the parameter + @param prm - the actual parameter (contains information about + the assignment source expression Item, + its free list, and its LEX) + */ + bool add_set_cursor_param_variable(THD *thd, + sp_pcontext *param_spcont, uint idx, + sp_assignment_lex *prm) + { + DBUG_ASSERT(idx < param_spcont->context_var_count()); + sp_variable *spvar= param_spcont->get_context_variable(idx); + /* + add_instr() gets free_list from m_thd->free_list. + Initialize it before the set_local_variable() call. + */ + DBUG_ASSERT(m_thd->free_list == NULL); + m_thd->free_list= prm->get_free_list(); + if (set_local_variable(thd, param_spcont, spvar, prm->get_item(), prm, true)) + return true; + /* + Safety: + The item and its free_list are now fully owned by the sp_instr_set + instance, created by set_local_variable(). The sp_instr_set instance + is now responsible for freeing the item and the free_list. + Reset the "item" and the "free_list" members of "prm", + to avoid double pointers to the same objects from "prm" and + from the sp_instr_set instance. + */ + prm->set_item_and_free_list(NULL, NULL); + return false; + } + + /** + Generate a code to set all cursor parameter variables. + This method is called only when parameters exists, + and the number of formal parameters matches the number of actual + parameters. See also comments to add_open_cursor(). + */ + bool add_set_cursor_param_variables(THD *thd, sp_pcontext *param_spcont, + List<sp_assignment_lex> *parameters) + { + DBUG_ASSERT(param_spcont->context_var_count() == parameters->elements); + sp_assignment_lex *prm; + List_iterator<sp_assignment_lex> li(*parameters); + for (uint idx= 0; (prm= li++); idx++) + { + if (add_set_cursor_param_variable(thd, param_spcont, idx, prm)) + return true; + } + return false; + } + + /** + Generate a code to set all cursor parameter variables for a FOR LOOP, e.g.: + FOR index IN cursor(1,2,3) + @param + */ + bool add_set_for_loop_cursor_param_variables(THD *thd, + sp_pcontext *param_spcont, + sp_assignment_lex *param_lex, + Item_args *parameters); + +public: + /** + Generate a code for an "OPEN cursor" statement. + @param thd - current thd, for mem_root allocations + @param spcont - the context of the cursor + @param offset - the offset of the cursor + @param param_spcont - the context of the cursor parameter block + @param parameters - the list of the OPEN actual parameters + + The caller must make sure that the number of local variables + in "param_spcont" (formal parameters) matches the number of list elements + in "parameters" (actual parameters). + NULL in either of them means 0 parameters. + */ + bool add_open_cursor(THD *thd, sp_pcontext *spcont, + uint offset, + sp_pcontext *param_spcont, + List<sp_assignment_lex> *parameters); + + /** + Generate an initiation code for a CURSOR FOR LOOP, e.g.: + FOR index IN cursor -- cursor without parameters + FOR index IN cursor(1,2,3) -- cursor with parameters + + The code generated by this method does the following during SP run-time: + - Sets all cursor parameter vartiables from "parameters" + - Initializes the index ROW-type variable from the cursor + (the structure is copied from the cursor to the index variable) + - The cursor gets opened + - The first records is fetched from the cursor to the variable "index". + + @param thd - the current thread (for mem_root and error reporting) + @param spcont - the current parse context + @param index - the loop "index" ROW-type variable + @param pcursor - the cursor + @param coffset - the cursor offset + @param param_lex - the LEX that owns Items in "parameters" + @param parameters - the cursor parameters Item array + @retval true - on error (EOM) + @retval false - on success + */ + bool add_for_loop_open_cursor(THD *thd, sp_pcontext *spcont, + sp_variable *index, + const sp_pcursor *pcursor, uint coffset, + sp_assignment_lex *param_lex, + Item_args *parameters); /** Returns true if any substatement in the routine directly (not through another routine) modifies data/changes table. @@ -368,6 +539,8 @@ public: return i; } + bool replace_instr_to_nop(THD *thd, uint ip); + /* Resets lex in 'thd' and keeps a copy of the old one. @@ -376,6 +549,23 @@ public: bool reset_lex(THD *thd); + bool + reset_lex(THD *thd, sp_lex_local *sublex); + + /** + Merge two LEX instances. + @param oldlex - the upper level LEX we're going to restore to. + @param sublex - the local lex that have just parsed some substatement. + @returns - false on success, true on error (e.g. failed to + merge the routine list or the table list). + This method is shared by: + - restore_lex(), when the old LEX is popped by sp_head::m_lex.pop() + - THD::restore_from_local_lex_to_old_lex(), when the old LEX + is stored in the caller's local variable. + */ + bool + merge_lex(THD *thd, LEX *oldlex, LEX *sublex); + /** Restores lex in 'thd' from our copy, but keeps some status from the one in 'thd', like ptr, tables, fields, etc. @@ -383,16 +573,40 @@ public: @todo Conflicting comment in sp_head.cc */ bool - restore_lex(THD *thd); + restore_lex(THD *thd) + { + DBUG_ENTER("sp_head::restore_lex"); + LEX *oldlex= (LEX *) m_lex.pop(); + if (!oldlex) + DBUG_RETURN(false); // Nothing to restore + LEX *sublex= thd->lex; + if (thd->restore_from_local_lex_to_old_lex(oldlex))// This restores thd->lex + DBUG_RETURN(true); + if (!sublex->sp_lex_in_use) + { + sublex->sphead= NULL; + lex_end(sublex); + delete sublex; + } + DBUG_RETURN(false); + } /// Put the instruction on the backpatch list, associated with the label. int push_backpatch(THD *thd, sp_instr *, sp_label *); + int + push_backpatch_goto(THD *thd, sp_pcontext *ctx, sp_label *lab); /// Update all instruction with this label in the backpatch list to /// the current position. void backpatch(sp_label *); + void + backpatch_goto(THD *thd, sp_label *, sp_label *); + + /// Check for unresolved goto label + bool + check_unresolved_goto(); /// Start a new cont. backpatch level. If 'i' is NULL, the level is just incr. int @@ -406,26 +620,119 @@ public: void do_cont_backpatch(); - char *name(uint *lenp = 0) const - { - if (lenp) - *lenp= (uint) m_name.length; - return m_name.str; - } + /// Add cpush instructions for all cursors declared in the current frame + bool sp_add_instr_cpush_for_cursors(THD *thd, sp_pcontext *pcontext); + + const LEX_CSTRING *name() const + { return &m_name; } char *create_string(THD *thd, ulong *lenp); - Field *create_result_field(uint field_max_length, const char *field_name, - TABLE *table); + Field *create_result_field(uint field_max_length, const LEX_CSTRING *field_name, + TABLE *table) const; + + + /** + Check and prepare an instance of Column_definition for field creation + (fill all necessary attributes), for variables, parameters and + function return values. + + @param[in] thd Thread handle + @param[in] lex Yacc parsing context + @param[out] field_def An instance of create_field to be filled - bool fill_field_definition(THD *thd, LEX *lex, - Column_definition *field_def); + @retval false on success + @retval true on error + */ + bool fill_field_definition(THD *thd, Column_definition *field_def) + { + const Type_handler *h= field_def->type_handler(); + return h->Column_definition_fix_attributes(field_def) || + field_def->sp_prepare_create_field(thd, mem_root); + } + bool row_fill_field_definitions(THD *thd, Row_definition_list *row) + { + /* + Prepare all row fields. This will (among other things) + - convert VARCHAR lengths from character length to octet length + - calculate interval lengths for SET and ENUM + */ + List_iterator<Spvar_definition> it(*row); + for (Spvar_definition *def= it++; def; def= it++) + { + if (fill_spvar_definition(thd, def)) + return true; + } + return false; + } + /** + Check and prepare a Column_definition for a variable or a parameter. + */ + bool fill_spvar_definition(THD *thd, Column_definition *def) + { + if (fill_field_definition(thd, def)) + return true; + def->pack_flag|= FIELDFLAG_MAYBE_NULL; + return false; + } + bool fill_spvar_definition(THD *thd, Column_definition *def, + LEX_CSTRING *name) + { + def->field_name= *name; + return fill_spvar_definition(thd, def); + } + +private: + /** + Set a column type reference for a parameter definition + */ + void fill_spvar_using_type_reference(sp_variable *spvar, + Qualified_column_ident *ref) + { + spvar->field_def.set_column_type_ref(ref); + spvar->field_def.field_name= spvar->name; + m_flags|= sp_head::HAS_COLUMN_TYPE_REFS; + } + + void fill_spvar_using_table_rowtype_reference(THD *thd, + sp_variable *spvar, + Table_ident *ref) + { + spvar->field_def.set_table_rowtype_ref(ref); + spvar->field_def.field_name= spvar->name; + fill_spvar_definition(thd, &spvar->field_def); + m_flags|= sp_head::HAS_COLUMN_TYPE_REFS; + } +public: + bool spvar_fill_row(THD *thd, sp_variable *spvar, Row_definition_list *def); + bool spvar_fill_type_reference(THD *thd, sp_variable *spvar, + const LEX_CSTRING &table, + const LEX_CSTRING &column); + bool spvar_fill_type_reference(THD *thd, sp_variable *spvar, + const LEX_CSTRING &db, + const LEX_CSTRING &table, + const LEX_CSTRING &column); + bool spvar_fill_table_rowtype_reference(THD *thd, sp_variable *spvar, + const LEX_CSTRING &table); + bool spvar_fill_table_rowtype_reference(THD *thd, sp_variable *spvar, + const LEX_CSTRING &db, + const LEX_CSTRING &table); + + void set_chistics(const st_sp_chistics &chistics); void set_info(longlong created, longlong modified, - st_sp_chistics *chistics, sql_mode_t sql_mode); + const st_sp_chistics &chistics, sql_mode_t sql_mode); - void set_definer(const char *definer, uint definerlen); - void set_definer(const LEX_STRING *user_name, const LEX_STRING *host_name); + void set_definer(const char *definer, uint definerlen) + { + AUTHID tmp; + tmp.parse(definer, definerlen); + m_definer.copy(mem_root, &tmp.user, &tmp.host); + } + void set_definer(const LEX_CSTRING *user_name, const LEX_CSTRING *host_name) + { + m_definer.copy(mem_root, user_name, host_name); + } void reset_thd_mem_root(THD *thd); @@ -445,8 +752,6 @@ public: */ void add_mark_lead(uint ip, List<sp_instr> *leads); - void recursion_level_error(THD *thd); - inline sp_instr * get_instr(uint i) { @@ -511,7 +816,7 @@ public: DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x", prelocking_ctx->get_stmt_unsafe_flags())); DBUG_PRINT("info", ("sp_head(0x%p=%s)->unsafe_flags: 0x%x", - this, name(), unsafe_flags)); + this, name()->str, unsafe_flags)); prelocking_ctx->set_stmt_unsafe_flags(unsafe_flags); DBUG_VOID_RETURN; } @@ -520,6 +825,8 @@ public: void set_select_number(uint num) { m_select_number= num; } + bool check_execute_access(THD *thd) const; + private: MEM_ROOT *m_thd_root; ///< Temp. store for thd's mem_root @@ -528,12 +835,17 @@ private: sp_pcontext *m_pcont; ///< Parse context List<LEX> m_lex; ///< Temp. store for the other lex DYNAMIC_ARRAY m_instr; ///< The "instructions" + + enum backpatch_instr_type { GOTO, CPOP, HPOP }; typedef struct { sp_label *lab; sp_instr *instr; + backpatch_instr_type instr_type; } bp_t; List<bp_t> m_backpatch; ///< Instructions needing backpatching + List<bp_t> m_backpatch_goto; // Instructions needing backpatching (for goto) + /** We need a special list for backpatching of instructions with a continue destination (in the case of a continue handler catching an error in @@ -571,9 +883,55 @@ private: by routine. */ bool merge_table_list(THD *thd, TABLE_LIST *table, LEX *lex_for_tmp_check); + + /// Put the instruction on the a backpatch list, associated with the label. + int + push_backpatch(THD *thd, sp_instr *, sp_label *, List<bp_t> *list, + backpatch_instr_type itype); + }; // class sp_head : public Sql_alloc +class sp_lex_cursor: public sp_lex_local, public Query_arena +{ + LEX_CSTRING m_cursor_name; +public: + sp_lex_cursor(THD *thd, const LEX *oldlex, MEM_ROOT *mem_root_arg) + :sp_lex_local(thd, oldlex), + Query_arena(mem_root_arg, STMT_INITIALIZED_FOR_SP), + m_cursor_name(null_clex_str) + { } + sp_lex_cursor(THD *thd, const LEX *oldlex) + :sp_lex_local(thd, oldlex), + Query_arena(thd->lex->sphead->get_main_mem_root(), STMT_INITIALIZED_FOR_SP) + { } + ~sp_lex_cursor() { free_items(); } + void cleanup_stmt() { } + Query_arena *query_arena() { return this; } + bool validate() + { + DBUG_ASSERT(sql_command == SQLCOM_SELECT); + if (result) + { + my_error(ER_SP_BAD_CURSOR_SELECT, MYF(0)); + return true; + } + return false; + } + bool stmt_finalize(THD *thd) + { + if (validate()) + return true; + sp_lex_in_use= true; + free_list= thd->free_list; + thd->free_list= NULL; + return false; + } + const LEX_CSTRING *cursor_name() const { return &m_cursor_name; } + void set_cursor_name(const LEX_CSTRING *name) { m_cursor_name= *name; } +}; + + // // "Instructions"... // @@ -588,6 +946,7 @@ public: uint marked; uint m_ip; ///< My index sp_pcontext *m_ctx; ///< My parse context + uint m_lineno; /// Should give each a name or type code for debugging purposes? sp_instr(uint ip, sp_pcontext *ctx) @@ -731,6 +1090,9 @@ public: int reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables, sp_instr* instr); + int cursor_reset_lex_and_exec_core(THD *thd, uint *nextp, bool open_tables, + sp_instr *instr); + inline uint sql_command() const { return (uint)m_lex->sql_command; @@ -740,6 +1102,11 @@ public: { m_lex->safe_to_cache_query= 0; } + + const LEX_CSTRING *cursor_name() const + { + return m_lex->cursor_name(); + } private: LEX *m_lex; @@ -813,9 +1180,9 @@ class sp_instr_set : public sp_instr public: sp_instr_set(uint ip, sp_pcontext *ctx, - uint offset, Item *val, enum enum_field_types type_arg, + uint offset, Item *val, LEX *lex, bool lex_resp) - : sp_instr(ip, ctx), m_offset(offset), m_value(val), m_type(type_arg), + : sp_instr(ip, ctx), m_offset(offset), m_value(val), m_lex_keeper(lex, lex_resp) {} @@ -828,16 +1195,87 @@ public: virtual void print(String *str); -private: +protected: uint m_offset; ///< Frame offset Item *m_value; - enum enum_field_types m_type; ///< The declared type sp_lex_keeper m_lex_keeper; }; // class sp_instr_set : public sp_instr +/* + This class handles assignments of a ROW fields: + DECLARE rec ROW (a INT,b INT); + SET rec.a= 10; +*/ +class sp_instr_set_row_field : public sp_instr_set +{ + sp_instr_set_row_field(const sp_instr_set_row_field &); // Prevent use of this + void operator=(sp_instr_set_row_field &); + uint m_field_offset; + +public: + + sp_instr_set_row_field(uint ip, sp_pcontext *ctx, + uint offset, uint field_offset, + Item *val, + LEX *lex, bool lex_resp) + : sp_instr_set(ip, ctx, offset, val, lex, lex_resp), + m_field_offset(field_offset) + {} + + virtual ~sp_instr_set_row_field() + {} + + virtual int exec_core(THD *thd, uint *nextp); + + virtual void print(String *str); +}; // class sp_instr_set_field : public sp_instr_set + + +/** + This class handles assignment instructions like this: + DECLARE + CURSOR cur IS SELECT * FROM t1; + rec cur%ROWTYPE; + BEGIN + rec.column1:= 10; -- This instruction + END; + + The idea is that during sp_rcontext::create() we do not know the extact + structure of "rec". It gets resolved at run time, during the corresponding + sp_instr_cursor_copy_struct::exec_core(). + + So sp_instr_set_row_field_by_name searches for ROW fields by name, + while sp_instr_set_row_field (see above) searches for ROW fields by index. +*/ +class sp_instr_set_row_field_by_name : public sp_instr_set +{ + // Prevent use of this + sp_instr_set_row_field_by_name(const sp_instr_set_row_field &); + void operator=(sp_instr_set_row_field_by_name &); + const LEX_CSTRING m_field_name; + +public: + + sp_instr_set_row_field_by_name(uint ip, sp_pcontext *ctx, + uint offset, const LEX_CSTRING &field_name, + Item *val, + LEX *lex, bool lex_resp) + : sp_instr_set(ip, ctx, offset, val, lex, lex_resp), + m_field_name(field_name) + {} + + virtual ~sp_instr_set_row_field_by_name() + {} + + virtual int exec_core(THD *thd, uint *nextp); + + virtual void print(String *str); +}; // class sp_instr_set_field_by_name : public sp_instr_set + + /** Set NEW/OLD row field value instruction. Used in triggers. */ @@ -1009,6 +1447,41 @@ private: }; // class sp_instr_jump_if_not : public sp_instr_jump +class sp_instr_preturn : public sp_instr +{ + sp_instr_preturn(const sp_instr_preturn &); /**< Prevent use of these */ + void operator=(sp_instr_preturn &); + +public: + + sp_instr_preturn(uint ip, sp_pcontext *ctx) + : sp_instr(ip, ctx) + {} + + virtual ~sp_instr_preturn() + {} + + virtual int execute(THD *thd, uint *nextp) + { + DBUG_ENTER("sp_instr_preturn::execute"); + *nextp= UINT_MAX; + DBUG_RETURN(0); + } + + virtual void print(String *str) + { + str->append(STRING_WITH_LEN("preturn")); + } + + virtual uint opt_mark(sp_head *sp, List<sp_instr> *leads) + { + marked= 1; + return UINT_MAX; + } + +}; // class sp_instr_preturn : public sp_instr + + class sp_instr_freturn : public sp_instr { sp_instr_freturn(const sp_instr_freturn &); /**< Prevent use of these */ @@ -1017,8 +1490,8 @@ class sp_instr_freturn : public sp_instr public: sp_instr_freturn(uint ip, sp_pcontext *ctx, - Item *val, enum enum_field_types type_arg, LEX *lex) - : sp_instr(ip, ctx), m_value(val), m_type(type_arg), + Item *val, const Type_handler *handler, LEX *lex) + : sp_instr(ip, ctx), m_value(val), m_type_handler(handler), m_lex_keeper(lex, TRUE) {} @@ -1040,7 +1513,7 @@ public: protected: Item *m_value; - enum enum_field_types m_type; + const Type_handler *m_type_handler; sp_lex_keeper m_lex_keeper; }; // class sp_instr_freturn : public sp_instr @@ -1127,6 +1600,11 @@ public: virtual ~sp_instr_hpop() {} + void update_count(uint count) + { + m_count= count; + } + virtual int execute(THD *thd, uint *nextp); virtual void print(String *str); @@ -1219,6 +1697,11 @@ public: virtual ~sp_instr_cpop() {} + void update_count(uint count) + { + m_count= count; + } + virtual int execute(THD *thd, uint *nextp); virtual void print(String *str); @@ -1257,6 +1740,30 @@ private: }; // class sp_instr_copen : public sp_instr_stmt +/** + Initialize the structure of a cursor%ROWTYPE variable + from the LEX containing the cursor SELECT statement. +*/ +class sp_instr_cursor_copy_struct: public sp_instr +{ + /**< Prevent use of these */ + sp_instr_cursor_copy_struct(const sp_instr_cursor_copy_struct &); + void operator=(sp_instr_cursor_copy_struct &); + sp_lex_keeper m_lex_keeper; + uint m_var; +public: + sp_instr_cursor_copy_struct(uint ip, sp_pcontext *ctx, + sp_lex_cursor *lex, uint voffs) + : sp_instr(ip, ctx), m_lex_keeper(lex, FALSE), m_var(voffs) + {} + virtual ~sp_instr_cursor_copy_struct() + {} + virtual int execute(THD *thd, uint *nextp); + virtual int exec_core(THD *thd, uint *nextp); + virtual void print(String *str); +}; + + class sp_instr_cclose : public sp_instr { sp_instr_cclose(const sp_instr_cclose &); /**< Prevent use of these */ @@ -1393,8 +1900,7 @@ void sp_restore_security_context(THD *thd, Security_context *backup); bool -set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc, - Security_context **save_ctx); +set_routine_security_ctx(THD *thd, sp_head *sp, Security_context **save_ctx); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ TABLE_LIST * @@ -1404,10 +1910,11 @@ sp_add_to_query_tables(THD *thd, LEX *lex, enum_mdl_type mdl_type); Item * -sp_prepare_func_item(THD* thd, Item **it_addr); +sp_prepare_func_item(THD* thd, Item **it_addr, uint cols= 1); bool -sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr); +sp_eval_expr(THD *thd, Item *result_item, Field *result_field, + Item **expr_item_ptr); /** @} (end of group Stored_Routines) diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 9a6353c9337..d98f8005945 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -27,19 +27,50 @@ bool sp_condition_value::equals(const sp_condition_value *cv) const { DBUG_ASSERT(cv); + /* + The following test disallows duplicate handlers, + including user defined exceptions with the same WHEN clause: + DECLARE + a EXCEPTION; + b EXCEPTION; + BEGIN + RAUSE a; + EXCEPTION + WHEN a THEN RETURN 'a0'; + WHEN a THEN RETURN 'a1'; + END + */ if (this == cv) return true; - if (type != cv->type) + /* + The test below considers two conditions of the same type as equal + (except for the user defined exceptions) to avoid declaring duplicate + handlers. + + All user defined conditions have type==SQLSTATE + with the same SQL state and error code. + It's OK to have multiple user defined conditions: + DECLARE + a EXCEPTION; + b EXCEPTION; + BEGIN + RAISE a; + EXCEPTION + WHEN a THEN RETURN 'a'; + WHEN b THEN RETURN 'b'; + END; + */ + if (type != cv->type || m_is_user_defined || cv->m_is_user_defined) return false; switch (type) { case sp_condition_value::ERROR_CODE: - return (mysqlerr == cv->mysqlerr); + return (get_sql_errno() == cv->get_sql_errno()); case sp_condition_value::SQLSTATE: - return (strcmp(sql_state, cv->sql_state) == 0); + return Sql_state::eq(cv); default: return true; @@ -56,6 +87,7 @@ void sp_pcontext::init(uint var_offset, m_num_case_exprs= num_case_expressions; m_labels.empty(); + m_goto_labels.empty(); } @@ -98,6 +130,12 @@ sp_pcontext *sp_pcontext::push_context(THD *thd, sp_pcontext::enum_scope scope) } +bool cmp_labels(sp_label *a, sp_label *b) +{ + return (my_strcasecmp(system_charset_info, a->name.str, b->name.str) == 0 + && a->type == b->type); +} + sp_pcontext *sp_pcontext::pop_context() { m_parent->m_max_var_index+= m_max_var_index; @@ -109,6 +147,18 @@ sp_pcontext *sp_pcontext::pop_context() if (m_num_case_exprs > m_parent->m_num_case_exprs) m_parent->m_num_case_exprs= m_num_case_exprs; + /* + ** Push unresolved goto label to parent context + */ + sp_label *label; + List_iterator_fast<sp_label> li(m_goto_labels); + while ((label= li++)) + { + if (label->ip == 0) + { + m_parent->m_goto_labels.add_unique(label, &cmp_labels); + } + } return m_parent; } @@ -149,7 +199,7 @@ uint sp_pcontext::diff_cursors(const sp_pcontext *ctx, bool exclusive) const } -sp_variable *sp_pcontext::find_variable(LEX_STRING name, +sp_variable *sp_pcontext::find_variable(const LEX_CSTRING *name, bool current_scope_only) const { uint i= m_vars.elements() - m_pboundary; @@ -159,7 +209,7 @@ sp_variable *sp_pcontext::find_variable(LEX_STRING name, sp_variable *p= m_vars.at(i); if (my_strnncoll(system_charset_info, - (const uchar *)name.str, name.length, + (const uchar *)name->str, name->length, (const uchar *)p->name.str, p->name.length) == 0) { return p; @@ -172,10 +222,46 @@ sp_variable *sp_pcontext::find_variable(LEX_STRING name, } +/* + Find a variable by its run-time offset. + If the variable with a desired run-time offset is not found in this + context frame, it's recursively searched on parent context frames. + + Note, context frames can have holes: + CREATE PROCEDURE p1() AS + x0 INT:=100; + CURSOR cur(p0 INT, p1 INT) IS SELECT p0, p1; + x1 INT:=101; + BEGIN + ... + END; + The variables (x0 and x1) and the cursor parameters (p0 and p1) + reside in separate parse context frames. + + The variables reside on the top level parse context frame: + - x0 has frame offset 0 and run-time offset 0 + - x1 has frame offset 1 and run-time offset 3 + + The cursor parameters reside on the second level parse context frame: + - p0 has frame offset 0 and run-time offset 1 + - p1 has frame offset 1 and run-time offset 2 + + Run-time offsets on a frame can have holes, but offsets monotonocally grow, + so run-time offsets of all variables are not greater than the run-time offset + of the very last variable in this frame. +*/ sp_variable *sp_pcontext::find_variable(uint offset) const { - if (m_var_offset <= offset && offset < m_var_offset + m_vars.elements()) - return m_vars.at(offset - m_var_offset); // This frame + if (m_var_offset <= offset && + m_vars.elements() && + offset <= get_last_context_variable()->offset) + { + for (uint i= 0; i < m_vars.elements(); i++) + { + if (m_vars.at(i)->offset == offset) + return m_vars.at(i); // This frame + } + } return m_parent ? m_parent->find_variable(offset) : // Some previous frame @@ -183,10 +269,10 @@ sp_variable *sp_pcontext::find_variable(uint offset) const } -sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name) +sp_variable *sp_pcontext::add_variable(THD *thd, const LEX_CSTRING *name) { sp_variable *p= - new (thd->mem_root) sp_variable(name, current_var_count()); + new (thd->mem_root) sp_variable(name, m_var_offset + m_max_var_index); if (!p) return NULL; @@ -196,29 +282,66 @@ sp_variable *sp_pcontext::add_variable(THD *thd, LEX_STRING name) return m_vars.append(p) ? NULL : p; } - -sp_label *sp_pcontext::push_label(THD *thd, LEX_STRING name, uint ip) +sp_label *sp_pcontext::push_label(THD *thd, const LEX_CSTRING *name, uint ip, + sp_label::enum_type type, + List<sp_label> *list) { sp_label *label= - new (thd->mem_root) sp_label(name, ip, sp_label::IMPLICIT, this); + new (thd->mem_root) sp_label(name, ip, type, this); if (!label) return NULL; - m_labels.push_front(label, thd->mem_root); + list->push_front(label, thd->mem_root); return label; } +sp_label *sp_pcontext::find_goto_label(const LEX_CSTRING *name, bool recusive) +{ + List_iterator_fast<sp_label> li(m_goto_labels); + sp_label *lab; + + while ((lab= li++)) + { + if (my_strcasecmp(system_charset_info, name->str, lab->name.str) == 0) + return lab; + } -sp_label *sp_pcontext::find_label(LEX_STRING name) + if (!recusive) + return NULL; + + /* + Note about exception handlers. + See SQL:2003 SQL/PSM (ISO/IEC 9075-4:2003), + section 13.1 <compound statement>, + syntax rule 4. + In short, a DECLARE HANDLER block can not refer + to labels from the parent context, as they are out of scope. + */ + if (m_scope == HANDLER_SCOPE && m_parent) + { + if (m_parent->m_parent) + { + // Skip the parent context + return m_parent->m_parent->find_goto_label(name); + } + } + + return m_parent && (m_scope == REGULAR_SCOPE) ? + m_parent->find_goto_label(name) : + NULL; +} + + +sp_label *sp_pcontext::find_label(const LEX_CSTRING *name) { List_iterator_fast<sp_label> li(m_labels); sp_label *lab; while ((lab= li++)) { - if (my_strcasecmp(system_charset_info, name.str, lab->name.str) == 0) + if (my_strcasecmp(system_charset_info, name->str, lab->name.str) == 0) return lab; } @@ -236,8 +359,25 @@ sp_label *sp_pcontext::find_label(LEX_STRING name) } +sp_label *sp_pcontext::find_label_current_loop_start() +{ + List_iterator_fast<sp_label> li(m_labels); + sp_label *lab; + + while ((lab= li++)) + { + if (lab->type == sp_label::ITERATION) + return lab; + } + // See a comment in sp_pcontext::find_label() + return (m_parent && (m_scope == REGULAR_SCOPE)) ? + m_parent->find_label_current_loop_start() : + NULL; +} + + bool sp_pcontext::add_condition(THD *thd, - LEX_STRING name, + const LEX_CSTRING *name, sp_condition_value *value) { sp_condition *p= new (thd->mem_root) sp_condition(name, value); @@ -249,7 +389,7 @@ bool sp_pcontext::add_condition(THD *thd, } -sp_condition_value *sp_pcontext::find_condition(LEX_STRING name, +sp_condition_value *sp_pcontext::find_condition(const LEX_CSTRING *name, bool current_scope_only) const { uint i= m_conditions.elements(); @@ -258,9 +398,7 @@ sp_condition_value *sp_pcontext::find_condition(LEX_STRING name, { sp_condition *p= m_conditions.at(i); - if (my_strnncoll(system_charset_info, - (const uchar *) name.str, name.length, - (const uchar *) p->name.str, p->name.length) == 0) + if (p->eq_name(name)) { return p->value; } @@ -272,6 +410,38 @@ sp_condition_value *sp_pcontext::find_condition(LEX_STRING name, } +static sp_condition_value + // Warnings + cond_no_data_found(ER_SP_FETCH_NO_DATA, "01000"), + // Errors + cond_invalid_cursor(ER_SP_CURSOR_NOT_OPEN, "24000"), + cond_dup_val_on_index(ER_DUP_ENTRY, "23000"), + cond_too_many_rows(ER_TOO_MANY_ROWS, "42000"); + + +static sp_condition sp_predefined_conditions[]= +{ + // Warnings + sp_condition(C_STRING_WITH_LEN("NO_DATA_FOUND"), &cond_no_data_found), + // Errors + sp_condition(C_STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor), + sp_condition(C_STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index), + sp_condition(C_STRING_WITH_LEN("TOO_MANY_ROWS"), &cond_too_many_rows) +}; + + +sp_condition_value * +sp_pcontext::find_predefined_condition(const LEX_CSTRING *name) const +{ + for (uint i= 0; i < array_elements(sp_predefined_conditions) ; i++) + { + if (sp_predefined_conditions[i].eq_name(name)) + return sp_predefined_conditions[i].value; + } + return NULL; +} + + sp_handler *sp_pcontext::add_handler(THD *thd, sp_handler::enum_type type) { @@ -305,10 +475,57 @@ bool sp_pcontext::check_duplicate_handler( } +bool sp_condition_value::matches(const Sql_condition_identity &value, + const sp_condition_value *found_cv) const +{ + bool user_value_matched= !value.get_user_condition_value() || + this == value.get_user_condition_value(); + + switch (type) + { + case sp_condition_value::ERROR_CODE: + return user_value_matched && + value.get_sql_errno() == get_sql_errno() && + (!found_cv || found_cv->type > sp_condition_value::ERROR_CODE); + + case sp_condition_value::SQLSTATE: + return user_value_matched && + Sql_state::eq(&value) && + (!found_cv || found_cv->type > sp_condition_value::SQLSTATE); + + case sp_condition_value::WARNING: + return user_value_matched && + (value.Sql_state::is_warning() || + value.get_level() == Sql_condition::WARN_LEVEL_WARN) && + !found_cv; + + case sp_condition_value::NOT_FOUND: + return user_value_matched && + value.Sql_state::is_not_found() && + !found_cv; + + case sp_condition_value::EXCEPTION: + /* + In sql_mode=ORACLE this construct should catch both errors and warnings: + EXCEPTION + WHEN OTHERS THEN ...; + E.g. NO_DATA_FOUND is more like a warning than an error, + and it should be caught. + + We don't check user_value_matched here. + "WHEN OTHERS" catches all user defined exception. + */ + return (((current_thd->variables.sql_mode & MODE_ORACLE) || + (value.Sql_state::is_exception() && + value.get_level() == Sql_condition::WARN_LEVEL_ERROR)) && + !found_cv); + } + return false; +} + + sp_handler* -sp_pcontext::find_handler(const char *sql_state, - uint sql_errno, - Sql_condition::enum_warning_level level) const +sp_pcontext::find_handler(const Sql_condition_identity &value) const { sp_handler *found_handler= NULL; sp_condition_value *found_cv= NULL; @@ -322,53 +539,10 @@ sp_pcontext::find_handler(const char *sql_state, while ((cv= li++)) { - switch (cv->type) + if (cv->matches(value, found_cv)) { - case sp_condition_value::ERROR_CODE: - if (sql_errno == cv->mysqlerr && - (!found_cv || - found_cv->type > sp_condition_value::ERROR_CODE)) - { - found_cv= cv; - found_handler= h; - } - break; - - case sp_condition_value::SQLSTATE: - if (strcmp(sql_state, cv->sql_state) == 0 && - (!found_cv || - found_cv->type > sp_condition_value::SQLSTATE)) - { - found_cv= cv; - found_handler= h; - } - break; - - case sp_condition_value::WARNING: - if ((is_sqlstate_warning(sql_state) || - level == Sql_condition::WARN_LEVEL_WARN) && !found_cv) - { - found_cv= cv; - found_handler= h; - } - break; - - case sp_condition_value::NOT_FOUND: - if (is_sqlstate_not_found(sql_state) && !found_cv) - { - found_cv= cv; - found_handler= h; - } - break; - - case sp_condition_value::EXCEPTION: - if (is_sqlstate_exception(sql_state) && - level == Sql_condition::WARN_LEVEL_ERROR && !found_cv) - { - found_cv= cv; - found_handler= h; - } - break; + found_cv= cv; + found_handler= h; } } } @@ -411,64 +585,98 @@ sp_pcontext::find_handler(const char *sql_state, if (!p || !p->m_parent) return NULL; - return p->m_parent->find_handler(sql_state, sql_errno, level); + return p->m_parent->find_handler(value); } -bool sp_pcontext::add_cursor(LEX_STRING name) +bool sp_pcontext::add_cursor(const LEX_CSTRING *name, sp_pcontext *param_ctx, + sp_lex_cursor *lex) { if (m_cursors.elements() == m_max_cursor_index) ++m_max_cursor_index; - return m_cursors.append(name); + return m_cursors.append(sp_pcursor(name, param_ctx, lex)); } -bool sp_pcontext::find_cursor(LEX_STRING name, - uint *poff, - bool current_scope_only) const +const sp_pcursor *sp_pcontext::find_cursor(const LEX_CSTRING *name, + uint *poff, + bool current_scope_only) const { uint i= m_cursors.elements(); while (i--) { - LEX_STRING n= m_cursors.at(i); + LEX_CSTRING n= m_cursors.at(i); if (my_strnncoll(system_charset_info, - (const uchar *) name.str, name.length, + (const uchar *) name->str, name->length, (const uchar *) n.str, n.length) == 0) { *poff= m_cursor_offset + i; - return true; + return &m_cursors.at(i); } } return (!current_scope_only && m_parent) ? m_parent->find_cursor(name, poff, false) : - false; + NULL; } void sp_pcontext::retrieve_field_definitions( - List<Column_definition> *field_def_lst) const + List<Spvar_definition> *field_def_lst) const { /* Put local/context fields in the result list. */ + size_t next_child= 0; for (size_t i= 0; i < m_vars.elements(); ++i) { sp_variable *var_def= m_vars.at(i); + /* + The context can have holes in run-time offsets, + the missing offsets reside on the children contexts in such cases. + Example: + CREATE PROCEDURE p1() AS + x0 INT:=100; -- context 0, position 0, run-time 0 + CURSOR cur( + p0 INT, -- context 1, position 0, run-time 1 + p1 INT -- context 1, position 1, run-time 2 + ) IS SELECT p0, p1; + x1 INT:=101; -- context 0, position 1, run-time 3 + BEGIN + ... + END; + See more comments in sp_pcontext::find_variable(). + We must retrieve the definitions in the order of their run-time offsets. + Check that there are children that should go before the current variable. + */ + for ( ; next_child < m_children.elements(); next_child++) + { + sp_pcontext *child= m_children.at(next_child); + if (!child->context_var_count() || + child->get_context_variable(0)->offset > var_def->offset) + break; + /* + All variables on the embedded context (that fills holes of the parent) + should have the run-time offset strictly less than var_def. + */ + DBUG_ASSERT(child->get_context_variable(0)->offset < var_def->offset); + DBUG_ASSERT(child->get_last_context_variable()->offset < var_def->offset); + child->retrieve_field_definitions(field_def_lst); + } field_def_lst->push_back(&var_def->field_def); } - /* Put the fields of the enclosed contexts in the result list. */ + /* Put the fields of the remaining enclosed contexts in the result list. */ - for (size_t i= 0; i < m_children.elements(); ++i) + for (size_t i= next_child; i < m_children.elements(); ++i) m_children.at(i)->retrieve_field_definitions(field_def_lst); } -const LEX_STRING *sp_pcontext::find_cursor(uint offset) const +const sp_pcursor *sp_pcontext::find_cursor(uint offset) const { if (m_cursor_offset <= offset && offset < m_cursor_offset + m_cursors.elements()) @@ -480,3 +688,35 @@ const LEX_STRING *sp_pcontext::find_cursor(uint offset) const m_parent->find_cursor(offset) : // Some previous frame NULL; // Index out of bounds } + + +bool sp_pcursor::check_param_count_with_error(uint param_count) const +{ + if (param_count != (m_param_context ? + m_param_context->context_var_count() : 0)) + { + my_error(ER_WRONG_PARAMCOUNT_TO_CURSOR, MYF(0), LEX_CSTRING::str); + return true; + } + return false; +} + + +const Spvar_definition * +sp_variable::find_row_field(const LEX_CSTRING *var_name, + const LEX_CSTRING *field_name, + uint *row_field_offset) +{ + if (!field_def.is_row()) + { + my_printf_error(ER_UNKNOWN_ERROR, + "'%s' is not a row variable", MYF(0), var_name->str); + return NULL; + } + const Spvar_definition *def; + if ((def= field_def.find_row_field_by_name(field_name, row_field_offset))) + return def; + my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0), + var_name->str, field_name->str); + return NULL; +} diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index d4d532340fb..9c879099410 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -41,7 +41,7 @@ public: }; /// Name of the SP-variable. - LEX_STRING name; + LEX_CSTRING name; /// Mode of the SP-variable. enum_mode mode; @@ -57,18 +57,35 @@ public: Item *default_value; /// Full type information (field meta-data) of the SP-variable. - Column_definition field_def; + Spvar_definition field_def; /// Field-type of the SP-variable. - enum_field_types sql_type() const { return field_def.sql_type; } + enum_field_types sql_type() const { return field_def.real_field_type(); } + + const Type_handler *type_handler() const { return field_def.type_handler(); } + public: - sp_variable(LEX_STRING _name, uint _offset) + sp_variable(const LEX_CSTRING *name_arg, uint offset_arg) :Sql_alloc(), - name(_name), + name(*name_arg), mode(MODE_IN), - offset(_offset), + offset(offset_arg), default_value(NULL) { } + /* + Find a ROW field by its qualified name. + @param var_name - the name of the variable + @param field_name - the name of the variable field + @param[OUT] row_field_offset - the index of the field + + @retval NULL if the variable with the given name was not found, + or it is not a row variable, or it does not have a field + with the given name, or a non-null pointer otherwise. + row_field_offset[0] is set only when the method returns !NULL. + */ + const Spvar_definition *find_row_field(const LEX_CSTRING *var_name, + const LEX_CSTRING *field_name, + uint *row_field_offset); }; /////////////////////////////////////////////////////////////////////////// @@ -80,6 +97,7 @@ public: /// IF/WHILE/REPEAT/LOOP, when such statement is rewritten into a /// combination of low-level jump/jump_if instructions and labels. + class sp_label : public Sql_alloc { public: @@ -92,11 +110,14 @@ public: BEGIN, /// Label at iteration control - ITERATION + ITERATION, + + /// Label for jump + GOTO }; /// Name of the label. - LEX_STRING name; + LEX_CSTRING name; /// Instruction pointer of the label. uint ip; @@ -108,15 +129,17 @@ public: class sp_pcontext *ctx; public: - sp_label(LEX_STRING _name, uint _ip, enum_type _type, sp_pcontext *_ctx) + sp_label(const LEX_CSTRING *_name, + uint _ip, enum_type _type, sp_pcontext *_ctx) :Sql_alloc(), - name(_name), + name(*_name), ip(_ip), type(_type), ctx(_ctx) { } }; + /////////////////////////////////////////////////////////////////////////// /// This class represents condition-value term in DECLARE CONDITION or @@ -126,8 +149,9 @@ public: /// In some sense, this class is a union -- a set of filled attributes /// depends on the sp_condition_value::type value. -class sp_condition_value : public Sql_alloc +class sp_condition_value : public Sql_alloc, public Sql_state_errno { + bool m_is_user_defined; public: enum enum_type { @@ -141,29 +165,31 @@ public: /// Type of the condition value. enum_type type; - /// SQLSTATE of the condition value. - char sql_state[SQLSTATE_LENGTH+1]; - - /// MySQL error code of the condition value. - uint mysqlerr; - public: sp_condition_value(uint _mysqlerr) :Sql_alloc(), - type(ERROR_CODE), - mysqlerr(_mysqlerr) + Sql_state_errno(_mysqlerr), + m_is_user_defined(false), + type(ERROR_CODE) { } - sp_condition_value(const char *_sql_state) + sp_condition_value(uint _mysqlerr, const char *_sql_state) :Sql_alloc(), + Sql_state_errno(_mysqlerr, _sql_state), + m_is_user_defined(false), + type(ERROR_CODE) + { } + + sp_condition_value(const char *_sql_state, bool is_user_defined= false) + :Sql_alloc(), + Sql_state_errno(0, _sql_state), + m_is_user_defined(is_user_defined), type(SQLSTATE) - { - memcpy(sql_state, _sql_state, SQLSTATE_LENGTH); - sql_state[SQLSTATE_LENGTH]= 0; - } + { } sp_condition_value(enum_type _type) :Sql_alloc(), + m_is_user_defined(false), type(_type) { DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE); @@ -175,8 +201,39 @@ public: /// /// @return true if the instances are equal, false otherwise. bool equals(const sp_condition_value *cv) const; + + + /** + Checks if this condition is OK for search. + See also sp_context::find_handler(). + + @param identity - The condition identity + @param found_cv - A previously found matching condition or NULL. + @return true - If the current value matches identity and + makes a stronger match than the previously + found condition found_cv. + @return false - If the current value does not match identity, + of the current value makes a weaker match than found_cv. + */ + bool matches(const Sql_condition_identity &identity, + const sp_condition_value *found_cv) const; + + Sql_user_condition_identity get_user_condition_identity() const + { + return Sql_user_condition_identity(m_is_user_defined ? this : NULL); + } }; + +class sp_condition_value_user_defined: public sp_condition_value +{ +public: + sp_condition_value_user_defined() + :sp_condition_value("45000", true) + { } +}; + + /////////////////////////////////////////////////////////////////////////// /// This class represents 'DECLARE CONDITION' statement. @@ -186,19 +243,66 @@ class sp_condition : public Sql_alloc { public: /// Name of the condition. - LEX_STRING name; + LEX_CSTRING name; /// Value of the condition. sp_condition_value *value; public: - sp_condition(LEX_STRING _name, sp_condition_value *_value) + sp_condition(const LEX_CSTRING *name_arg, sp_condition_value *value_arg) :Sql_alloc(), - name(_name), - value(_value) + name(*name_arg), + value(value_arg) { } + sp_condition(const char *name_arg, size_t name_length_arg, + sp_condition_value *value_arg) + :value(value_arg) + { + name.str= name_arg; + name.length= name_length_arg; + } + bool eq_name(const LEX_CSTRING *str) const + { + return my_strnncoll(system_charset_info, + (const uchar *) name.str, name.length, + (const uchar *) str->str, str->length) == 0; + } }; + +/////////////////////////////////////////////////////////////////////////// + +/** + class sp_pcursor. + Stores information about a cursor: + - Cursor's name in LEX_STRING. + - Cursor's formal parameter descriptions. + + Formal parameter descriptions reside in a separate context block, + pointed by the "m_param_context" member. + + m_param_context can be NULL. This means a cursor with no parameters. + Otherwise, the number of variables in m_param_context means + the number of cursor's formal parameters. + + Note, m_param_context can be not NULL, but have no variables. + This is also means a cursor with no parameters (similar to NULL). +*/ +class sp_pcursor: public LEX_CSTRING +{ + class sp_pcontext *m_param_context; // Formal parameters + class sp_lex_cursor *m_lex; // The cursor statement LEX +public: + sp_pcursor(const LEX_CSTRING *name, class sp_pcontext *param_ctx, + class sp_lex_cursor *lex) + :LEX_CSTRING(*name), m_param_context(param_ctx), m_lex(lex) + { } + class sp_pcontext *param_context() const { return m_param_context; } + class sp_lex_cursor *lex() const { return m_lex; } + bool check_param_count_with_error(uint param_count) const; +}; + + /////////////////////////////////////////////////////////////////////////// /// This class represents 'DECLARE HANDLER' statement. @@ -267,6 +371,12 @@ public: HANDLER_SCOPE }; + class Lex_for_loop: public Lex_for_loop_st + { + public: + Lex_for_loop() { init(); } + }; + public: sp_pcontext(); ~sp_pcontext(); @@ -329,9 +439,22 @@ public: uint context_var_count() const { return (uint)m_vars.elements(); } - /// @return map index in this parsing context to runtime offset. - uint var_context2runtime(uint i) const - { return m_var_offset + i; } + /// return the i-th variable on the current context + sp_variable *get_context_variable(uint i) const + { + DBUG_ASSERT(i < m_vars.elements()); + return m_vars.at(i); + } + + /* + Return the i-th last context variable. + If i is 0, then return the very last variable in m_vars. + */ + sp_variable *get_last_context_variable(uint i= 0) const + { + DBUG_ASSERT(i < m_vars.elements()); + return m_vars.at(m_vars.elements() - i - 1); + } /// Add SP-variable to the parsing context. /// @@ -339,13 +462,13 @@ public: /// @param name Name of the SP-variable. /// /// @return instance of newly added SP-variable. - sp_variable *add_variable(THD *thd, LEX_STRING name); + sp_variable *add_variable(THD *thd, const LEX_CSTRING *name); /// Retrieve full type information about SP-variables in this parsing /// context and its children. /// /// @param field_def_lst[out] Container to store type information. - void retrieve_field_definitions(List<Column_definition> *field_def_lst) const; + void retrieve_field_definitions(List<Spvar_definition> *field_def_lst) const; /// Find SP-variable by name. /// @@ -358,7 +481,7 @@ public: /// @param current_scope_only A flag if we search only in current scope. /// /// @return instance of found SP-variable, or NULL if not found. - sp_variable *find_variable(LEX_STRING name, bool current_scope_only) const; + sp_variable *find_variable(const LEX_CSTRING *name, bool current_scope_only) const; /// Find SP-variable by the offset in the root parsing context. /// @@ -401,9 +524,31 @@ public: // Labels. ///////////////////////////////////////////////////////////////////////// - sp_label *push_label(THD *thd, LEX_STRING name, uint ip); + sp_label *push_label(THD *thd, const LEX_CSTRING *name, uint ip, + sp_label::enum_type type, List<sp_label> * list); + + sp_label *push_label(THD *thd, const LEX_CSTRING *name, uint ip, + sp_label::enum_type type) + { return push_label(thd, name, ip, type, &m_labels); } - sp_label *find_label(LEX_STRING name); + sp_label *push_goto_label(THD *thd, const LEX_CSTRING *name, uint ip, + sp_label::enum_type type) + { return push_label(thd, name, ip, type, &m_goto_labels); } + + sp_label *push_label(THD *thd, const LEX_CSTRING *name, uint ip) + { return push_label(thd, name, ip, sp_label::IMPLICIT); } + + sp_label *push_goto_label(THD *thd, const LEX_CSTRING *name, uint ip) + { return push_goto_label(thd, name, ip, sp_label::GOTO); } + + sp_label *find_label(const LEX_CSTRING *name); + + sp_label *find_goto_label(const LEX_CSTRING *name, bool recusive); + + sp_label *find_goto_label(const LEX_CSTRING *name) + { return find_goto_label(name, true); } + + sp_label *find_label_current_loop_start(); sp_label *last_label() { @@ -415,19 +560,56 @@ public: return label; } + sp_label *last_goto_label() + { + return m_goto_labels.head(); + } + sp_label *pop_label() { return m_labels.pop(); } + bool block_label_declare(LEX_CSTRING *label) + { + sp_label *lab= find_label(label); + if (lab) + { + my_error(ER_SP_LABEL_REDEFINE, MYF(0), label->str); + return true; + } + return false; + } + ///////////////////////////////////////////////////////////////////////// // Conditions. ///////////////////////////////////////////////////////////////////////// - bool add_condition(THD *thd, LEX_STRING name, sp_condition_value *value); + bool add_condition(THD *thd, const LEX_CSTRING *name, + sp_condition_value *value); /// See comment for find_variable() above. - sp_condition_value *find_condition(LEX_STRING name, + sp_condition_value *find_condition(const LEX_CSTRING *name, bool current_scope_only) const; + sp_condition_value * + find_declared_or_predefined_condition(const LEX_CSTRING *name) const + { + sp_condition_value *p= find_condition(name, false); + if (p) + return p; + return find_predefined_condition(name); + } + + bool declare_condition(THD *thd, const LEX_CSTRING *name, + sp_condition_value *val) + { + if (find_condition(name, true)) + { + my_error(ER_SP_DUP_COND, MYF(0), name->str); + return true; + } + return add_condition(thd, name, val); + } + ///////////////////////////////////////////////////////////////////////// // Handlers. ///////////////////////////////////////////////////////////////////////// @@ -454,26 +636,46 @@ public: /// Find an SQL handler for the given SQL condition according to the /// SQL-handler resolution rules. This function is used at runtime. /// - /// @param sql_state The SQL condition state - /// @param sql_errno The error code + /// @param value The error code and the SQL state /// @param level The SQL condition level /// /// @return a pointer to the found SQL-handler or NULL. - sp_handler *find_handler(const char *sql_state, - uint sql_errno, - Sql_condition::enum_warning_level level) const; + sp_handler *find_handler(const Sql_condition_identity &identity) const; ///////////////////////////////////////////////////////////////////////// // Cursors. ///////////////////////////////////////////////////////////////////////// - bool add_cursor(LEX_STRING name); + bool add_cursor(const LEX_CSTRING *name, sp_pcontext *param_ctx, + class sp_lex_cursor *lex); /// See comment for find_variable() above. - bool find_cursor(LEX_STRING name, uint *poff, bool current_scope_only) const; + const sp_pcursor *find_cursor(const LEX_CSTRING *name, + uint *poff, bool current_scope_only) const; + + const sp_pcursor *find_cursor_with_error(const LEX_CSTRING *name, + uint *poff, + bool current_scope_only) const + { + const sp_pcursor *pcursor= find_cursor(name, poff, current_scope_only); + if (!pcursor) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), name->str); + return NULL; + } + return pcursor; + } + /// Find cursor by offset (for SHOW {PROCEDURE|FUNCTION} CODE only). + const sp_pcursor *find_cursor(uint offset) const; + + const sp_pcursor *get_cursor_by_local_frame_offset(uint offset) const + { return &m_cursors.at(offset); } + + uint cursor_offset() const + { return m_cursor_offset; } - /// Find cursor by offset (for debugging only). - const LEX_STRING *find_cursor(uint offset) const; + uint frame_cursor_count() const + { return m_cursors.elements(); } uint max_cursor_index() const { return m_max_cursor_index + (uint)m_cursors.elements(); } @@ -481,6 +683,15 @@ public: uint current_cursor_count() const { return m_cursor_offset + (uint)m_cursors.elements(); } + void set_for_loop(const Lex_for_loop_st &for_loop) + { + m_for_loop.init(for_loop); + } + const Lex_for_loop_st &for_loop() + { + return m_for_loop; + } + private: /// Constructor for a tree node. /// @param prev the parent parsing context @@ -493,6 +704,8 @@ private: sp_pcontext(const sp_pcontext &); void operator=(sp_pcontext &); + sp_condition_value *find_predefined_condition(const LEX_CSTRING *name) const; + private: /// m_max_var_index -- number of variables (including all types of arguments) /// in this context including all children contexts. @@ -538,19 +751,41 @@ private: Dynamic_array<sp_condition *> m_conditions; /// Stack of cursors. - Dynamic_array<LEX_STRING> m_cursors; + Dynamic_array<sp_pcursor> m_cursors; /// Stack of SQL-handlers. Dynamic_array<sp_handler *> m_handlers; - /// List of labels. + /* + In the below example the label <<lab>> has two meanings: + - GOTO lab : must go before the beginning of the loop + - CONTINUE lab : must go to the beginning of the loop + We solve this by storing block labels and goto labels into separate lists. + + BEGIN + <<lab>> + FOR i IN a..10 LOOP + ... + GOTO lab; + ... + CONTINUE lab; + ... + END LOOP; + END; + */ + /// List of block labels List<sp_label> m_labels; + /// List of goto labels + List<sp_label> m_goto_labels; /// Children contexts, used for destruction. Dynamic_array<sp_pcontext *> m_children; /// Scope of this parsing context. enum_scope m_scope; + + /// FOR LOOP characteristics + Lex_for_loop m_for_loop; }; // class sp_pcontext : public Sql_alloc diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 08f942b7d6d..43a42b579bd 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -26,6 +26,9 @@ #include "sp_rcontext.h" #include "sp_pcontext.h" #include "sql_select.h" // create_virtual_tmp_table +#include "sql_base.h" // open_tables_only_view_structure +#include "sql_acl.h" // SELECT_ACL +#include "sql_parse.h" // check_table_access /////////////////////////////////////////////////////////////////////////// // sp_rcontext implementation. @@ -59,18 +62,18 @@ sp_rcontext::~sp_rcontext() sp_rcontext *sp_rcontext::create(THD *thd, const sp_pcontext *root_parsing_ctx, - Field *return_value_fld) + Field *return_value_fld, + Row_definition_list &field_def_lst) { sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(root_parsing_ctx, return_value_fld, thd->in_sub_stmt); - if (!ctx) return NULL; if (ctx->alloc_arrays(thd) || - ctx->init_var_table(thd) || - ctx->init_var_items(thd)) + ctx->init_var_table(thd, field_def_lst) || + ctx->init_var_items(thd, field_def_lst)) { delete ctx; return NULL; @@ -80,6 +83,39 @@ sp_rcontext *sp_rcontext::create(THD *thd, } +bool Row_definition_list:: + adjust_formal_params_to_actual_params(THD *thd, List<Item> *args) +{ + List_iterator<Spvar_definition> it(*this); + List_iterator<Item> it_args(*args); + DBUG_ASSERT(elements >= args->elements ); + Spvar_definition *def; + Item *arg; + while ((def= it++) && (arg= it_args++)) + { + if (def->type_handler()->adjust_spparam_type(def, arg)) + return true; + } + return false; +} + + +bool Row_definition_list:: + adjust_formal_params_to_actual_params(THD *thd, + Item **args, uint arg_count) +{ + List_iterator<Spvar_definition> it(*this); + DBUG_ASSERT(elements >= arg_count ); + Spvar_definition *def; + for (uint i= 0; (def= it++) && (i < arg_count) ; i++) + { + if (def->type_handler()->adjust_spparam_type(def, args[i])) + return true; + } + return false; +} + + bool sp_rcontext::alloc_arrays(THD *thd) { { @@ -102,28 +138,171 @@ bool sp_rcontext::alloc_arrays(THD *thd) } -bool sp_rcontext::init_var_table(THD *thd) +bool sp_rcontext::init_var_table(THD *thd, + List<Spvar_definition> &field_def_lst) { - List<Column_definition> field_def_lst; - if (!m_root_parsing_ctx->max_var_index()) return false; - m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst); - DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->max_var_index()); if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst))) return true; - m_var_table->copy_blobs= true; - m_var_table->alias.set("", 0, m_var_table->alias.charset()); + return false; +} + +/** + Check if we have access to use a column as a %TYPE reference. + @return false - OK + @return true - access denied +*/ +static inline bool +check_column_grant_for_type_ref(THD *thd, TABLE_LIST *table_list, + const char *str, size_t length) +{ +#ifndef NO_EMBEDDED_ACCESS_CHECKS + table_list->table->grant.want_privilege= SELECT_ACL; + return check_column_grant_in_table_ref(thd, table_list, str, length); +#else return false; +#endif } -bool sp_rcontext::init_var_items(THD *thd) +/** + This method implementation is very close to fill_schema_table_by_open(). +*/ +bool Qualified_column_ident::resolve_type_ref(THD *thd, Column_definition *def) +{ + Open_tables_backup open_tables_state_backup; + thd->reset_n_backup_open_tables_state(&open_tables_state_backup); + + TABLE_LIST *table_list; + Field *src; + LEX *save_lex= thd->lex; + bool rc= true; + + sp_lex_local lex(thd, thd->lex); + thd->lex= &lex; + + lex.context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW; + // Make %TYPE variables see temporary tables that shadow permanent tables + thd->temporary_tables= open_tables_state_backup.temporary_tables; + + if ((table_list= lex.select_lex.add_table_to_list(thd, this, NULL, 0, + TL_READ_NO_INSERT, + MDL_SHARED_READ)) && + !check_table_access(thd, SELECT_ACL, table_list, TRUE, UINT_MAX, FALSE) && + !open_tables_only_view_structure(thd, table_list, + thd->mdl_context.has_locks())) + { + if ((src= lex.query_tables->table->find_field_by_name(&m_column))) + { + if (!(rc= check_column_grant_for_type_ref(thd, table_list, + m_column.str, + m_column.length))) + { + *def= Column_definition(thd, src, NULL/*No defaults,no constraints*/); + def->flags&= (uint) ~NOT_NULL_FLAG; + rc= def->sp_prepare_create_field(thd, thd->mem_root); + } + } + else + my_error(ER_BAD_FIELD_ERROR, MYF(0), m_column.str, table.str); + } + + lex.unit.cleanup(); + thd->temporary_tables= NULL; // Avoid closing temporary tables + close_thread_tables(thd); + thd->lex= save_lex; + thd->restore_backup_open_tables_state(&open_tables_state_backup); + return rc; +} + + +/** + This method resolves the structure of a variable declared as: + rec t1%ROWTYPE; + It opens the table "t1" and copies its structure to %ROWTYPE variable. +*/ +bool Table_ident::resolve_table_rowtype_ref(THD *thd, + Row_definition_list &defs) +{ + Open_tables_backup open_tables_state_backup; + thd->reset_n_backup_open_tables_state(&open_tables_state_backup); + + TABLE_LIST *table_list; + LEX *save_lex= thd->lex; + bool rc= true; + + /* + Create a temporary LEX on stack and switch to it. + In case of VIEW, open_tables_only_view_structure() will open more + tables/views recursively. We want to avoid them to stick to the current LEX. + */ + sp_lex_local lex(thd, thd->lex); + thd->lex= &lex; + + lex.context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW; + // Make %ROWTYPE variables see temporary tables that shadow permanent tables + thd->temporary_tables= open_tables_state_backup.temporary_tables; + + if ((table_list= lex.select_lex.add_table_to_list(thd, this, NULL, 0, + TL_READ_NO_INSERT, + MDL_SHARED_READ)) && + !check_table_access(thd, SELECT_ACL, table_list, TRUE, UINT_MAX, FALSE) && + !open_tables_only_view_structure(thd, table_list, + thd->mdl_context.has_locks())) + { + for (Field **src= lex.query_tables->table->field; *src; src++) + { + /* + Make field names on the THD memory root, + as the table will be closed and freed soon, + in the end of this method. + */ + LEX_CSTRING tmp= src[0]->field_name; + Spvar_definition *def; + if ((rc= check_column_grant_for_type_ref(thd, table_list, + tmp.str, tmp.length)) || + (rc= !(src[0]->field_name.str= thd->strmake(tmp.str, tmp.length))) || + (rc= !(def= new (thd->mem_root) Spvar_definition(thd, *src)))) + break; + src[0]->field_name.str= tmp.str; // Restore field name, just in case. + def->flags&= (uint) ~NOT_NULL_FLAG; + if ((rc= def->sp_prepare_create_field(thd, thd->mem_root))) + break; + defs.push_back(def, thd->mem_root); + } + } + + lex.unit.cleanup(); + thd->temporary_tables= NULL; // Avoid closing temporary tables + close_thread_tables(thd); + thd->lex= save_lex; + thd->restore_backup_open_tables_state(&open_tables_state_backup); + return rc; +} + + +bool Row_definition_list::resolve_type_refs(THD *thd) +{ + List_iterator<Spvar_definition> it(*this); + Spvar_definition *def; + while ((def= it++)) + { + if (def->is_column_type_ref() && + def->column_type_ref()->resolve_type_ref(thd, def)) + return true; + } + return false; +}; + + +bool sp_rcontext::init_var_items(THD *thd, + List<Spvar_definition> &field_def_lst) { uint num_vars= m_root_parsing_ctx->max_var_index(); @@ -135,34 +314,91 @@ bool sp_rcontext::init_var_items(THD *thd) if (!m_var_items.array()) return true; - for (uint idx = 0; idx < num_vars; ++idx) + DBUG_ASSERT(field_def_lst.elements == num_vars); + List_iterator<Spvar_definition> it(field_def_lst); + Spvar_definition *def= it++; + + for (uint idx= 0; idx < num_vars; ++idx, def= it++) { - if (!(m_var_items[idx]= new (thd->mem_root) Item_field(thd, m_var_table->field[idx]))) - return true; + Field *field= m_var_table->field[idx]; + if (def->is_table_rowtype_ref()) + { + Row_definition_list defs; + Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field); + if (!(m_var_items[idx]= item) || + def->table_rowtype_ref()->resolve_table_rowtype_ref(thd, defs) || + item->row_create_items(thd, &defs)) + return true; + } + else if (def->is_cursor_rowtype_ref()) + { + Row_definition_list defs; + Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field); + if (!(m_var_items[idx]= item)) + return true; + } + else if (def->is_row()) + { + Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field); + if (!(m_var_items[idx]= item) || + item->row_create_items(thd, def->row_field_definitions())) + return true; + } + else + { + if (!(m_var_items[idx]= new (thd->mem_root) Item_field(thd, field))) + return true; + } } + return false; +} + + +bool Item_spvar_args::row_create_items(THD *thd, List<Spvar_definition> *list) +{ + DBUG_ASSERT(list); + if (!(m_table= create_virtual_tmp_table(thd, *list))) + return true; + + if (alloc_arguments(thd, list->elements)) + return true; + List_iterator<Spvar_definition> it(*list); + Spvar_definition *def; + for (arg_count= 0; (def= it++); arg_count++) + { + if (!(args[arg_count]= new (thd->mem_root) + Item_field(thd, m_table->field[arg_count]))) + return true; + } return false; } +Item_spvar_args::~Item_spvar_args() +{ + if (m_table) + free_blobs(m_table); +} + + bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item) { DBUG_ASSERT(m_return_value_fld); m_return_value_set = true; - return sp_eval_expr(thd, m_return_value_fld, return_value_item); + return sp_eval_expr(thd, NULL, m_return_value_fld, return_value_item); } -bool sp_rcontext::push_cursor(THD *thd, sp_lex_keeper *lex_keeper, - sp_instr_cpush *i) +bool sp_rcontext::push_cursor(THD *thd, sp_lex_keeper *lex_keeper) { /* We should create cursors in the callers arena, as it could be (and usually is) used in several instructions. */ - sp_cursor *c= new (callers_arena->mem_root) sp_cursor(thd, lex_keeper, i); + sp_cursor *c= new (callers_arena->mem_root) sp_cursor(thd, lex_keeper); if (c == NULL) return true; @@ -228,9 +464,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd, if (thd->is_error()) { found_handler= - cur_spi->m_ctx->find_handler(da->get_sqlstate(), - da->sql_errno(), - Sql_condition::WARN_LEVEL_ERROR); + cur_spi->m_ctx->find_handler(da->get_error_condition_identity()); if (found_handler) found_condition= da->get_error_condition(); @@ -244,12 +478,10 @@ bool sp_rcontext::handle_sql_condition(THD *thd, */ if (!found_condition) { - Sql_condition *condition= - new (callers_arena->mem_root) Sql_condition(callers_arena->mem_root); - condition->set(da->sql_errno(), da->get_sqlstate(), - Sql_condition::WARN_LEVEL_ERROR, - da->message()); - found_condition= condition; + found_condition= + new (callers_arena->mem_root) Sql_condition(callers_arena->mem_root, + da->get_error_condition_identity(), + da->message()); } } else if (da->current_statement_warn_count()) @@ -266,10 +498,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd, if (c->get_level() == Sql_condition::WARN_LEVEL_WARN || c->get_level() == Sql_condition::WARN_LEVEL_NOTE) { - const sp_handler *handler= - cur_spi->m_ctx->find_handler(c->get_sqlstate(), - c->get_sql_errno(), - c->get_level()); + const sp_handler *handler= cur_spi->m_ctx->find_handler(*c); if (handler) { found_handler= handler; @@ -366,15 +595,102 @@ uint sp_rcontext::exit_handler(Diagnostics_area *da) } -int sp_rcontext::set_variable(THD *thd, Field *field, Item **value) +int sp_rcontext::set_variable(THD *thd, uint idx, Item **value) { + Field *field= m_var_table->field[idx]; if (!value) { field->set_null(); return 0; } + Item *dst= m_var_items[idx]; + + if (dst->cmp_type() != ROW_RESULT) + return sp_eval_expr(thd, dst, m_var_table->field[idx], value); + + DBUG_ASSERT(dst->type() == Item::FIELD_ITEM); + if (value[0]->type() == Item::NULL_ITEM) + { + /* + We're in a auto-generated sp_inst_set, to assign + the explicit default NULL value to a ROW variable. + */ + for (uint i= 0; i < dst->cols(); i++) + { + Item_field_row *item_field_row= (Item_field_row*) dst; + item_field_row->get_row_field(i)->set_null(); + } + return false; + } + + /** + - In case if we're assigning a ROW variable from another ROW variable, + value[0] points to Item_splocal. sp_prepare_func_item() will return the + fixed underlying Item_field_spvar with ROW members in its aguments(). + - In case if we're assigning from a ROW() value, src and value[0] will + point to the same Item_row. + */ + Item *src; + if (!(src= sp_prepare_func_item(thd, value, dst->cols())) || + src->cmp_type() != ROW_RESULT) + { + my_error(ER_OPERAND_COLUMNS, MYF(0), dst->cols()); + return true; + } + DBUG_ASSERT(dst->cols() == src->cols()); + for (uint i= 0; i < src->cols(); i++) + set_variable_row_field(thd, idx, i, src->addr(i)); + return false; +} + + +void sp_rcontext::set_variable_row_field_to_null(THD *thd, + uint var_idx, + uint field_idx) +{ + Item *dst= get_item(var_idx); + DBUG_ASSERT(dst->type() == Item::FIELD_ITEM); + DBUG_ASSERT(dst->cmp_type() == ROW_RESULT); + Item_field_row *item_field_row= (Item_field_row*) dst; + item_field_row->get_row_field(field_idx)->set_null(); +} + + +int sp_rcontext::set_variable_row_field(THD *thd, uint var_idx, uint field_idx, + Item **value) +{ + DBUG_ASSERT(value); + Item *dst= get_item(var_idx); + DBUG_ASSERT(dst->type() == Item::FIELD_ITEM); + DBUG_ASSERT(dst->cmp_type() == ROW_RESULT); + Item_field_row *item_field_row= (Item_field_row*) dst; + + Item *expr_item= sp_prepare_func_item(thd, value); + if (!expr_item) + { + DBUG_ASSERT(thd->is_error()); + return true; + } + return sp_eval_expr(thd, + item_field_row->arguments()[field_idx], + item_field_row->get_row_field(field_idx), + value); +} + - return sp_eval_expr(thd, field, value); +int sp_rcontext::set_variable_row(THD *thd, uint var_idx, List<Item> &items) +{ + DBUG_ENTER("sp_rcontext::set_variable_row"); + DBUG_ASSERT(thd->spcont->get_item(var_idx)->cols() == items.elements); + List_iterator<Item> it(items); + Item *item; + for (uint i= 0 ; (item= it++) ; i++) + { + int rc; + if ((rc= thd->spcont->set_variable_row_field(thd, var_idx, i, &item))) + DBUG_RETURN(rc); + } + DBUG_RETURN(0); } @@ -386,7 +702,7 @@ Item_cache *sp_rcontext::create_case_expr_holder(THD *thd, thd->set_n_backup_active_arena(thd->spcont->callers_arena, ¤t_arena); - holder= Item_cache::get_cache(thd, item); + holder= item->get_cache(thd); thd->restore_active_arena(thd->spcont->callers_arena, ¤t_arena); @@ -420,11 +736,13 @@ bool sp_rcontext::set_case_expr(THD *thd, int case_expr_id, /////////////////////////////////////////////////////////////////////////// -sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i): +sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper): result(thd_arg), m_lex_keeper(lex_keeper), server_side_cursor(NULL), - m_i(i) + m_fetch_count(0), + m_row_count(0), + m_found(false) { /* currsor can't be stored in QC, so we should prevent opening QC for @@ -461,6 +779,27 @@ int sp_cursor::open(THD *thd) } +/** + Open the cursor, but do not copy data. + This method is used to fetch the cursor structure + to cursor%ROWTYPE routine variables. + Data copying is suppressed by setting thd->lex->limit_rows_examined to 0. +*/ +int sp_cursor::open_view_structure_only(THD *thd) +{ + int res; + int thd_no_errors_save= thd->no_errors; + Item *limit_rows_examined= thd->lex->limit_rows_examined; // No data copying + if (!(thd->lex->limit_rows_examined= new (thd->mem_root) Item_uint(thd, 0))) + return -1; + thd->no_errors= true; // Suppress ER_QUERY_EXCEEDED_ROWS_EXAMINED_LIMIT + res= open(thd); + thd->no_errors= thd_no_errors_save; + thd->lex->limit_rows_examined= limit_rows_examined; + return res; +} + + int sp_cursor::close(THD *thd) { if (! server_side_cursor) @@ -469,6 +808,8 @@ int sp_cursor::close(THD *thd) MYF(0)); return -1; } + m_row_count= m_fetch_count= 0; + m_found= false; destroy(); return 0; } @@ -489,13 +830,17 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars) MYF(0)); return -1; } - if (vars->elements != result.get_field_count()) + if (vars->elements != result.get_field_count() && + (vars->elements != 1 || + result.get_field_count() != + thd->spcont->get_item(vars->head()->offset)->cols())) { my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS, ER_THD(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0)); return -1; } + m_fetch_count++; DBUG_EXECUTE_IF("bug23032_emit_warning", push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, @@ -513,14 +858,24 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars) */ if (! server_side_cursor->is_open()) { + m_found= false; + if (thd->variables.sql_mode & MODE_ORACLE) + return 0; my_message(ER_SP_FETCH_NO_DATA, ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0)); return -1; } + m_found= true; + m_row_count++; return 0; } +bool sp_cursor::export_structure(THD *thd, Row_definition_list *list) +{ + return server_side_cursor->export_structure(thd, list); +} + /////////////////////////////////////////////////////////////////////////// // sp_cursor::Select_fetch_into_spvars implementation. /////////////////////////////////////////////////////////////////////////// @@ -538,15 +893,16 @@ int sp_cursor::Select_fetch_into_spvars::prepare(List<Item> &fields, } -int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items) +bool sp_cursor::Select_fetch_into_spvars:: + send_data_to_variable_list(List<sp_variable> &vars, List<Item> &items) { - List_iterator_fast<sp_variable> spvar_iter(*spvar_list); + List_iterator_fast<sp_variable> spvar_iter(vars); List_iterator_fast<Item> item_iter(items); sp_variable *spvar; Item *item; /* Must be ensured by the caller */ - DBUG_ASSERT(spvar_list->elements == items.elements); + DBUG_ASSERT(vars.elements == items.elements); /* Assign the row fetched from a server side cursor to stored @@ -559,3 +915,25 @@ int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items) } return false; } + + +int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items) +{ + Item *item; + /* + If we have only one variable in spvar_list, and this is a ROW variable, + and the number of fields in the ROW variable matches the number of + fields in the query result, we fetch to this ROW variable. + + If there is one variable, and it is a ROW variable, but its number + of fields does not match the number of fields in the query result, + we go through send_data_to_variable_list(). It will report an error + on attempt to assign a scalar value to a ROW variable. + */ + return spvar_list->elements == 1 && + (item= thd->spcont->get_item(spvar_list->head()->offset)) && + item->type_handler() == &type_handler_row && + item->cols() == items.elements ? + thd->spcont->set_variable_row(thd, spvar_list->head()->offset, items) : + send_data_to_variable_list(*spvar_list, items); +} diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 2640490fefa..93c8cacc70a 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -70,7 +70,8 @@ public: /// @return valid sp_rcontext object or NULL in case of OOM-error. static sp_rcontext *create(THD *thd, const sp_pcontext *root_parsing_ctx, - Field *return_value_fld); + Field *return_value_fld, + Row_definition_list &defs); ~sp_rcontext(); @@ -120,18 +121,10 @@ public: /// standard SQL-condition processing (Diagnostics_area should contain an /// object for active SQL-condition, not just information stored in DA's /// fields). - class Sql_condition_info : public Sql_alloc + class Sql_condition_info : public Sql_alloc, + public Sql_condition_identity { public: - /// SQL error code. - uint sql_errno; - - /// Error level. - Sql_condition::enum_warning_level level; - - /// SQLSTATE. - char sql_state[SQLSTATE_LENGTH + 1]; - /// Text message. char *message; @@ -141,12 +134,8 @@ public: /// @param arena Query arena for SP Sql_condition_info(const Sql_condition *_sql_condition, Query_arena *arena) - :sql_errno(_sql_condition->get_sql_errno()), - level(_sql_condition->get_level()) + :Sql_condition_identity(*_sql_condition) { - memcpy(sql_state, _sql_condition->get_sqlstate(), SQLSTATE_LENGTH); - sql_state[SQLSTATE_LENGTH]= '\0'; - message= strdup_root(arena->mem_root, _sql_condition->get_message_text()); } }; @@ -197,9 +186,11 @@ public: // SP-variables. ///////////////////////////////////////////////////////////////////////// - int set_variable(THD *thd, uint var_idx, Item **value) - { return set_variable(thd, m_var_table->field[var_idx], value); } - + int set_variable(THD *thd, uint var_idx, Item **value); + void set_variable_row_field_to_null(THD *thd, uint var_idx, uint field_idx); + int set_variable_row_field(THD *thd, uint var_idx, uint field_idx, + Item **value); + int set_variable_row(THD *thd, uint var_idx, List<Item> &items); Item *get_item(uint var_idx) const { return m_var_items[var_idx]; } @@ -281,7 +272,7 @@ public: /// @return error flag. /// @retval false on success. /// @retval true on error. - bool push_cursor(THD *thd, sp_lex_keeper *lex_keeper, sp_instr_cpush *i); + bool push_cursor(THD *thd, sp_lex_keeper *lex_keeper); /// Pop and delete given number of sp_cursor instance from the cursor stack. /// @@ -346,7 +337,7 @@ private: /// @return error flag. /// @retval false on success. /// @retval true on error. - bool init_var_table(THD *thd); + bool init_var_table(THD *thd, List<Spvar_definition> &defs); /// Create and initialize an Item-adapter (Item_field) for each SP-var field. /// @@ -355,7 +346,7 @@ private: /// @return error flag. /// @retval false on success. /// @retval true on error. - bool init_var_items(THD *thd); + bool init_var_items(THD *thd, List<Spvar_definition> &defs); /// Create an instance of appropriate Item_cache class depending on the /// specified type in the callers arena. @@ -369,8 +360,6 @@ private: /// @return Pointer to valid object on success, or NULL in case of error. Item_cache *create_case_expr_holder(THD *thd, const Item *item) const; - int set_variable(THD *thd, Field *field, Item **value); - private: /// Top-level (root) parsing context for this runtime context. const sp_pcontext *m_root_parsing_ctx; @@ -427,6 +416,7 @@ private: { List<sp_variable> *spvar_list; uint field_count; + bool send_data_to_variable_list(List<sp_variable> &vars, List<Item> &items); public: Select_fetch_into_spvars(THD *thd_arg): select_result_interceptor(thd_arg) {} uint get_field_count() { return field_count; } @@ -438,7 +428,7 @@ private: }; public: - sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i); + sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper); virtual ~sp_cursor() { destroy(); } @@ -447,21 +437,33 @@ public: int open(THD *thd); + int open_view_structure_only(THD *thd); + int close(THD *thd); my_bool is_open() { return MY_TEST(server_side_cursor); } + bool found() const + { return m_found; } + + ulonglong row_count() const + { return m_row_count; } + + ulonglong fetch_count() const + { return m_fetch_count; } + int fetch(THD *, List<sp_variable> *vars); - sp_instr_cpush *get_instr() - { return m_i; } + bool export_structure(THD *thd, Row_definition_list *list); private: Select_fetch_into_spvars result; sp_lex_keeper *m_lex_keeper; Server_side_cursor *server_side_cursor; - sp_instr_cpush *m_i; // My push instruction + ulonglong m_fetch_count; // Number of FETCH commands since last OPEN + ulonglong m_row_count; // Number of successful FETCH since last OPEN + bool m_found; // If last FETCH fetched a row void destroy(); }; // class sp_cursor : public Sql_alloc diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 819bdb8200a..b0550fb5189 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -176,30 +176,30 @@ TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = { const TABLE_FIELD_DEF mysql_db_table_def= {MYSQL_DB_FIELD_COUNT, mysql_db_table_fields, 0, (uint*) 0 }; -static LEX_STRING native_password_plugin_name= { - C_STRING_WITH_LEN("mysql_native_password") +static LEX_CSTRING native_password_plugin_name= { + STRING_WITH_LEN("mysql_native_password") }; -static LEX_STRING old_password_plugin_name= { - C_STRING_WITH_LEN("mysql_old_password") +static LEX_CSTRING old_password_plugin_name= { + STRING_WITH_LEN("mysql_old_password") }; /// @todo make it configurable -LEX_STRING *default_auth_plugin_name= &native_password_plugin_name; +LEX_CSTRING *default_auth_plugin_name= &native_password_plugin_name; /* Wildcard host, matches any hostname */ -LEX_STRING host_not_specified= { C_STRING_WITH_LEN("%") }; +LEX_CSTRING host_not_specified= { STRING_WITH_LEN("%") }; /* Constants, used in the SHOW GRANTS command. Their actual string values are irrelevant, they're always compared as pointers to these string constants. */ -LEX_STRING current_user= { C_STRING_WITH_LEN("*current_user") }; -LEX_STRING current_role= { C_STRING_WITH_LEN("*current_role") }; -LEX_STRING current_user_and_current_role= { C_STRING_WITH_LEN("*current_user_and_current_role") }; +LEX_CSTRING current_user= { STRING_WITH_LEN("*current_user") }; +LEX_CSTRING current_role= { STRING_WITH_LEN("*current_role") }; +LEX_CSTRING current_user_and_current_role= { STRING_WITH_LEN("*current_user_and_current_role") }; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -244,7 +244,7 @@ public: { return (void*) alloc_root(mem_root, size); } uchar flags; // field used to store various state information - LEX_STRING user; + LEX_CSTRING user; /* list to hold references to granted roles (ACL_ROLE instances) */ DYNAMIC_ARRAY role_grants; }; @@ -259,9 +259,9 @@ public: uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 4.0, 20 - 4.1.1 enum SSL_type ssl_type; const char *ssl_cipher, *x509_issuer, *x509_subject; - LEX_STRING plugin; - LEX_STRING auth_string; - LEX_STRING default_rolename; + LEX_CSTRING plugin; + LEX_CSTRING auth_string; + LEX_CSTRING default_rolename; ACL_USER *copy(MEM_ROOT *root) { @@ -338,7 +338,7 @@ class ACL_DB :public ACL_ACCESS { public: acl_host_and_ip host; - char *user,*db; + const char *user,*db; ulong initial_access; /* access bits present in the table */ }; @@ -362,8 +362,8 @@ static bool show_database_privileges(THD *, const char *, const char *, char *, size_t); static bool show_table_and_column_privileges(THD *, const char *, const char *, char *, size_t); -static int show_routine_grants(THD *, const char *, const char *, HASH *, - const char *, int, char *, int); +static int show_routine_grants(THD *, const char *, const char *, + const Sp_handler *sph, char *, int); class Grant_tables; class User_table; @@ -530,10 +530,10 @@ public: } static int store_pk(TABLE *table, - const LEX_STRING *host, - const LEX_STRING *user, - const LEX_STRING *proxied_host, - const LEX_STRING *proxied_user) + const LEX_CSTRING *host, + const LEX_CSTRING *user, + const LEX_CSTRING *proxied_host, + const LEX_CSTRING *proxied_user) { DBUG_ENTER("ACL_PROXY_USER::store_pk"); DBUG_PRINT("info", ("host=%s, user=%s, proxied_host=%s, proxied_user=%s", @@ -560,10 +560,10 @@ public: } static int store_data_record(TABLE *table, - const LEX_STRING *host, - const LEX_STRING *user, - const LEX_STRING *proxied_host, - const LEX_STRING *proxied_user, + const LEX_CSTRING *host, + const LEX_CSTRING *user, + const LEX_CSTRING *proxied_host, + const LEX_CSTRING *proxied_user, bool with_grant, const char *grantor) { @@ -616,8 +616,8 @@ struct ROLE_GRANT_PAIR : public Sql_alloc LEX_STRING hashkey; bool with_admin; - bool init(MEM_ROOT *mem, char *username, char *hostname, char *rolename, - bool with_admin_option); + bool init(MEM_ROOT *mem, const char *username, const char *hostname, + const char *rolename, bool with_admin_option); }; static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length, @@ -627,8 +627,8 @@ static uchar* acl_role_map_get_key(ROLE_GRANT_PAIR *entry, size_t *length, return (uchar*) entry->hashkey.str; } -bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, char *username, - char *hostname, char *rolename, +bool ROLE_GRANT_PAIR::init(MEM_ROOT *mem, const char *username, + const char *hostname, const char *rolename, bool with_admin_option) { size_t uname_l = safe_strlen(username); @@ -736,7 +736,7 @@ static void rebuild_role_grants(void); static ACL_USER *find_user_exact(const char *host, const char *user); static ACL_USER *find_user_wild(const char *host, const char *user, const char *ip= 0); static ACL_ROLE *find_acl_role(const char *user); -static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_STRING *u, const LEX_STRING *h, const LEX_STRING *r); +static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, const LEX_CSTRING *h, const LEX_CSTRING *r); static ACL_USER_BASE *find_acl_user_base(const char *user, const char *host); static bool update_user_table(THD *, const User_table &, const char *, const char *, const char *, uint); @@ -754,6 +754,19 @@ static int traverse_role_graph_down(ACL_USER_BASE *, void *, int (*) (ACL_USER_BASE *, void *), int (*) (ACL_USER_BASE *, ACL_ROLE *, void *)); + +HASH *Sp_handler_procedure::get_priv_hash() const +{ + return &proc_priv_hash; +} + + +HASH *Sp_handler_function::get_priv_hash() const +{ + return &func_priv_hash; +} + + /* Enumeration of ACL/GRANT tables in the mysql database */ @@ -1487,7 +1500,7 @@ static bool has_validation_plugins() MariaDB_PASSWORD_VALIDATION_PLUGIN, NULL); } -struct validation_data { LEX_STRING *user, *password; }; +struct validation_data { LEX_CSTRING *user, *password; }; static my_bool do_validate(THD *, plugin_ref plugin, void *arg) { @@ -1504,7 +1517,7 @@ static bool validate_password(LEX_USER *user, THD *thd) { struct validation_data data= { &user->user, user->pwtext.str ? &user->pwtext : - const_cast<LEX_STRING *>(&empty_lex_str) }; + const_cast<LEX_CSTRING *>(&empty_clex_str) }; if (plugin_foreach(NULL, do_validate, MariaDB_PASSWORD_VALIDATION_PLUGIN, &data)) { @@ -1553,7 +1566,7 @@ set_user_salt(ACL_USER *acl_user, const char *password, uint password_len) acl_user->salt_len= 0; } -static char *fix_plugin_ptr(char *name) +static const char *fix_plugin_ptr(const char *name) { if (my_strcasecmp(system_charset_info, name, native_password_plugin_name.str) == 0) @@ -1645,13 +1658,13 @@ static bool fix_lex_user(THD *thd, LEX_USER *user) if (user->plugin.length) { user->pwhash= user->auth; - user->plugin= empty_lex_str; - user->auth= empty_lex_str; + user->plugin= empty_clex_str; + user->auth= empty_clex_str; } if (user->pwhash.length && user->pwhash.length != check_length) { - my_error(ER_PASSWD_LENGTH, MYF(0), check_length); + my_error(ER_PASSWD_LENGTH, MYF(0), (int) check_length); return true; } @@ -2138,12 +2151,13 @@ static bool acl_load(THD *thd, const Grant_tables& tables) while (!(read_record_info.read_record(&read_record_info))) { ACL_DB db; + char *db_name; db.user=get_field(&acl_memroot, db_table.user()); const char *hostname= get_field(&acl_memroot, db_table.host()); if (!hostname && find_acl_role(db.user)) hostname= ""; update_hostname(&db.host, hostname); - db.db=get_field(&acl_memroot, db_table.db()); + db.db= db_name= get_field(&acl_memroot, db_table.db()); if (!db.db) { sql_print_warning("Found an entry in the 'db' table with empty database name; Skipped"); @@ -2171,8 +2185,8 @@ static bool acl_load(THD *thd, const Grant_tables& tables) sql_print_warning(ER_THD(thd, ER_WRONG_DB_NAME), db.db); continue; } - my_casedn_str(files_charset_info, db.db); - if (strcmp(db.db, tmp_name) != 0) + my_casedn_str(files_charset_info, db_name); + if (strcmp(db_name, tmp_name) != 0) { sql_print_warning("'db' entry '%s %s@%s' had database in mixed " "case that has been forced to lowercase because " @@ -2496,8 +2510,8 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) TRUE Error */ -bool acl_getroot(Security_context *sctx, char *user, char *host, - char *ip, char *db) +bool acl_getroot(Security_context *sctx, const char *user, const char *host, + const char *ip, const char *db) { int res= 1; uint i; @@ -2610,7 +2624,7 @@ static int check_user_can_set_role(const char *user, const char *host, acl_user= find_user_wild(host, user, ip); if (acl_user == NULL) { - my_error(ER_INVALID_CURRENT_USER, MYF(0), rolename); + my_error(ER_INVALID_CURRENT_USER, MYF(0)); result= -1; } else if (access) @@ -2660,7 +2674,7 @@ end: } -int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) +int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access) { /* Yes! priv_user@host. Don't ask why - that's what check_access() does. */ return check_user_can_set_role(thd->security_ctx->priv_user, @@ -2668,7 +2682,7 @@ int acl_check_setrole(THD *thd, char *rolename, ulonglong *access) } -int acl_setrole(THD *thd, char *rolename, ulonglong access) +int acl_setrole(THD *thd, const char *rolename, ulonglong access) { /* merge the privileges */ Security_context *sctx= thd->security_ctx; @@ -2714,8 +2728,8 @@ static void acl_update_user(const char *user, const char *host, const char *x509_subject, USER_RESOURCES *mqh, ulong privileges, - const LEX_STRING *plugin, - const LEX_STRING *auth) + const LEX_CSTRING *plugin, + const LEX_CSTRING *auth) { mysql_mutex_assert_owner(&acl_cache->lock); @@ -2792,8 +2806,8 @@ static void acl_insert_user(const char *user, const char *host, const char *x509_subject, USER_RESOURCES *mqh, ulong privileges, - const LEX_STRING *plugin, - const LEX_STRING *auth) + const LEX_CSTRING *plugin, + const LEX_CSTRING *auth) { ACL_USER acl_user; @@ -3998,7 +4012,7 @@ static int replace_user_table(THD *thd, const User_table &user_table, } } else - combo.pwhash= empty_lex_str; + combo.pwhash= empty_clex_str; /* if the user table is not up to date, we can't handle role updates */ if (!user_table.is_role() && handle_as_role) @@ -4401,8 +4415,8 @@ abort: @param revoke_grant true for REVOKE, false for GRANT */ static int -replace_roles_mapping_table(TABLE *table, LEX_STRING *user, LEX_STRING *host, - LEX_STRING *role, bool with_admin, +replace_roles_mapping_table(TABLE *table, LEX_CSTRING *user, LEX_CSTRING *host, + LEX_CSTRING *role, bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant) { DBUG_ENTER("replace_roles_mapping_table"); @@ -4483,7 +4497,7 @@ table_error: @param revoke_grant true for REVOKE, false for GRANT */ static int -update_role_mapping(LEX_STRING *user, LEX_STRING *host, LEX_STRING *role, +update_role_mapping(LEX_CSTRING *user, LEX_CSTRING *host, LEX_CSTRING *role, bool with_admin, ROLE_GRANT_PAIR *existing, bool revoke_grant) { if (revoke_grant) @@ -4972,10 +4986,11 @@ static GRANT_NAME *name_hash_search(HASH *name_hash, static GRANT_NAME * routine_hash_search(const char *host, const char *ip, const char *db, - const char *user, const char *tname, bool proc, bool exact) + const char *user, const char *tname, const Sp_handler *sph, + bool exact) { return (GRANT_TABLE*) - name_hash_search(proc ? &proc_priv_hash : &func_priv_hash, + name_hash_search(sph->get_priv_hash(), host, ip, db, user, tname, exact, TRUE); } @@ -5350,13 +5365,14 @@ table_error: static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, TABLE *table, const LEX_USER &combo, const char *db, const char *routine_name, - bool is_proc, ulong rights, bool revoke_grant) + const Sp_handler *sph, + ulong rights, bool revoke_grant) { char grantor[USER_HOST_BUFF_SIZE]; int old_row_exists= 1; int error=0; ulong store_proc_rights; - HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; + HASH *hash= sph->get_priv_hash(); DBUG_ENTER("replace_routine_table"); if (revoke_grant && !grant_name->init_privs) // only inherited role privs @@ -5380,9 +5396,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1); table->field[3]->store(routine_name,(uint) strlen(routine_name), &my_charset_latin1); - table->field[4]->store((longlong)(is_proc ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION), - TRUE); + table->field[4]->store((longlong) sph->type(), true); store_record(table,record[1]); // store at pos 1 if (table->file->ha_index_read_idx_map(table->record[0], 0, @@ -5502,6 +5516,23 @@ struct PRIVS_TO_MERGE const char *db, *name; }; + +static enum PRIVS_TO_MERGE::what sp_privs_to_merge(stored_procedure_type type) +{ + switch (type) { + case TYPE_ENUM_FUNCTION: + return PRIVS_TO_MERGE::FUNC; + case TYPE_ENUM_PROCEDURE: + return PRIVS_TO_MERGE::PROC; + case TYPE_ENUM_TRIGGER: + case TYPE_ENUM_PROXY: + break; + } + DBUG_ASSERT(0); + return PRIVS_TO_MERGE::PROC; +} + + static int init_role_for_merging(ACL_ROLE *role, void *context) { role->counter= 0; @@ -5823,7 +5854,8 @@ static int db_name_sort(ACL_DB * const *db1, ACL_DB * const *db2) 2 - ACL_DB was added 4 - ACL_DB was deleted */ -static int update_role_db(ACL_DB *merged, ACL_DB **first, ulong access, char *role) +static int update_role_db(ACL_DB *merged, ACL_DB **first, ulong access, + const char *role) { if (!first) return 0; @@ -6043,8 +6075,8 @@ static int update_role_columns(GRANT_TABLE *merged, 4 - GRANT_TABLE was deleted */ static int update_role_table_columns(GRANT_TABLE *merged, - GRANT_TABLE **first, GRANT_TABLE **last, - ulong privs, ulong cols, char *role) + GRANT_TABLE **first, GRANT_TABLE **last, + ulong privs, ulong cols, const char *role) { if (!first) return 0; @@ -6173,7 +6205,7 @@ static int routine_name_sort(GRANT_NAME * const *r1, GRANT_NAME * const *r2) 4 - GRANT_NAME was deleted */ static int update_role_routines(GRANT_NAME *merged, GRANT_NAME **first, - ulong privs, char *role, HASH *hash) + ulong privs, const char *role, HASH *hash) { if (!first) return 0; @@ -6392,7 +6424,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, List_iterator <LEX_USER> str_list (user_list); LEX_USER *Str, *tmp_Str; bool create_new_users=0; - char *db_name, *table_name; + const char *db_name, *table_name; DBUG_ENTER("mysql_table_grant"); if (rights & ~TABLE_ACLS) @@ -6438,7 +6470,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, { if (!(rights & CREATE_ACL)) { - if (!ha_table_exists(thd, table_list->db, table_list->table_name, 0)) + if (!ha_table_exists(thd, table_list->db, table_list->table_name)) { my_error(ER_NO_SUCH_TABLE, MYF(0), table_list->db, table_list->alias); DBUG_RETURN(TRUE); @@ -6625,7 +6657,7 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, @param thd Thread handle @param table_list List of routines to give grant - @param is_proc Is this a list of procedures? + @param sph SP handler @param user_list List of users to give grant @param rights Table level grant @param revoke_grant Is this is a REVOKE command? @@ -6635,14 +6667,15 @@ int mysql_table_grant(THD *thd, TABLE_LIST *table_list, @retval TRUE An error occurred. */ -bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, +bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, + const Sp_handler *sph, List <LEX_USER> &user_list, ulong rights, bool revoke_grant, bool write_to_binlog) { List_iterator <LEX_USER> str_list (user_list); LEX_USER *Str, *tmp_Str; bool create_new_users= 0, result; - char *db_name, *table_name; + const char *db_name, *table_name; DBUG_ENTER("mysql_routine_grant"); if (rights & ~PROC_ACLS) @@ -6655,7 +6688,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, if (!revoke_grant) { - if (sp_exist_routines(thd, table_list, is_proc)) + if (sph->sp_exist_routines(thd, table_list)) DBUG_RETURN(TRUE); } @@ -6696,7 +6729,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, db_name= table_list->db; table_name= table_list->table_name; grant_name= routine_hash_search(Str->host.str, NullS, db_name, - Str->user.str, table_name, is_proc, 1); + Str->user.str, table_name, sph, 1); if (!grant_name || !grant_name->init_privs) { if (revoke_grant) @@ -6710,8 +6743,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, Str->user.str, table_name, rights, TRUE); if (!grant_name || - my_hash_insert(is_proc ? - &proc_priv_hash : &func_priv_hash,(uchar*) grant_name)) + my_hash_insert(sph->get_priv_hash(), (uchar*) grant_name)) { result= TRUE; continue; @@ -6722,7 +6754,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, instead of TABLE directly. */ if (tables.procs_priv_table().no_such_table() || replace_routine_table(thd, grant_name, tables.procs_priv_table().table(), - *Str, db_name, table_name, is_proc, rights, + *Str, db_name, table_name, sph, rights, revoke_grant) != 0) { result= TRUE; @@ -6730,7 +6762,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, } if (Str->is_role()) propagate_role_grants(find_acl_role(Str->user.str), - is_proc ? PRIVS_TO_MERGE::PROC : PRIVS_TO_MERGE::FUNC, + sp_privs_to_merge(sph->type()), db_name, table_name); } thd->mem_root= old_root; @@ -6752,7 +6784,7 @@ bool mysql_routine_grant(THD *thd, TABLE_LIST *table_list, bool is_proc, append a user or role name to a buffer that will be later used as an error message */ static void append_user(THD *thd, String *str, - const LEX_STRING *u, const LEX_STRING *h) + const LEX_CSTRING *u, const LEX_CSTRING *h) { if (str->length()) str->append(','); @@ -6795,11 +6827,11 @@ static int can_grant_role_callback(ACL_USER_BASE *grantee, return 0; // keep searching if (grantee->flags & IS_ROLE) - pair= find_role_grant_pair(&grantee->user, &empty_lex_str, &role->user); + pair= find_role_grant_pair(&grantee->user, &empty_clex_str, &role->user); else { ACL_USER *user= (ACL_USER *)grantee; - LEX_STRING host= { user->host.hostname, user->hostname_length }; + LEX_CSTRING host= { user->host.hostname, user->hostname_length }; pair= find_role_grant_pair(&user->user, &host, &role->user); } if (!pair->with_admin) @@ -6844,9 +6876,9 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) bool create_new_user, no_auto_create_user; String wrong_users; LEX_USER *user, *granted_role; - LEX_STRING rolename; - LEX_STRING username; - LEX_STRING hostname; + LEX_CSTRING rolename; + LEX_CSTRING username; + LEX_CSTRING hostname; ACL_ROLE *role, *role_as_user; List_iterator <LEX_USER> user_list(list); @@ -6900,9 +6932,9 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) } if (!(role_as_user= find_acl_role(thd->security_ctx->priv_role))) { - LEX_STRING ls= { thd->security_ctx->priv_role, - strlen(thd->security_ctx->priv_role) }; - append_user(thd, &wrong_users, &ls, &empty_lex_str); + LEX_CSTRING ls= { thd->security_ctx->priv_role, + strlen(thd->security_ctx->priv_role) }; + append_user(thd, &wrong_users, &ls, &empty_clex_str); result= 1; continue; } @@ -6910,13 +6942,13 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) /* can not grant current_role to current_role */ if (granted_role->user.str == current_role.str) { - append_user(thd, &wrong_users, &role_as_user->user, &empty_lex_str); + append_user(thd, &wrong_users, &role_as_user->user, &empty_clex_str); result= 1; continue; } username.str= thd->security_ctx->priv_role; username.length= strlen(username.str); - hostname= empty_lex_str; + hostname= empty_clex_str; } else if (user->user.str == current_user.str) { @@ -6932,12 +6964,12 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) hostname= user->host; else if ((role_as_user= find_acl_role(user->user.str))) - hostname= empty_lex_str; + hostname= empty_clex_str; else { if (is_invalid_role_name(username.str)) { - append_user(thd, &wrong_users, &username, &empty_lex_str); + append_user(thd, &wrong_users, &username, &empty_clex_str); result= 1; continue; } @@ -6999,7 +7031,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) if (role_as_user && traverse_role_graph_down(role, 0, 0, 0) == ROLE_CYCLE_FOUND) { - append_user(thd, &wrong_users, &username, &empty_lex_str); + append_user(thd, &wrong_users, &username, &empty_clex_str); result= 1; undo_add_role_user_mapping(grantee, role); continue; @@ -7034,7 +7066,7 @@ bool mysql_grant_role(THD *thd, List <LEX_USER> &list, bool revoke) thd->lex->with_admin_option, hash_entry, revoke)) { - append_user(thd, &wrong_users, &username, &empty_lex_str); + append_user(thd, &wrong_users, &username, &empty_clex_str); result= 1; if (!revoke) { @@ -7339,16 +7371,10 @@ static bool grant_load(THD *thd, continue; } } - if (procs_priv.routine_type()->val_int() == TYPE_ENUM_PROCEDURE) - { - hash= &proc_priv_hash; - } - else - if (procs_priv.routine_type()->val_int() == TYPE_ENUM_FUNCTION) - { - hash= &func_priv_hash; - } - else + uint type= procs_priv.routine_type()->val_int(); + const Sp_handler *sph= Sp_handler::handler((stored_procedure_type) + type); + if (!sph || !(hash= sph->get_priv_hash())) { sql_print_warning("'procs_priv' entry '%s' " "ignored, bad routine type", @@ -7522,7 +7548,7 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); Security_context *sctx= thd->security_ctx; uint i; - ulong orig_want_access= want_access; + ulong original_want_access= want_access; bool locked= 0; GRANT_TABLE *grant_table; GRANT_TABLE *grant_table_role= NULL; @@ -7556,6 +7582,16 @@ bool check_grant(THD *thd, ulong want_access, TABLE_LIST *tables, TABLE_LIST *const t_ref= tl->correspondent_table ? tl->correspondent_table : tl; sctx= t_ref->security_ctx ? t_ref->security_ctx : thd->security_ctx; + ulong orig_want_access= original_want_access; + + if (t_ref->sequence) + { + /* We want to have either SELECT or INSERT rights to sequences depending + on how they are accessed + */ + orig_want_access= ((t_ref->lock_type == TL_WRITE_ALLOW_WRITE) ? + INSERT_ACL : SELECT_ACL); + } const ACL_internal_table_access *access= get_cached_table_access(&t_ref->grant.m_internal, @@ -7890,7 +7926,7 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, for (; !fields->end_of_fields(); fields->next()) { - const char *field_name= fields->name(); + LEX_CSTRING *field_name= fields->name(); if (table_name != fields->get_table_name()) { @@ -7927,16 +7963,15 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, if (grant_table) { GRANT_COLUMN *grant_column= - column_hash_search(grant_table, field_name, - (uint) strlen(field_name)); + column_hash_search(grant_table, field_name->str, field_name->length); if (grant_column) have_access= grant_column->rights; } if (grant_table_role) { GRANT_COLUMN *grant_column= - column_hash_search(grant_table_role, field_name, - (uint) strlen(field_name)); + column_hash_search(grant_table_role, field_name->str, + field_name->length); if (grant_column) have_access|= grant_column->rights; } @@ -7968,7 +8003,7 @@ err: command, sctx->priv_user, sctx->host_or_ip, - fields->name(), + fields->name()->str, table_name); return 1; } @@ -8080,7 +8115,7 @@ bool check_grant_db(THD *thd, const char *db) thd Thread handler want_access Bits of privileges user needs to have procs List of routines to check. The user should have 'want_access' - is_proc True if the list is all procedures, else functions + sph SP handler no_errors If 0 then we write an error. The error is sent directly to the client @@ -8090,7 +8125,8 @@ bool check_grant_db(THD *thd, const char *db) ****************************************************************************/ bool check_grant_routine(THD *thd, ulong want_access, - TABLE_LIST *procs, bool is_proc, bool no_errors) + TABLE_LIST *procs, const Sp_handler *sph, + bool no_errors) { TABLE_LIST *table; Security_context *sctx= thd->security_ctx; @@ -8108,12 +8144,12 @@ bool check_grant_routine(THD *thd, ulong want_access, { GRANT_NAME *grant_proc; if ((grant_proc= routine_hash_search(host, sctx->ip, table->db, user, - table->table_name, is_proc, 0))) + table->table_name, sph, 0))) table->grant.privilege|= grant_proc->privs; if (role[0]) /* current role set check */ { if ((grant_proc= routine_hash_search("", NULL, table->db, role, - table->table_name, is_proc, 0))) + table->table_name, sph, 0))) table->grant.privilege|= grant_proc->privs; } @@ -8162,7 +8198,7 @@ err: */ bool check_routine_level_acl(THD *thd, const char *db, const char *name, - bool is_proc) + const Sp_handler *sph) { bool no_routine_acl= 1; GRANT_NAME *grant_proc; @@ -8171,7 +8207,7 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, if ((grant_proc= routine_hash_search(sctx->priv_host, sctx->ip, db, sctx->priv_user, - name, is_proc, 0))) + name, sph, 0))) no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); if (no_routine_acl && sctx->priv_role[0]) /* current set role check */ @@ -8179,7 +8215,7 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, if ((grant_proc= routine_hash_search("", NULL, db, sctx->priv_role, - name, is_proc, 0))) + name, sph, 0))) no_routine_acl= !(grant_proc->privs & SHOW_PROC_ACLS); } mysql_rwlock_unlock(&LOCK_grant); @@ -8451,12 +8487,12 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role) if (show_table_and_column_privileges(thd, role->user.str, "", buff, sizeof(buff))) return TRUE; - if (show_routine_grants(thd, role->user.str, "", &proc_priv_hash, - STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff))) + if (show_routine_grants(thd, role->user.str, "", &sp_handler_procedure, + buff, sizeof(buff))) return TRUE; - if (show_routine_grants(thd, role->user.str, "", &func_priv_hash, - STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff))) + if (show_routine_grants(thd, role->user.str, "", &sp_handler_function, + buff, sizeof(buff))) return TRUE; return FALSE; @@ -8469,8 +8505,9 @@ static bool print_grants_for_role(THD *thd, ACL_ROLE * role) that a role can never happen here, so *rolename will never be assigned to */ -static bool check_show_access(THD *thd, LEX_USER *lex_user, char **username, - char **hostname, char **rolename) +static bool check_show_access(THD *thd, LEX_USER *lex_user, + const char **username, + const char **hostname, const char **rolename) { DBUG_ENTER("check_show_access"); @@ -8519,26 +8556,26 @@ static bool check_show_access(THD *thd, LEX_USER *lex_user, char **username, bool mysql_show_create_user(THD *thd, LEX_USER *lex_user) { - char *username= NULL, *hostname= NULL; + const char *username= NULL, *hostname= NULL; char buff[1024]; //Show create user should not take more than 1024 bytes. Protocol *protocol= thd->protocol; bool error= false; ACL_USER *acl_user; + uint head_length; DBUG_ENTER("mysql_show_create_user"); if (check_show_access(thd, lex_user, &username, &hostname, NULL)) DBUG_RETURN(TRUE); List<Item> field_list; - strxmov(buff, "CREATE USER for ", username, "@", hostname, NullS); + head_length= (uint) (strxmov(buff, "CREATE USER for ", username, "@", + hostname, NullS) - buff); Item_string *field = new (thd->mem_root) Item_string_ascii(thd, "", 0); if (!field) - { - my_error(ER_OUTOFMEMORY, MYF(0)); DBUG_RETURN(true); - } - field->name= buff; + field->name.str= buff; + field->name.length= head_length; field->max_length= sizeof(buff); field_list.push_back(field, thd->mem_root); if (protocol->send_result_set_metadata(&field_list, @@ -8593,10 +8630,12 @@ static int show_grants_callback(ACL_USER_BASE *role, void *data) } void mysql_show_grants_get_fields(THD *thd, List<Item> *fields, - const char *name) + const char *name, size_t length) { Item_string *field=new (thd->mem_root) Item_string_ascii(thd, "", 0); - field->name= (char *) name; + /* Set name explicit to avoid character set conversions */ + field->name.str= name; + field->name.length= length; field->max_length=1024; fields->push_back(field, thd->mem_root); } @@ -8616,7 +8655,7 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) ACL_ROLE *acl_role= NULL; char buff[1024]; Protocol *protocol= thd->protocol; - char *username= NULL, *hostname= NULL, *rolename= NULL; + const char *username= NULL, *hostname= NULL, *rolename= NULL, *end; DBUG_ENTER("mysql_show_grants"); if (!initialized) @@ -8631,11 +8670,11 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) List<Item> field_list; if (!username) - strxmov(buff,"Grants for ",rolename, NullS); + end= strxmov(buff,"Grants for ",rolename, NullS); else - strxmov(buff,"Grants for ",username,"@",hostname, NullS); + end= strxmov(buff,"Grants for ",username,"@",hostname, NullS); - mysql_show_grants_get_fields(thd, &field_list, buff); + mysql_show_grants_get_fields(thd, &field_list, buff, (uint) (end-buff)); if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)) @@ -8673,12 +8712,12 @@ bool mysql_show_grants(THD *thd, LEX_USER *lex_user) if (show_table_and_column_privileges(thd, username, hostname, buff, sizeof(buff))) goto end; - if (show_routine_grants(thd, username, hostname, &proc_priv_hash, - STRING_WITH_LEN("PROCEDURE"), buff, sizeof(buff))) + if (show_routine_grants(thd, username, hostname, &sp_handler_procedure, + buff, sizeof(buff))) goto end; - if (show_routine_grants(thd, username, hostname, &func_priv_hash, - STRING_WITH_LEN("FUNCTION"), buff, sizeof(buff))) + if (show_routine_grants(thd, username, hostname, &sp_handler_function, + buff, sizeof(buff))) goto end; if (show_proxy_grants(thd, username, hostname, buff, sizeof(buff))) @@ -8716,9 +8755,9 @@ end: DBUG_RETURN(error); } -static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_STRING *u, - const LEX_STRING *h, - const LEX_STRING *r) +static ROLE_GRANT_PAIR *find_role_grant_pair(const LEX_CSTRING *u, + const LEX_CSTRING *h, + const LEX_CSTRING *r) { char buf[1024]; String pair_key(buf, sizeof(buf), &my_charset_bin); @@ -8739,7 +8778,7 @@ static bool show_role_grants(THD *thd, const char *username, { uint counter; Protocol *protocol= thd->protocol; - LEX_STRING host= {const_cast<char*>(hostname), strlen(hostname)}; + LEX_CSTRING host= {const_cast<char*>(hostname), strlen(hostname)}; String grant(buff,sizeof(buff),system_charset_info); for (counter= 0; counter < acl_entry->role_grants.elements; counter++) @@ -9058,12 +9097,13 @@ static bool show_table_and_column_privileges(THD *thd, const char *username, static int show_routine_grants(THD* thd, const char *username, const char *hostname, - HASH *hash, const char *type, int typelen, + const Sp_handler *sph, char *buff, int buffsize) { uint counter, index; int error= 0; Protocol *protocol= thd->protocol; + HASH *hash= sph->get_priv_hash(); /* Add routine access */ for (index=0 ; index < hash->records ; index++) { @@ -9117,7 +9157,7 @@ static int show_routine_grants(THD* thd, } } global.append(STRING_WITH_LEN(" ON ")); - global.append(type,typelen); + global.append(sph->type_lex_cstring()); global.append(' '); append_identifier(thd, &global, grant_proc->db, strlen(grant_proc->db)); @@ -9550,7 +9590,7 @@ static int handle_grant_struct(enum enum_acl_lists struct_no, bool drop, DBUG_RETURN(1); /* this calls for a role update */ - char *old_key= acl_role->user.str; + const char *old_key= acl_role->user.str; size_t old_key_length= acl_role->user.length; if (drop) { @@ -10492,6 +10532,45 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list) DBUG_RETURN(result); } + +static bool +mysql_revoke_sp_privs(THD *thd, + Grant_tables *tables, + const Sp_handler *sph, + const LEX_USER *lex_user) +{ + bool rc= false; + uint counter, revoked; + do { + HASH *hash= sph->get_priv_hash(); + for (counter= 0, revoked= 0 ; counter < hash->records ; ) + { + const char *user,*host; + GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); + user= safe_str(grant_proc->user); + host= safe_str(grant_proc->host.hostname); + + if (!strcmp(lex_user->user.str, user) && + !strcmp(lex_user->host.str, host)) + { + if (replace_routine_table(thd, grant_proc, + tables->procs_priv_table().table(), + *lex_user, + grant_proc->db, grant_proc->tname, + sph, ~(ulong)0, 1) == 0) + { + revoked= 1; + continue; + } + rc= true; // Something went wrong + } + counter++; + } + } while (revoked); + return rc; +} + + /* Revoke all privileges from a list of users. @@ -10508,7 +10587,7 @@ int mysql_alter_user(THD* thd, List<LEX_USER> &users_list) bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) { - uint counter, revoked, is_proc; + uint counter, revoked; int result; ACL_DB *acl_db; DBUG_ENTER("mysql_revoke_all"); @@ -10637,32 +10716,9 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list) } while (revoked); /* Remove procedure access */ - for (is_proc=0; is_proc<2; is_proc++) do { - HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; - for (counter= 0, revoked= 0 ; counter < hash->records ; ) - { - const char *user,*host; - GRANT_NAME *grant_proc= (GRANT_NAME*) my_hash_element(hash, counter); - user= safe_str(grant_proc->user); - host= safe_str(grant_proc->host.hostname); - - if (!strcmp(lex_user->user.str,user) && - !strcmp(lex_user->host.str, host)) - { - if (replace_routine_table(thd, grant_proc, - tables.procs_priv_table().table(), - *lex_user, - grant_proc->db, grant_proc->tname, - is_proc, ~(ulong)0, 1) == 0) - { - revoked= 1; - continue; - } - result= -1; // Something went wrong - } - counter++; - } - } while (revoked); + if (mysql_revoke_sp_privs(thd, &tables, &sp_handler_function, lex_user) || + mysql_revoke_sp_privs(thd, &tables, &sp_handler_procedure, lex_user)) + result= -1; ACL_USER_BASE *user_or_role; /* remove role grants */ @@ -10814,11 +10870,11 @@ Silence_routine_definer_errors::handle_condition( */ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc) + const Sp_handler *sph) { uint counter, revoked; int result; - HASH *hash= is_proc ? &proc_priv_hash : &func_priv_hash; + HASH *hash= sph->get_priv_hash(); Silence_routine_definer_errors error_handler; DBUG_ENTER("sp_revoke_privileges"); @@ -10854,7 +10910,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, if (replace_routine_table(thd, grant_proc, tables.procs_priv_table().table(), lex_user, grant_proc->db, grant_proc->tname, - is_proc, ~(ulong)0, 1) == 0) + sph, ~(ulong)0, 1) == 0) { revoked= 1; continue; @@ -10879,7 +10935,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, @param thd The current thread. @param sp_db @param sp_name - @param is_proc + @param sph @return @retval FALSE Success @@ -10887,7 +10943,7 @@ bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, */ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc) + const Sp_handler *sph) { Security_context *sctx= thd->security_ctx; LEX_USER *combo; @@ -10898,7 +10954,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, Dummy_error_handler error_handler; DBUG_ENTER("sp_grant_privileges"); - if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + if (!(combo=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) DBUG_RETURN(TRUE); combo->user.str= sctx->user; @@ -10949,7 +11005,7 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, as all errors will be handled later. */ thd->push_internal_handler(&error_handler); - result= mysql_routine_grant(thd, tables, is_proc, user_list, + result= mysql_routine_grant(thd, tables, sph, user_list, DEFAULT_CREATE_PROC_ACLS, FALSE, FALSE); thd->pop_internal_handler(); DBUG_RETURN(result); @@ -11111,8 +11167,8 @@ static int enabled_roles_insert(ACL_USER_BASE *role, void *context_data) struct APPLICABLE_ROLES_DATA { TABLE *table; - const LEX_STRING host; - const LEX_STRING user_and_host; + const LEX_CSTRING host; + const LEX_CSTRING user_and_host; ACL_USER *user; }; @@ -11123,9 +11179,9 @@ applicable_roles_insert(ACL_USER_BASE *grantee, ACL_ROLE *role, void *ptr) CHARSET_INFO *cs= system_charset_info; TABLE *table= data->table; bool is_role= grantee != data->user; - const LEX_STRING *user_and_host= is_role ? &grantee->user + const LEX_CSTRING *user_and_host= is_role ? &grantee->user : &data->user_and_host; - const LEX_STRING *host= is_role ? &empty_lex_str : &data->host; + const LEX_CSTRING *host= is_role ? &empty_clex_str : &data->host; restore_record(table, s->default_values); table->field[0]->store(user_and_host->str, user_and_host->length, cs); @@ -11735,7 +11791,7 @@ void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, ****************************************************************************/ bool check_routine_level_acl(THD *thd, const char *db, const char *name, - bool is_proc) + const Sp_handler *sph) { return FALSE; } @@ -11783,7 +11839,7 @@ LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock) if (lock) mysql_mutex_lock(&acl_cache->lock); if (find_acl_role(dup->user.str)) - dup->host= empty_lex_str; + dup->host= empty_clex_str; else dup->host= host_not_specified; if (lock) @@ -11798,7 +11854,7 @@ LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock) struct ACL_internal_schema_registry_entry { - const LEX_STRING *m_name; + const LEX_CSTRING *m_name; const ACL_internal_schema_access *m_access; }; @@ -11819,7 +11875,7 @@ static uint m_registry_array_size= 0; @param access the schema ACL specific rules */ void ACL_internal_schema_registry::register_schema - (const LEX_STRING *name, const ACL_internal_schema_access *access) + (const LEX_CSTRING *name, const ACL_internal_schema_access *access) { DBUG_ASSERT(m_registry_array_size < array_elements(registry_array)); @@ -11926,10 +11982,11 @@ struct MPVIO_EXT :public MYSQL_PLUGIN_VIO MYSQL_SERVER_AUTH_INFO auth_info; ACL_USER *acl_user; ///< a copy, independent from acl_users array plugin_ref plugin; ///< what plugin we're under - LEX_STRING db; ///< db name from the handshake packet + LEX_CSTRING db; ///< db name from the handshake packet /** when restarting a plugin this caches the last client reply */ struct { - char *plugin, *pkt; ///< pointers into NET::buff + const char *plugin; + char *pkt; ///< pointer into NET::buff uint pkt_len; } cached_client_reply; /** this caches the first plugin packet for restart request on the client */ @@ -12428,7 +12485,7 @@ static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) if (find_mpvio_user(mpvio)) DBUG_RETURN(1); - char *client_plugin; + const char *client_plugin; if (thd->client_capabilities & CLIENT_PLUGIN_AUTH) { if (next_field >= end) @@ -12622,10 +12679,13 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, db_len= safe_strlen(db); char *next_field; - char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0); + const char *client_plugin= next_field= passwd + passwd_len + (db ? db_len + 1 : 0); - /* Since 4.1 all database names are stored in utf8 */ - if (thd->copy_with_error(system_charset_info, &mpvio->db, + /* + Since 4.1 all database names are stored in utf8 + The cast is ok as copy_with_error will create a new area for db + */ + if (thd->copy_with_error(system_charset_info, (LEX_STRING*) &mpvio->db, thd->charset(), db, db_len)) return packet_error; @@ -12652,7 +12712,7 @@ static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, Security_context *sctx= thd->security_ctx; - my_free(sctx->user); + my_free((char*) sctx->user); if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME)))) return packet_error; /* The error is set by my_strdup(). */ @@ -13013,7 +13073,7 @@ static bool acl_check_ssl(THD *thd, const ACL_USER *acl_user) } -static int do_auth_once(THD *thd, const LEX_STRING *auth_plugin_name, +static int do_auth_once(THD *thd, const LEX_CSTRING *auth_plugin_name, MPVIO_EXT *mpvio) { int res= CR_OK, old_status= MPVIO_EXT::FAILURE; @@ -13095,7 +13155,7 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) { int res= CR_OK; MPVIO_EXT mpvio; - const LEX_STRING *auth_plugin_name= default_auth_plugin_name; + const LEX_CSTRING *auth_plugin_name= default_auth_plugin_name; enum enum_server_command command= com_change_user_pkt_len ? COM_CHANGE_USER : COM_CONNECT; DBUG_ENTER("acl_authenticate"); diff --git a/sql/sql_acl.h b/sql/sql_acl.h index daa36f6c32a..6164c6fa57d 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -182,10 +182,10 @@ enum mysql_db_table_field extern const TABLE_FIELD_DEF mysql_db_table_def; extern bool mysql_user_table_is_in_short_password_format; -extern LEX_STRING host_not_specified; -extern LEX_STRING current_user; -extern LEX_STRING current_role; -extern LEX_STRING current_user_and_current_role; +extern LEX_CSTRING host_not_specified; +extern LEX_CSTRING current_user; +extern LEX_CSTRING current_role; +extern LEX_CSTRING current_user_and_current_role; static inline int access_denied_error_code(int passwd_used) @@ -194,7 +194,6 @@ static inline int access_denied_error_code(int passwd_used) : ER_ACCESS_DENIED_ERROR; } - /* prototypes */ bool hostname_requires_resolving(const char *hostname); @@ -204,8 +203,8 @@ void acl_free(bool end=0); ulong acl_get(const char *host, const char *ip, const char *user, const char *db, my_bool db_is_pattern); bool acl_authenticate(THD *thd, uint com_change_user_pkt_len); -bool acl_getroot(Security_context *sctx, char *user, char *host, - char *ip, char *db); +bool acl_getroot(Security_context *sctx, const char *user, const char *host, + const char *ip, const char *db); bool acl_check_host(const char *host, const char *ip); bool check_change_password(THD *thd, LEX_USER *user); bool change_password(THD *thd, LEX_USER *user); @@ -216,7 +215,7 @@ bool mysql_grant(THD *thd, const char *db, List <LEX_USER> &user_list, int mysql_table_grant(THD *thd, TABLE_LIST *table, List <LEX_USER> &user_list, List <LEX_COLUMN> &column_list, ulong rights, bool revoke); -bool mysql_routine_grant(THD *thd, TABLE_LIST *table, bool is_proc, +bool mysql_routine_grant(THD *thd, TABLE_LIST *table, const Sp_handler *sph, List <LEX_USER> &user_list, ulong rights, bool revoke, bool write_to_binlog); bool grant_init(); @@ -232,7 +231,8 @@ bool check_column_grant_in_table_ref(THD *thd, TABLE_LIST * table_ref, bool check_grant_all_columns(THD *thd, ulong want_access, Field_iterator_table_ref *fields); bool check_grant_routine(THD *thd, ulong want_access, - TABLE_LIST *procs, bool is_proc, bool no_error); + TABLE_LIST *procs, const Sp_handler *sph, + bool no_error); bool check_grant_db(THD *thd,const char *db); bool check_global_access(THD *thd, ulong want_access, bool no_errors= false); bool check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, @@ -243,7 +243,7 @@ ulong get_column_grant(THD *thd, GRANT_INFO *grant, const char *db_name, const char *table_name, const char *field_name); void mysql_show_grants_get_fields(THD *thd, List<Item> *fields, - const char *name); + const char *name, size_t length); bool mysql_show_grants(THD *thd, LEX_USER *user); bool mysql_show_create_user(THD *thd, LEX_USER *user); int fill_schema_enabled_roles(THD *thd, TABLE_LIST *tables, COND *cond); @@ -258,11 +258,11 @@ bool mysql_revoke_all(THD *thd, List <LEX_USER> &list); void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table); bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc); + const Sp_handler *sph); bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, - bool is_proc); + const Sp_handler *sph); bool check_routine_level_acl(THD *thd, const char *db, const char *name, - bool is_proc); + const Sp_handler *sph); bool is_acl_user(const char *host, const char *user); int fill_schema_user_privileges(THD *thd, TABLE_LIST *tables, COND *cond); int fill_schema_schema_privileges(THD *thd, TABLE_LIST *tables, COND *cond); @@ -385,7 +385,7 @@ public: class ACL_internal_schema_registry { public: - static void register_schema(const LEX_STRING *name, + static void register_schema(const LEX_CSTRING *name, const ACL_internal_schema_access *access); static const ACL_internal_schema_access *lookup(const char *name); }; @@ -401,8 +401,8 @@ get_cached_table_access(GRANT_INTERNAL_INFO *grant_internal_info, bool acl_check_proxy_grant_access (THD *thd, const char *host, const char *user, bool with_grant); -int acl_setrole(THD *thd, char *rolename, ulonglong access); -int acl_check_setrole(THD *thd, char *rolename, ulonglong *access); +int acl_setrole(THD *thd, const char *rolename, ulonglong access); +int acl_check_setrole(THD *thd, const char *rolename, ulonglong *access); int acl_check_set_default_role(THD *thd, const char *host, const char *user); int acl_set_default_role(THD *thd, const char *host, const char *user, const char *rolename); diff --git a/sql/sql_admin.cc b/sql/sql_admin.cc index fcbcd5d31bf..1e46fc44c5e 100644 --- a/sql/sql_admin.cc +++ b/sql/sql_admin.cc @@ -189,7 +189,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list, if (!mysql_file_stat(key_file_misc, from, &stat_info, MYF(0))) goto end; // Can't use USE_FRM flag - my_snprintf(tmp, sizeof(tmp), "%s-%lx_%lx", + my_snprintf(tmp, sizeof(tmp), "%s-%lx_%llx", from, current_pid, thd->thread_id); if (table_list->table) @@ -341,16 +341,17 @@ static bool open_only_one_table(THD* thd, TABLE_LIST* table, if (lex->alter_info.flags & Alter_info::ALTER_ADMIN_PARTITION || !is_view_operator_func) { - table->required_type=FRMTYPE_TABLE; - DBUG_ASSERT(!lex->only_view); + table->required_type= TABLE_TYPE_NORMAL; + DBUG_ASSERT(lex->table_type != TABLE_TYPE_VIEW); } - else if (lex->only_view) + else if (lex->table_type == TABLE_TYPE_VIEW) { - table->required_type= FRMTYPE_VIEW; + table->required_type= lex->table_type; } - else if (!lex->only_view && lex->sql_command == SQLCOM_REPAIR) + else if ((lex->table_type != TABLE_TYPE_VIEW) && + lex->sql_command == SQLCOM_REPAIR) { - table->required_type= FRMTYPE_TABLE; + table->required_type= TABLE_TYPE_NORMAL; } if (lex->sql_command == SQLCOM_CHECK || @@ -393,7 +394,9 @@ static bool open_only_one_table(THD* thd, TABLE_LIST* table, open_and_lock_tables(thd, table, TRUE, 0)); } +#ifndef DBUG_OFF dbug_err: +#endif thd->prepare_derived_at_open= FALSE; @@ -488,7 +491,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, for (table= tables; table; table= table->next_local) { char table_name[SAFE_NAME_LEN*2+2]; - char* db = table->db; + const char *db= table->db; bool fatal_error=0; bool open_error; bool collect_eis= FALSE; @@ -841,7 +844,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_EIS_FOR_FIELD, ER_THD(thd, ER_NO_EIS_FOR_FIELD), - (*field_ptr)->field_name); + (*field_ptr)->field_name.str); } } else @@ -1233,7 +1236,7 @@ err2: */ bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* tables, - LEX_STRING *key_cache_name) + const LEX_CSTRING *key_cache_name) { HA_CHECK_OPT check_opt; KEY_CACHE *key_cache; diff --git a/sql/sql_admin.h b/sql/sql_admin.h index 96594fad0cb..e7f4086540a 100644 --- a/sql/sql_admin.h +++ b/sql/sql_admin.h @@ -20,7 +20,7 @@ #define SQL_ADMIN_MSG_TEXT_SIZE (128 * 1024) bool mysql_assign_to_keycache(THD* thd, TABLE_LIST* table_list, - LEX_STRING *key_cache_name); + const LEX_CSTRING *key_cache_name); bool mysql_preload_keys(THD* thd, TABLE_LIST* table_list); int reassign_keycache_tables(THD* thd, KEY_CACHE *src_cache, KEY_CACHE *dst_cache); diff --git a/sql/sql_alter.cc b/sql/sql_alter.cc index 3592fa3cfbd..0194f26d217 100644 --- a/sql/sql_alter.cc +++ b/sql/sql_alter.cc @@ -50,7 +50,7 @@ Alter_info::Alter_info(const Alter_info &rhs, MEM_ROOT *mem_root) } -bool Alter_info::set_requested_algorithm(const LEX_STRING *str) +bool Alter_info::set_requested_algorithm(const LEX_CSTRING *str) { // To avoid adding new keywords to the grammar, we match strings here. if (!my_strcasecmp(system_charset_info, str->str, "INPLACE")) @@ -65,7 +65,7 @@ bool Alter_info::set_requested_algorithm(const LEX_STRING *str) } -bool Alter_info::set_requested_lock(const LEX_STRING *str) +bool Alter_info::set_requested_lock(const LEX_CSTRING *str) { // To avoid adding new keywords to the grammar, we match strings here. if (!my_strcasecmp(system_charset_info, str->str, "NONE")) @@ -95,10 +95,15 @@ Alter_table_ctx::Alter_table_ctx() { } +/* + TODO: new_name_arg changes if lower case table names. + Should be copied or converted before call +*/ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, uint tables_opened_arg, - char *new_db_arg, char *new_name_arg) + const char *new_db_arg, + const char *new_name_arg) : datetime_field(NULL), error_if_not_empty(false), tables_opened(tables_opened_arg), new_db(new_db_arg), new_name(new_name_arg), @@ -126,13 +131,14 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, if (lower_case_table_names == 1) // Convert new_name/new_alias to lower case { - my_casedn_str(files_charset_info, new_name); + my_casedn_str(files_charset_info, (char*) new_name); new_alias= new_name; } else if (lower_case_table_names == 2) // Convert new_name to lower case { - strmov(new_alias= new_alias_buff, new_name); - my_casedn_str(files_charset_info, new_name); + new_alias= new_alias_buff; + strmov(new_alias_buff, new_name); + my_casedn_str(files_charset_info, (char*) new_name); } else new_alias= new_name; // LCTN=0 => case sensitive + case preserving @@ -154,7 +160,7 @@ Alter_table_ctx::Alter_table_ctx(THD *thd, TABLE_LIST *table_list, new_name= table_name; } - my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%lx", tmp_file_prefix, + my_snprintf(tmp_name, sizeof(tmp_name), "%s-%lx_%llx", tmp_file_prefix, current_pid, thd->thread_id); /* Safety fix for InnoDB */ if (lower_case_table_names) diff --git a/sql/sql_alter.h b/sql/sql_alter.h index 5668a0f52be..c0232dd7358 100644 --- a/sql/sql_alter.h +++ b/sql/sql_alter.h @@ -38,91 +38,65 @@ public: type of index to be added/dropped. */ - // Set for ADD [COLUMN] - static const uint ALTER_ADD_COLUMN = 1L << 0; - - // Set for DROP [COLUMN] - static const uint ALTER_DROP_COLUMN = 1L << 1; - - // Set for CHANGE [COLUMN] | MODIFY [CHANGE] - // Set by mysql_recreate_table() - static const uint ALTER_CHANGE_COLUMN = 1L << 2; - - // Set for ADD INDEX | ADD KEY | ADD PRIMARY KEY | ADD UNIQUE KEY | - // ADD UNIQUE INDEX | ALTER ADD [COLUMN] - static const uint ALTER_ADD_INDEX = 1L << 3; - - // Set for DROP PRIMARY KEY | DROP FOREIGN KEY | DROP KEY | DROP INDEX - static const uint ALTER_DROP_INDEX = 1L << 4; - - // Set for RENAME [TO] - static const uint ALTER_RENAME = 1L << 5; - - // Set for ORDER BY - static const uint ALTER_ORDER = 1L << 6; - - // Set for table_options - static const uint ALTER_OPTIONS = 1L << 7; - - // Set for ALTER [COLUMN] ... SET DEFAULT ... | DROP DEFAULT - static const uint ALTER_CHANGE_COLUMN_DEFAULT = 1L << 8; - - // Set for DISABLE KEYS | ENABLE KEYS - static const uint ALTER_KEYS_ONOFF = 1L << 9; - - // Set for FORCE - // Set for ENGINE(same engine) - // Set by mysql_recreate_table() - static const uint ALTER_RECREATE = 1L << 10; - - // Set for ADD PARTITION - static const uint ALTER_ADD_PARTITION = 1L << 11; - - // Set for DROP PARTITION - static const uint ALTER_DROP_PARTITION = 1L << 12; - - // Set for COALESCE PARTITION - static const uint ALTER_COALESCE_PARTITION = 1L << 13; - - // Set for REORGANIZE PARTITION ... INTO - static const uint ALTER_REORGANIZE_PARTITION = 1L << 14; - - // Set for partition_options - static const uint ALTER_PARTITION = 1L << 15; - - // Set for LOAD INDEX INTO CACHE ... PARTITION - // Set for CACHE INDEX ... PARTITION - static const uint ALTER_ADMIN_PARTITION = 1L << 16; - - // Set for REORGANIZE PARTITION - static const uint ALTER_TABLE_REORG = 1L << 17; - - // Set for REBUILD PARTITION - static const uint ALTER_REBUILD_PARTITION = 1L << 18; - - // Set for partitioning operations specifying ALL keyword - static const uint ALTER_ALL_PARTITION = 1L << 19; - - // Set for REMOVE PARTITIONING - static const uint ALTER_REMOVE_PARTITIONING = 1L << 20; - - // Set for ADD FOREIGN KEY - static const uint ADD_FOREIGN_KEY = 1L << 21; - - // Set for DROP FOREIGN KEY - static const uint DROP_FOREIGN_KEY = 1L << 22; - - // Set for EXCHANGE PARITION - static const uint ALTER_EXCHANGE_PARTITION = 1L << 23; - - // Set by Sql_cmd_alter_table_truncate_partition::execute() - static const uint ALTER_TRUNCATE_PARTITION = 1L << 24; - - // Set for ADD [COLUMN] FIRST | AFTER - static const uint ALTER_COLUMN_ORDER = 1L << 25; - - static const uint ALTER_ADD_CHECK_CONSTRAINT = 1L << 27; - static const uint ALTER_DROP_CHECK_CONSTRAINT = 1L << 28; + enum operations_used_flags + { + // Set for ADD [COLUMN] + ALTER_ADD_COLUMN = 1L << 0, + // Set for DROP [COLUMN] + ALTER_DROP_COLUMN = 1L << 1, + // Set for CHANGE [COLUMN] | MODIFY [CHANGE] & mysql_recreate_table + ALTER_CHANGE_COLUMN = 1L << 2, + // Set for ADD INDEX | ADD KEY | ADD PRIMARY KEY | ADD UNIQUE KEY | + // ADD UNIQUE INDEX | ALTER ADD [COLUMN] + ALTER_ADD_INDEX = 1L << 3, + // Set for DROP PRIMARY KEY | DROP FOREIGN KEY | DROP KEY | DROP INDEX + ALTER_DROP_INDEX = 1L << 4, + // Set for RENAME [TO] + ALTER_RENAME = 1L << 5, + // Set for ORDER BY + ALTER_ORDER = 1L << 6, + // Set for table_options + ALTER_OPTIONS = 1L << 7, + // Set for ALTER [COLUMN] ... SET DEFAULT ... | DROP DEFAULT + ALTER_CHANGE_COLUMN_DEFAULT = 1L << 8, + // Set for DISABLE KEYS | ENABLE KEYS + ALTER_KEYS_ONOFF = 1L << 9, + // Set for FORCE, ENGINE(same engine), by mysql_recreate_table() + ALTER_RECREATE = 1L << 10, + // Set for ADD PARTITION + ALTER_ADD_PARTITION = 1L << 11, + // Set for DROP PARTITION + ALTER_DROP_PARTITION = 1L << 12, + // Set for COALESCE PARTITION + ALTER_COALESCE_PARTITION = 1L << 13, + // Set for REORGANIZE PARTITION ... INTO + ALTER_REORGANIZE_PARTITION = 1L << 14, + // Set for partition_options + ALTER_PARTITION = 1L << 15, + // Set for LOAD INDEX INTO CACHE ... PARTITION + // Set for CACHE INDEX ... PARTITION + ALTER_ADMIN_PARTITION = 1L << 16, + // Set for REORGANIZE PARTITION + ALTER_TABLE_REORG = 1L << 17, + // Set for REBUILD PARTITION + ALTER_REBUILD_PARTITION = 1L << 18, + // Set for partitioning operations specifying ALL keyword + ALTER_ALL_PARTITION = 1L << 19, + // Set for REMOVE PARTITIONING + ALTER_REMOVE_PARTITIONING = 1L << 20, + // Set for ADD FOREIGN KEY + ADD_FOREIGN_KEY = 1L << 21, + // Set for DROP FOREIGN KEY + DROP_FOREIGN_KEY = 1L << 22, + // Set for EXCHANGE PARITION + ALTER_EXCHANGE_PARTITION = 1L << 23, + // Set by Sql_cmd_alter_table_truncate_partition::execute() + ALTER_TRUNCATE_PARTITION = 1L << 24, + // Set for ADD [COLUMN] FIRST | AFTER + ALTER_COLUMN_ORDER = 1L << 25, + ALTER_ADD_CHECK_CONSTRAINT = 1L << 27, + ALTER_DROP_CHECK_CONSTRAINT = 1L << 28 + }; enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE }; @@ -172,14 +146,17 @@ public: // List of columns, used by both CREATE and ALTER TABLE. List<Create_field> create_list; - static const uint CHECK_CONSTRAINT_IF_NOT_EXISTS= 1; + enum flags_bits + { + CHECK_CONSTRAINT_IF_NOT_EXISTS= 1 + }; List<Virtual_column_info> check_constraint_list; // Type of ALTER TABLE operation. uint flags; // Enable or disable keys. enum_enable_or_disable keys_onoff; // List of partitions. - List<char> partition_names; + List<const char> partition_names; // Number of partitions. uint num_parts; // Type of ALTER TABLE algorithm. @@ -239,7 +216,7 @@ public: @retval false Supported value found, state updated @retval true Not supported value, no changes made */ - bool set_requested_algorithm(const LEX_STRING *str); + bool set_requested_algorithm(const LEX_CSTRING *str); /** @@ -252,7 +229,7 @@ public: @retval true Not supported value, no changes made */ - bool set_requested_lock(const LEX_STRING *str); + bool set_requested_lock(const LEX_CSTRING *str); private: Alter_info &operator=(const Alter_info &rhs); // not implemented @@ -267,7 +244,7 @@ public: Alter_table_ctx(); Alter_table_ctx(THD *thd, TABLE_LIST *table_list, uint tables_opened_arg, - char *new_db_arg, char *new_name_arg); + const char *new_db_arg, const char *new_name_arg); /** @return true if the table is moved to another database, false otherwise. @@ -329,12 +306,12 @@ public: Create_field *datetime_field; bool error_if_not_empty; uint tables_opened; - char *db; - char *table_name; - char *alias; - char *new_db; - char *new_name; - char *new_alias; + const char *db; + const char *table_name; + const char *alias; + const char *new_db; + const char *new_name; + const char *new_alias; char tmp_name[80]; /** Indicates that if a row is deleted during copying of data from old version @@ -408,6 +385,29 @@ public: /** + Sql_cmd_alter_sequence represents the ALTER SEQUENCE statement. +*/ +class Sql_cmd_alter_sequence : public Sql_cmd +{ +public: + /** + Constructor, used to represent a ALTER TABLE statement. + */ + Sql_cmd_alter_sequence() + {} + + ~Sql_cmd_alter_sequence() + {} + + enum_sql_command sql_command_code() const + { + return SQLCOM_ALTER_SEQUENCE; + } + bool execute(THD *thd); +}; + + +/** Sql_cmd_alter_table_tablespace represents ALTER TABLE IMPORT/DISCARD TABLESPACE statements. */ diff --git a/sql/sql_analyse.cc b/sql/sql_analyse.cc index 91a80c552cb..d6af0410ad2 100644 --- a/sql/sql_analyse.cc +++ b/sql/sql_analyse.cc @@ -72,7 +72,7 @@ Procedure * proc_analyse_init(THD *thd, ORDER *param, select_result *result, List<Item> &field_list) { - char *proc_name = (*param->item)->name; + const char *proc_name = (*param->item)->name.str; analyse *pc = new analyse(result); field_info **f_info; DBUG_ENTER("proc_analyse_init"); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 9c752b4f882..c0736130cfe 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -502,7 +502,7 @@ err_with_reopen: struct close_cached_connection_tables_arg { THD *thd; - LEX_STRING *connection; + LEX_CSTRING *connection; TABLE_LIST *tables; }; @@ -547,7 +547,7 @@ end: } -bool close_cached_connection_tables(THD *thd, LEX_STRING *connection) +bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connection) { close_cached_connection_tables_arg argument; DBUG_ENTER("close_cached_connections"); @@ -1481,7 +1481,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, Open_table_context *ot_ctx) TABLE *table; const char *key; uint key_length; - char *alias= table_list->alias; + const char *alias= table_list->alias; uint flags= ot_ctx->get_flags(); MDL_ticket *mdl_ticket; TABLE_SHARE *share; @@ -1783,7 +1783,11 @@ retry_share: my_error(ER_WRONG_MRG_TABLE, MYF(0)); goto err_lock; } - + if (table_list->sequence) + { + my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db, table_list->alias); + goto err_lock; + } /* This table is a view. Validate its metadata version: in particular, that it was a view when the statement was prepared. @@ -1934,6 +1938,11 @@ retry_share: DBUG_RETURN(true); } #endif + if (table_list->sequence && table->s->table_type != TABLE_TYPE_SEQUENCE) + { + my_error(ER_NOT_SEQUENCE, MYF(0), table_list->db, table_list->alias); + DBUG_RETURN(true); + } table->init(thd, table_list); @@ -3118,7 +3127,7 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, DEBUG_SYNC(thd, "after_shared_lock_pname"); /* Ensures the routine is up-to-date and cached, if exists. */ - if (sp_cache_routine(thd, rt, has_prelocking_list, &sp)) + if (rt->sp_cache_routine(thd, has_prelocking_list, &sp)) DBUG_RETURN(TRUE); /* Remember the version of the routine in the parse tree. */ @@ -3143,7 +3152,7 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx, Validating routine version is unnecessary, since CALL does not affect the prepared statement prelocked list. */ - if (sp_cache_routine(thd, rt, FALSE, &sp)) + if (rt->sp_cache_routine(thd, false, &sp)) DBUG_RETURN(TRUE); } } @@ -3650,8 +3659,9 @@ lock_table_names(THD *thd, const DDL_options_st &options, DBUG_RETURN(FALSE); /* Check if CREATE TABLE without REPLACE was used */ - create_table= thd->lex->sql_command == SQLCOM_CREATE_TABLE && - !options.or_replace(); + create_table= ((thd->lex->sql_command == SQLCOM_CREATE_TABLE || + thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE) && + !options.or_replace()); if (!(flags & MYSQL_OPEN_SKIP_SCOPED_MDL_LOCK)) { @@ -4168,8 +4178,9 @@ handle_routine(THD *thd, Query_tables_list *prelocking_ctx, @note this can be changed to use a hash, instead of scanning the linked list, if the performance of this function will ever become an issue */ -static bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_STRING *db, - LEX_STRING *table, thr_lock_type lock_type) +static bool table_already_fk_prelocked(TABLE_LIST *tl, LEX_CSTRING *db, + LEX_CSTRING *table, + thr_lock_type lock_type) { for (; tl; tl= tl->next_global ) { @@ -4519,7 +4530,7 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, /* Set requested lock type. */ table_l->lock_type= lock_type; /* Allow to open real tables only. */ - table_l->required_type= FRMTYPE_TABLE; + table_l->required_type= TABLE_TYPE_NORMAL; /* Open the table. */ if (open_and_lock_tables(thd, table_l, FALSE, flags, @@ -4575,7 +4586,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type, THD_STAGE_INFO(thd, stage_opening_tables); thd->current_tablenr= 0; /* open_ltable can be used only for BASIC TABLEs */ - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; /* This function can't properly handle requests for such metadata locks. */ DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_UPGRADABLE); @@ -4764,6 +4775,51 @@ end: } +/** + Open a table to read its structure, e.g. for: + - SHOW FIELDS + - delayed SP variable data type definition: DECLARE a t1.a%TYPE + + The flag MYSQL_OPEN_GET_NEW_TABLE is passed to make %TYPE work + in stored functions, as during a stored function call + (e.g. in a SELECT query) the tables referenced in %TYPE can already be locked, + and attempt to open it again would return an error in open_table(). + + The flag MYSQL_OPEN_GET_NEW_TABLE is not really needed for + SHOW FIELDS or for a "CALL sp()" statement, but it's not harmful, + so let's pass it unconditionally. +*/ + +bool open_tables_only_view_structure(THD *thd, TABLE_LIST *table_list, + bool can_deadlock) +{ + DBUG_ENTER("open_tables_only_view_structure"); + /* + Let us set fake sql_command so views won't try to merge + themselves into main statement. If we don't do this, + SELECT * from information_schema.xxxx will cause problems. + SQLCOM_SHOW_FIELDS is used because it satisfies + 'LEX::only_view_structure()'. + */ + enum_sql_command save_sql_command= thd->lex->sql_command; + thd->lex->sql_command= SQLCOM_SHOW_FIELDS; + bool rc= (thd->open_temporary_tables(table_list) || + open_normal_and_derived_tables(thd, table_list, + (MYSQL_OPEN_IGNORE_FLUSH | + MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL | + MYSQL_OPEN_GET_NEW_TABLE | + (can_deadlock ? + MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)), + DT_INIT | DT_PREPARE | DT_CREATE)); + /* + Restore old value of sql_command back as it is being looked at in + process_table() function. + */ + thd->lex->sql_command= save_sql_command; + DBUG_RETURN(rc); +} + + /* Mark all real tables in the list as free for reuse. @@ -5196,7 +5252,7 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, for (; !field_it.end_of_fields(); field_it.next()) { - if (!my_strcasecmp(system_charset_info, field_it.name(), name)) + if (!my_strcasecmp(system_charset_info, field_it.name()->str, name)) { // in PS use own arena or data will be freed after prepare if (register_tree_change && @@ -5218,27 +5274,10 @@ find_field_in_view(THD *thd, TABLE_LIST *table_list, *ref != NULL means that *ref contains the item that we need to replace. If the item was aliased by the user, set the alias to the replacing item. - We need to set alias on both ref itself and on ref real item. */ if (*ref && !(*ref)->is_autogenerated_name) - { - if (register_tree_change) - { - item->set_name_for_rollback(thd, (*ref)->name, - (*ref)->name_length, - system_charset_info); - item->real_item()->set_name_for_rollback(thd, (*ref)->name, - (*ref)->name_length, - system_charset_info); - } - else - { - item->set_name(thd, (*ref)->name, (*ref)->name_length, - system_charset_info); - item->real_item()->set_name(thd, (*ref)->name, (*ref)->name_length, - system_charset_info); - } - } + item->set_name(thd, (*ref)->name.str, (*ref)->name.length, + system_charset_info); if (register_tree_change) thd->change_item_tree(ref, item); else @@ -5298,7 +5337,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, for (nj_col= NULL, curr_nj_col= field_it++; curr_nj_col; curr_nj_col= field_it++) { - if (!my_strcasecmp(system_charset_info, curr_nj_col->name(), name)) + if (!my_strcasecmp(system_charset_info, curr_nj_col->name()->str, name)) { if (nj_col) { @@ -5325,15 +5364,10 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, *ref != NULL means that *ref contains the item that we need to replace. If the item was aliased by the user, set the alias to the replacing item. - We need to set alias on both ref itself and on ref real item. */ if (*ref && !(*ref)->is_autogenerated_name) - { - item->set_name(thd, (*ref)->name, (*ref)->name_length, + item->set_name(thd, (*ref)->name.str, (*ref)->name.length, system_charset_info); - item->real_item()->set_name(thd, (*ref)->name, (*ref)->name_length, - system_charset_info); - } if (register_tree_change && arena) thd->restore_active_arena(arena, &backup); @@ -5370,7 +5404,7 @@ find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name, nj_col->table_field->fix_fields(thd, &ref)) { DBUG_PRINT("info", ("column '%s' was dropped by the concurrent connection", - nj_col->table_field->name)); + nj_col->table_field->name.str)); DBUG_RETURN(NULL); } DBUG_ASSERT(ref == 0); // Should not have changed @@ -5418,7 +5452,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, /* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */ if (cached_field_index < table->s->fields && !my_strcasecmp(system_charset_info, - table->field[cached_field_index]->field_name, name)) + table->field[cached_field_index]->field_name.str, name)) field_ptr= table->field + cached_field_index; else if (table->s->name_hash.records) { @@ -5438,7 +5472,8 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, if (!(field_ptr= table->field)) DBUG_RETURN((Field *)0); for (; *field_ptr; ++field_ptr) - if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name)) + if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name.str, + name)) break; } @@ -5558,6 +5593,13 @@ find_field_in_table_ref(THD *thd, TABLE_LIST *table_list, strcmp(db_name, table_list->db))))) DBUG_RETURN(0); + /* + Don't allow usage of fields in sequence table that is opened as part of + NEXT VALUE for sequence_name + */ + if (table_list->sequence) + DBUG_RETURN(0); + *actual_table= NULL; if (table_list->field_translation) @@ -5694,7 +5736,8 @@ Field *find_field_in_table_sef(TABLE *table, const char *name) if (!(field_ptr= table->field)) return (Field *)0; for (; *field_ptr; ++field_ptr) - if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name)) + if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name.str, + name)) break; } if (field_ptr) @@ -5747,8 +5790,8 @@ find_field_in_tables(THD *thd, Item_ident *item, Field *found=0; const char *db= item->db_name; const char *table_name= item->table_name; - const char *name= item->field_name; - uint length=(uint) strlen(name); + const char *name= item->field_name.str; + uint length= item->field_name.length; char name_buff[SAFE_NAME_LEN+1]; TABLE_LIST *cur_table= first_table; TABLE_LIST *actual_table; @@ -5795,7 +5838,7 @@ find_field_in_tables(THD *thd, Item_ident *item, #endif } else - found= find_field_in_table_ref(thd, table_ref, name, length, item->name, + found= find_field_in_table_ref(thd, table_ref, name, length, item->name.str, NULL, NULL, ref, check_privileges, TRUE, &(item->cached_field_index), register_tree_change, @@ -5863,7 +5906,7 @@ find_field_in_tables(THD *thd, Item_ident *item, cur_table= cur_table->next_name_resolution_table) { Field *cur_field= find_field_in_table_ref(thd, cur_table, name, length, - item->name, db, table_name, ref, + item->name.str, db, table_name, ref, (thd->lex->sql_command == SQLCOM_SHOW_FIELDS) ? false : check_privileges, @@ -5880,7 +5923,7 @@ find_field_in_tables(THD *thd, Item_ident *item, thd->clear_error(); cur_field= find_field_in_table_ref(thd, cur_table, name, length, - item->name, db, table_name, ref, + item->name.str, db, table_name, ref, false, allow_rowid, &(item->cached_field_index), @@ -5889,7 +5932,7 @@ find_field_in_tables(THD *thd, Item_ident *item, if (cur_field) { Field *nf=new Field_null(NULL,0,Field::NONE, - cur_field->field_name, + &cur_field->field_name, &my_charset_bin); nf->init(cur_table->table); cur_field= nf; @@ -6005,7 +6048,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, uint n_items= limit == 0 ? items.elements : limit; Item **found=0, **found_unaliased= 0, *item; const char *db_name=0; - const char *field_name=0; + const LEX_CSTRING *field_name= 0; const char *table_name=0; bool found_unaliased_non_uniq= 0; /* @@ -6021,7 +6064,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, find->type() == Item::REF_ITEM); if (is_ref_by_name) { - field_name= ((Item_ident*) find)->field_name; + field_name= &((Item_ident*) find)->field_name; table_name= ((Item_ident*) find)->table_name; db_name= ((Item_ident*) find)->db_name; } @@ -6029,7 +6072,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, for (uint i= 0; i < n_items; i++) { item= li++; - if (field_name && + if (field_name && field_name->str && (item->real_item()->type() == Item::FIELD_ITEM || ((item->type() == Item::REF_ITEM) && (((Item_ref *)item)->ref_type() == Item_ref::VIEW_REF)))) @@ -6042,7 +6085,7 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, (if this field created from expression argument of group_concat()), => we have to check presence of name before compare */ - if (!item_field->name) + if (!item_field->name.str) continue; if (table_name) @@ -6063,9 +6106,9 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, item_field->field_name and item_field->table_name can be 0x0 if item is not fix_field()'ed yet. */ - if (item_field->field_name && item_field->table_name && - !my_strcasecmp(system_charset_info, item_field->field_name, - field_name) && + if (item_field->field_name.str && item_field->table_name && + !my_strcasecmp(system_charset_info, item_field->field_name.str, + field_name->str) && !my_strcasecmp(table_alias_charset, item_field->table_name, table_name) && (!db_name || (item_field->db_name && @@ -6095,10 +6138,10 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, else { int fname_cmp= my_strcasecmp(system_charset_info, - item_field->field_name, - field_name); + item_field->field_name.str, + field_name->str); if (!my_strcasecmp(system_charset_info, - item_field->name,field_name)) + item_field->name.str,field_name->str)) { /* If table name was not given we should scan through aliases @@ -6142,8 +6185,9 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter, } else if (!table_name) { - if (is_ref_by_name && find->name && item->name && - !my_strcasecmp(system_charset_info,item->name,find->name)) + if (is_ref_by_name && find->name.str && item->name.str && + find->name.length == item->name.length && + !my_strcasecmp(system_charset_info,item->name.str, find->name.str)) { found= li.ref(); *counter= i; @@ -6317,17 +6361,17 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next()) { bool found= FALSE; - const char *field_name_1; + const LEX_CSTRING *field_name_1; /* true if field_name_1 is a member of using_fields */ bool is_using_column_1; if (!(nj_col_1= it_1.get_or_create_column_ref(thd, leaf_1))) goto err; field_name_1= nj_col_1->name(); is_using_column_1= using_fields && - test_if_string_in_list(field_name_1, using_fields); + test_if_string_in_list(field_name_1->str, using_fields); DBUG_PRINT ("info", ("field_name_1=%s.%s", nj_col_1->table_name() ? nj_col_1->table_name() : "", - field_name_1)); + field_name_1->str)); /* Find a field with the same name in table_ref_2. @@ -6340,14 +6384,14 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next()) { Natural_join_column *cur_nj_col_2; - const char *cur_field_name_2; + const LEX_CSTRING *cur_field_name_2; if (!(cur_nj_col_2= it_2.get_or_create_column_ref(thd, leaf_2))) goto err; cur_field_name_2= cur_nj_col_2->name(); DBUG_PRINT ("info", ("cur_field_name_2=%s.%s", cur_nj_col_2->table_name() ? cur_nj_col_2->table_name() : "", - cur_field_name_2)); + cur_field_name_2->str)); /* Compare the two columns and check for duplicate common fields. @@ -6360,13 +6404,14 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, here. These columns must be checked only on unqualified reference by name (e.g. in SELECT list). */ - if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2)) + if (!my_strcasecmp(system_charset_info, field_name_1->str, + cur_field_name_2->str)) { DBUG_PRINT ("info", ("match c1.is_common=%d", nj_col_1->is_common)); if (cur_nj_col_2->is_common || (found && (!using_fields || is_using_column_1))) { - my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where); + my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1->str, thd->where); goto err; } nj_col_2= cur_nj_col_2; @@ -6457,10 +6502,10 @@ mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2, DBUG_PRINT ("info", ("%s.%s and %s.%s are common", nj_col_1->table_name() ? nj_col_1->table_name() : "", - nj_col_1->name(), + nj_col_1->name()->str, nj_col_2->table_name() ? nj_col_2->table_name() : "", - nj_col_2->name())); + nj_col_2->name()->str)); if (field_1) { @@ -6598,7 +6643,7 @@ store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join, goto err; } if (!my_strcasecmp(system_charset_info, - common_field->name(), using_field_name_ptr)) + common_field->name()->str, using_field_name_ptr)) break; // Found match } } @@ -6925,8 +6970,7 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields, while (wild_num && (item= it++)) { if (item->type() == Item::FIELD_ITEM && - ((Item_field*) item)->field_name && - ((Item_field*) item)->field_name[0] == '*' && + ((Item_field*) item)->field_name.str == star_clex_str.str && !((Item_field*) item)->field) { uint elem= fields.elements; @@ -7547,7 +7591,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, !(fld->have_privileges= (get_column_grant(thd, field_iterator.grant(), field_iterator.get_db_name(), - field_table_name, fld->field_name) & + field_table_name, fld->field_name.str) & VIEW_ANY_ACL))) { my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), "ANY", @@ -7876,7 +7920,7 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, fld= (Item_field*)f++; if (!(field= fld->field_for_view_update())) { - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name); + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name.str); goto err; } DBUG_ASSERT(field->field->table == table_arg); @@ -7888,7 +7932,7 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, { if (!(field= fld->field_for_view_update())) { - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name); + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), fld->name.str); goto err; } value=v++; @@ -7906,7 +7950,7 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), - rfield->field_name, table->s->table_name.str); + rfield->field_name.str, table->s->table_name.str); } if (rfield->stored_in_db() && (value->save_in_field(rfield, 0)) < 0 && !ignore_errors) @@ -8151,7 +8195,7 @@ fill_record(THD *thd, TABLE *table, Field **ptr, List<Item> &values, push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, ER_THD(thd, ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), - field->field_name, table->s->table_name.str); + field->field_name.str, table->s->table_name.str); } } @@ -8415,7 +8459,7 @@ int init_ftfuncs(THD *thd, SELECT_LEX *select_lex, bool no_order) } -bool is_equal(const LEX_STRING *a, const LEX_STRING *b) +bool is_equal(const LEX_CSTRING *a, const LEX_CSTRING *b) { return a->length == b->length && !strncmp(a->str, b->str, a->length); } diff --git a/sql/sql_base.h b/sql/sql_base.h index 374ac56c3d8..7a8d27c9147 100644 --- a/sql/sql_base.h +++ b/sql/sql_base.h @@ -254,12 +254,14 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l, Prelocking_strategy *prelocking_strategy); bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags, uint dt_phases); +bool open_tables_only_view_structure(THD *thd, TABLE_LIST *tables, + bool can_deadlock); bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags); int decide_logging_format(THD *thd, TABLE_LIST *tables); void close_thread_table(THD *thd, TABLE **table_ptr); TABLE_LIST *unique_table(THD *thd, TABLE_LIST *table, TABLE_LIST *table_list, bool check_alias); -bool is_equal(const LEX_STRING *a, const LEX_STRING *b); +bool is_equal(const LEX_CSTRING *a, const LEX_CSTRING *b); class Open_tables_backup; /* Functions to work with system tables. */ @@ -277,7 +279,7 @@ void close_performance_schema_table(THD *thd, Open_tables_state *backup); bool close_cached_tables(THD *thd, TABLE_LIST *tables, bool wait_for_refresh, ulong timeout); -bool close_cached_connection_tables(THD *thd, LEX_STRING *connect_string); +bool close_cached_connection_tables(THD *thd, LEX_CSTRING *connect_string); void close_all_tables_for_name(THD *thd, TABLE_SHARE *share, ha_extra_function extra, TABLE *skip_table); diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in index a40a064dc4b..6a491a3e7bb 100644 --- a/sql/sql_builtin.cc.in +++ b/sql/sql_builtin.cc.in @@ -11,7 +11,13 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Note that sql_builtin.cc is automatically built by sql_bultin.cc.in + and cmake/plugin.cmake +*/ #include <my_global.h> #include <mysql/plugin.h> diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc index 9077af58d31..7849e16a839 100644 --- a/sql/sql_cache.cc +++ b/sql/sql_cache.cc @@ -2335,7 +2335,7 @@ void Query_cache::invalidate(THD *thd, const char *key, uint32 key_length, Remove all cached queries that uses the given database. */ -void Query_cache::invalidate(THD *thd, char *db) +void Query_cache::invalidate(THD *thd, const char *db) { DBUG_ENTER("Query_cache::invalidate (db)"); if (is_disabled()) diff --git a/sql/sql_cache.h b/sql/sql_cache.h index ad9cac76b0a..07a80f778b8 100644 --- a/sql/sql_cache.h +++ b/sql/sql_cache.h @@ -78,7 +78,7 @@ struct Query_cache_tls; struct LEX; class THD; -typedef my_bool (*qc_engine_callback)(THD *thd, char *table_key, +typedef my_bool (*qc_engine_callback)(THD *thd, const char *table_key, uint key_length, ulonglong *engine_data); @@ -482,7 +482,7 @@ protected: my_bool using_transactions); /* Remove all queries that uses any of the tables in following database */ - void invalidate(THD *thd, char *db); + void invalidate(THD *thd, const char *db); /* Remove all queries that uses any of the listed following table */ void invalidate_by_MyISAM_filename(const char *filename); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 22d6137bfba..b40ee63fac6 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -55,6 +55,7 @@ #include <mysys_err.h> #include <limits.h> +#include "sp_head.h" #include "sp_rcontext.h" #include "sp_cache.h" #include "transaction.h" @@ -100,6 +101,23 @@ extern "C" void free_user_var(user_var_entry *entry) my_free(entry); } +/* Functions for last-value-from-sequence hash */ + +extern "C" uchar *get_sequence_last_key(SEQUENCE_LAST_VALUE *entry, + size_t *length, + my_bool not_used + __attribute__((unused))) +{ + *length= entry->length; + return (uchar*) entry->key; +} + +extern "C" void free_sequence_last(SEQUENCE_LAST_VALUE *entry) +{ + delete entry; +} + + bool Key_part_spec::operator==(const Key_part_spec& other) const { return length == other.length && @@ -233,7 +251,7 @@ bool Foreign_key::validate(List<Create_field> &table_fields) while ((sql_field= it++) && my_strcasecmp(system_charset_info, column->field_name.str, - sql_field->field_name)) {} + sql_field->field_name.str)) {} if (!sql_field) { my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name.str); @@ -552,7 +570,7 @@ char *thd_get_error_context_description(THD *thd, char *buffer, len= my_snprintf(header, sizeof(header), "MySQL thread id %lu, OS thread handle %lu, query id %lu", - thd->thread_id, (ulong) thd->real_id, (ulong) thd->query_id); + (ulong) thd->thread_id, (ulong) thd->real_id, (ulong) thd->query_id); str.length(0); str.append(header, len); @@ -888,6 +906,9 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, HASH_THREAD_SPECIFIC); + my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0, + (my_hash_get_key) get_sequence_last_key, + (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC); sp_proc_cache= NULL; sp_func_cache= NULL; @@ -927,8 +948,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) } m_binlog_invoker= INVOKER_NONE; - memset(&invoker_user, 0, sizeof(invoker_user)); - memset(&invoker_host, 0, sizeof(invoker_host)); + invoker.init(); prepare_derived_at_open= FALSE; create_tmp_table_for_derived= FALSE; save_prep_leaf_list= FALSE; @@ -1075,9 +1095,10 @@ void THD::raise_note_printf(uint sql_errno, ...) } Sql_condition* THD::raise_condition(uint sql_errno, - const char* sqlstate, - Sql_condition::enum_warning_level level, - const char* msg) + const char* sqlstate, + Sql_condition::enum_warning_level level, + const Sql_user_condition_identity &ucid, + const char* msg) { Diagnostics_area *da= get_stmt_da(); Sql_condition *cond= NULL; @@ -1136,7 +1157,7 @@ Sql_condition* THD::raise_condition(uint sql_errno, if (!da->is_error()) { set_row_count_func(-1); - da->set_error_status(sql_errno, msg, sqlstate, cond); + da->set_error_status(sql_errno, msg, sqlstate, ucid, cond); } } @@ -1150,7 +1171,7 @@ Sql_condition* THD::raise_condition(uint sql_errno, if (!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY || sql_errno == ER_OUTOFMEMORY))) { - cond= da->push_warning(this, sql_errno, sqlstate, level, msg); + cond= da->push_warning(this, sql_errno, sqlstate, level, ucid, msg); } DBUG_RETURN(cond); } @@ -1180,11 +1201,11 @@ char *thd_strmake(MYSQL_THD thd, const char *str, size_t size) } extern "C" -LEX_STRING *thd_make_lex_string(THD *thd, LEX_STRING *lex_str, +LEX_CSTRING *thd_make_lex_string(THD *thd, LEX_CSTRING *lex_str, const char *str, size_t size, int allocate_lex_string) { - return allocate_lex_string ? thd->make_lex_string(str, size) + return allocate_lex_string ? thd->make_clex_string(str, size) : thd->make_lex_string(lex_str, str, size); } @@ -1233,6 +1254,17 @@ extern "C" my_thread_id next_thread_id_noinline() } #endif + +const Type_handler *THD::type_handler_for_date() const +{ + if (!(variables.sql_mode & MODE_ORACLE)) + return &type_handler_newdate; + if (opt_mysql56_temporal_format) + return &type_handler_datetime2; + return &type_handler_datetime; +} + + /* Init common variables that has to be reset on start and on change_user */ @@ -1250,7 +1282,7 @@ void THD::init(void) variables.pseudo_thread_id= thread_id; variables.default_master_connection.str= default_master_connection_buff; - ::strmake(variables.default_master_connection.str, + ::strmake(default_master_connection_buff, global_system_variables.default_master_connection.str, variables.default_master_connection.length); @@ -1335,7 +1367,17 @@ void THD::init(void) DBUG_VOID_RETURN; } - + +bool THD::restore_from_local_lex_to_old_lex(LEX *oldlex) +{ + DBUG_ASSERT(lex->sphead); + if (lex->sphead->merge_lex(this, oldlex, lex)) + return true; + lex= oldlex; + return false; +} + + /* Updates some status variables to be used by update_global_user_stats */ void THD::update_stats(void) @@ -1429,6 +1471,9 @@ void THD::change_user(void) my_hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0, (my_hash_get_key) get_var_key, (my_hash_free_key) free_user_var, 0); + my_hash_init(&sequences, system_charset_info, SEQUENCES_HASH_SIZE, 0, 0, + (my_hash_get_key) get_sequence_last_key, + (my_hash_free_key) free_sequence_last, HASH_THREAD_SPECIFIC); sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); } @@ -1485,6 +1530,7 @@ void THD::cleanup(void) #endif /* defined(ENABLED_DEBUG_SYNC) */ my_hash_free(&user_vars); + my_hash_free(&sequences); sp_cache_clear(&sp_proc_cache); sp_cache_clear(&sp_func_cache); auto_inc_intervals_forced.empty(); @@ -2343,6 +2389,26 @@ bool THD::convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs) } +Item_string *THD::make_string_literal(const char *str, size_t length, + uint repertoire) +{ + if (!charset_is_collation_connection && + (repertoire != MY_REPERTOIRE_ASCII || + !my_charset_is_ascii_based(variables.collation_connection))) + { + LEX_STRING to; + if (convert_string(&to, variables.collation_connection, + str, length, variables.character_set_client)) + return NULL; + str= to.str; + length= to.length; + } + return new (mem_root) Item_string(this, str, length, + variables.collation_connection, + DERIVATION_COERCIBLE, repertoire); +} + + /* Update some cache variables when character set changes */ @@ -2696,7 +2762,7 @@ static String default_field_term("\t",default_charset_info); static String default_enclosed_and_line_start("", default_charset_info); static String default_xml_row_term("<row>", default_charset_info); -sql_exchange::sql_exchange(char *name, bool flag, +sql_exchange::sql_exchange(const char *name, bool flag, enum enum_filetype filetype_arg) :file_name(name), opt_enclosed(0), dumpfile(flag), skip_lines(0) { @@ -3091,7 +3157,7 @@ int select_export::send_data(List<Item> &items) ER_TRUNCATED_WRONG_VALUE_FOR_FIELD, ER_THD(thd, ER_TRUNCATED_WRONG_VALUE_FOR_FIELD), "string", printable_buff, - item->name, static_cast<long>(row_count)); + item->name.str, static_cast<long>(row_count)); } else if (copier.source_end_pos() < res->ptr() + res->length()) { @@ -3374,7 +3440,7 @@ int select_max_min_finder_subselect::send_data(List<Item> &items) { if (!cache) { - cache= Item_cache::get_cache(thd, val_item); + cache= val_item->get_cache(thd); switch (val_item->result_type()) { case REAL_RESULT: op= &select_max_min_finder_subselect::cmp_real; @@ -3496,15 +3562,29 @@ int select_exists_subselect::send_data(List<Item> &items) int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) { + my_var_sp *mvsp; unit= u; - - if (var_list.elements != list.elements) + m_var_sp_row= NULL; + + if (var_list.elements == 1 && + (mvsp= var_list.head()->get_my_var_sp()) && + mvsp->type_handler() == &type_handler_row) { - my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, - ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0)); - return 1; - } - return 0; + // SELECT INTO row_type_sp_variable + if (thd->spcont->get_item(mvsp->offset)->cols() != list.elements) + goto error; + m_var_sp_row= mvsp; + return 0; + } + + // SELECT INTO variable list + if (var_list.elements == list.elements) + return 0; + +error: + my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, + ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), MYF(0)); + return 1; } @@ -3830,7 +3910,7 @@ Statement_map::~Statement_map() bool my_var_user::set(THD *thd, Item *item) { - Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, name, item); + Item_func_set_user_var *suv= new (thd->mem_root) Item_func_set_user_var(thd, &name, item); suv->save_item_result(item); return suv->fix_fields(thd, 0) || suv->update(); } @@ -3840,12 +3920,30 @@ bool my_var_sp::set(THD *thd, Item *item) return thd->spcont->set_variable(thd, offset, &item); } -int select_dumpvar::send_data(List<Item> &items) +bool my_var_sp_row_field::set(THD *thd, Item *item) { + return thd->spcont->set_variable_row_field(thd, offset, m_field_offset, &item); +} + + +bool select_dumpvar::send_data_to_var_list(List<Item> &items) +{ + DBUG_ENTER("select_dumpvar::send_data_to_var_list"); List_iterator_fast<my_var> var_li(var_list); List_iterator<Item> it(items); Item *item; my_var *mv; + while ((mv= var_li++) && (item= it++)) + { + if (mv->set(thd, item)) + DBUG_RETURN(true); + } + DBUG_RETURN(false); +} + + +int select_dumpvar::send_data(List<Item> &items) +{ DBUG_ENTER("select_dumpvar::send_data"); if (unit->offset_limit_cnt) @@ -3858,11 +3956,11 @@ int select_dumpvar::send_data(List<Item> &items) my_message(ER_TOO_MANY_ROWS, ER_THD(thd, ER_TOO_MANY_ROWS), MYF(0)); DBUG_RETURN(1); } - while ((mv= var_li++) && (item= it++)) - { - if (mv->set(thd, item)) - DBUG_RETURN(1); - } + if (m_var_sp_row ? + thd->spcont->set_variable_row(thd, m_var_sp_row->offset, items) : + send_data_to_var_list(items)) + DBUG_RETURN(1); + DBUG_RETURN(thd->is_error()); } @@ -3892,7 +3990,8 @@ create_result_table(THD *thd_arg, List<Item> *column_types, bool is_union_distinct, ulonglong options, const char *table_alias, bool bit_fields_as_long, bool create_table, - bool keep_row_order) + bool keep_row_order, + uint hidden) { DBUG_ASSERT(table == 0); tmp_table_param.field_count= column_types->elements; @@ -3927,12 +4026,12 @@ void select_materialize_with_stats::reset() void select_materialize_with_stats::cleanup() { reset(); - select_union::cleanup(); + select_unit::cleanup(); } /** - Override select_union::send_data to analyze each row for NULLs and to + Override select_unit::send_data to analyze each row for NULLs and to update null_statistics before sending data to the client. @return TRUE if fatal error when sending data to the client @@ -3947,7 +4046,7 @@ int select_materialize_with_stats::send_data(List<Item> &items) uint nulls_in_row= 0; int res; - if ((res= select_union::send_data(items))) + if ((res= select_unit::send_data(items))) return res; if (table->null_catch_flags & REJECT_ROW_DUE_TO_NULL_FIELDS) { @@ -4059,7 +4158,7 @@ void Security_context::destroy() } if (user != delayed_user) { - my_free(user); + my_free((char*) user); user= NULL; } @@ -4069,7 +4168,7 @@ void Security_context::destroy() external_user= NULL; } - my_free(ip); + my_free((char*) ip); ip= NULL; } @@ -4085,7 +4184,7 @@ void Security_context::skip_grants() bool Security_context::set_user(char *user_arg) { - my_free(user); + my_free((char*) user); user= my_strdup(user_arg, MYF(0)); return user == 0; } @@ -4143,9 +4242,9 @@ bool Security_context::set_user(char *user_arg) bool Security_context:: change_security_context(THD *thd, - LEX_STRING *definer_user, - LEX_STRING *definer_host, - LEX_STRING *db, + LEX_CSTRING *definer_user, + LEX_CSTRING *definer_host, + LEX_CSTRING *db, Security_context **backup) { bool needs_change; @@ -5324,8 +5423,8 @@ void THD::get_definer(LEX_USER *definer, bool role) if (slave_thread && has_invoker()) #endif { - definer->user = invoker_user; - definer->host= invoker_host; + definer->user= invoker.user; + definer->host= invoker.host; definer->reset_auth(); } else @@ -5707,7 +5806,7 @@ int xid_cache_iterate(THD *thd, my_hash_walk_action action, void *arg) int THD::decide_logging_format(TABLE_LIST *tables) { DBUG_ENTER("THD::decide_logging_format"); - DBUG_PRINT("info", ("Query: %s", query())); + DBUG_PRINT("info", ("Query: %.*s", (uint) query_length(), query())); DBUG_PRINT("info", ("variables.binlog_format: %lu", variables.binlog_format)); DBUG_PRINT("info", ("lex->get_stmt_unsafe_flags(): 0x%x", @@ -5874,7 +5973,8 @@ int THD::decide_logging_format(TABLE_LIST *tables) if (prev_write_table && prev_write_table->file->ht != table->table->file->ht) multi_write_engine= TRUE; - if (table->table->s->non_determinstic_insert) + if (table->table->s->non_determinstic_insert && + !(sql_command_flags[lex->sql_command] & CF_SCHEMA_CHANGE)) has_write_tables_with_unsafe_statements= true; trans= table->table->file->has_transactions(); @@ -7394,4 +7494,57 @@ bool Discrete_intervals_list::append(Discrete_interval *new_interval) DBUG_RETURN(0); } + +void AUTHID::copy(MEM_ROOT *mem_root, const LEX_CSTRING *user_name, + const LEX_CSTRING *host_name) +{ + user.str= strmake_root(mem_root, user_name->str, user_name->length); + user.length= user_name->length; + + host.str= strmake_root(mem_root, host_name->str, host_name->length); + host.length= host_name->length; +} + + +/* + Set from a string in 'user@host' format. + This method resebmles parse_user(), + but does not need temporary buffers. +*/ +void AUTHID::parse(const char *str, size_t length) +{ + const char *p= strrchr(str, '@'); + if (!p) + { + user.str= str; + user.length= length; + host= null_clex_str; + } + else + { + user.str= str; + user.length= (size_t) (p - str); + host.str= p + 1; + host.length= (size_t) (length - user.length - 1); + if (user.length && !host.length) + host= host_not_specified; // 'user@' -> 'user@%' + } + if (user.length > USERNAME_LENGTH) + user.length= USERNAME_LENGTH; + if (host.length > HOSTNAME_LENGTH) + host.length= HOSTNAME_LENGTH; +} + + +void Database_qualified_name::copy(MEM_ROOT *mem_root, + const LEX_CSTRING &db, + const LEX_CSTRING &name) +{ + m_db.length= db.length; + m_db.str= strmake_root(mem_root, db.str, db.length); + m_name.length= name.length; + m_name.str= strmake_root(mem_root, name.str, name.length); +} + + #endif /* !defined(MYSQL_CLIENT) */ diff --git a/sql/sql_class.h b/sql/sql_class.h index e5aaba9b407..96a3cc3522f 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -233,14 +233,11 @@ typedef struct st_copy_info { class Key_part_spec :public Sql_alloc { public: - LEX_STRING field_name; + LEX_CSTRING field_name; uint length; - Key_part_spec(const LEX_STRING &name, uint len) - : field_name(name), length(len) + Key_part_spec(const LEX_CSTRING *name, uint len) + : field_name(*name), length(len) {} - Key_part_spec(const char *name, const size_t name_len, uint len) - : length(len) - { field_name.str= (char *)name; field_name.length= name_len; } bool operator==(const Key_part_spec& other) const; /** Construct a copy of this Key_part_spec. field_name is copied @@ -303,37 +300,26 @@ public: enum Keytype type; KEY_CREATE_INFO key_create_info; List<Key_part_spec> columns; - LEX_STRING name; + LEX_CSTRING name; engine_option_value *option_list; bool generated; - Key(enum Keytype type_par, const LEX_STRING &name_arg, + Key(enum Keytype type_par, const LEX_CSTRING *name_arg, ha_key_alg algorithm_arg, bool generated_arg, DDL_options_st ddl_options) :DDL_options(ddl_options), type(type_par), key_create_info(default_key_create_info), - name(name_arg), option_list(NULL), generated(generated_arg) + name(*name_arg), option_list(NULL), generated(generated_arg) { key_create_info.algorithm= algorithm_arg; } - Key(enum Keytype type_par, const LEX_STRING &name_arg, + Key(enum Keytype type_par, const LEX_CSTRING *name_arg, KEY_CREATE_INFO *key_info_arg, - bool generated_arg, List<Key_part_spec> &cols, + bool generated_arg, List<Key_part_spec> *cols, engine_option_value *create_opt, DDL_options_st ddl_options) :DDL_options(ddl_options), - type(type_par), key_create_info(*key_info_arg), columns(cols), - name(name_arg), option_list(create_opt), generated(generated_arg) + type(type_par), key_create_info(*key_info_arg), columns(*cols), + name(*name_arg), option_list(create_opt), generated(generated_arg) {} - Key(enum Keytype type_par, const char *name_arg, size_t name_len_arg, - KEY_CREATE_INFO *key_info_arg, bool generated_arg, - List<Key_part_spec> &cols, - engine_option_value *create_opt, DDL_options_st ddl_options) - :DDL_options(ddl_options), - type(type_par), key_create_info(*key_info_arg), columns(cols), - option_list(create_opt), generated(generated_arg) - { - name.str= (char *)name_arg; - name.length= name_len_arg; - } Key(const Key &rhs, MEM_ROOT *mem_root); virtual ~Key() {} /* Equality comparison of keys (ignoring name) */ @@ -351,19 +337,18 @@ class Foreign_key: public Key { public: enum fk_match_opt { FK_MATCH_UNDEF, FK_MATCH_FULL, FK_MATCH_PARTIAL, FK_MATCH_SIMPLE}; - - LEX_STRING ref_db; - LEX_STRING ref_table; + LEX_CSTRING ref_db; + LEX_CSTRING ref_table; List<Key_part_spec> ref_columns; uint delete_opt, update_opt, match_opt; - Foreign_key(const LEX_STRING &name_arg, List<Key_part_spec> &cols, - const LEX_STRING &ref_db_arg, const LEX_STRING &ref_table_arg, - List<Key_part_spec> &ref_cols, + Foreign_key(const LEX_CSTRING *name_arg, List<Key_part_spec> *cols, + const LEX_CSTRING *ref_db_arg, const LEX_CSTRING *ref_table_arg, + List<Key_part_spec> *ref_cols, uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg, DDL_options ddl_options) :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL, ddl_options), - ref_db(ref_db_arg), ref_table(ref_table_arg), ref_columns(ref_cols), + ref_db(*ref_db_arg), ref_table(*ref_table_arg), ref_columns(*ref_cols), delete_opt(delete_opt_arg), update_opt(update_opt_arg), match_opt(match_opt_arg) { @@ -672,7 +657,7 @@ typedef struct system_variables CHARSET_INFO *collation_connection; /* Names. These will be allocated in buffers in thd */ - LEX_STRING default_master_connection; + LEX_CSTRING default_master_connection; /* Error messages */ MY_LOCALE *lc_messages; @@ -703,6 +688,7 @@ typedef struct system_variables ulong session_track_transaction_info; my_bool session_track_schema; my_bool session_track_state_change; + my_bool sequence_read_skip_cache; ulong threadpool_priority; } SV; @@ -759,6 +745,7 @@ typedef struct system_status_var /* The following are for internal temporary tables */ ulong ha_tmp_update_count; ulong ha_tmp_write_count; + ulong ha_tmp_delete_count; ulong ha_prepare_count; ulong ha_icp_attempts; ulong ha_icp_match; @@ -1051,7 +1038,7 @@ public: */ enum enum_mark_columns mark_used_columns; - LEX_STRING name; /* name for named prepared statements */ + LEX_CSTRING name; /* name for named prepared statements */ LEX *lex; // parse tree descriptor /* Points to the query associated with this statement. It's const, but @@ -1082,6 +1069,10 @@ public: { return static_cast<uint32>(query_string.length()); } + inline char *query_end() const + { + return query_string.str() + query_string.length(); + } CHARSET_INFO *query_charset() const { return query_string.charset(); } void set_query_inner(const CSET_STRING &string_arg) { @@ -1149,7 +1140,7 @@ public: int insert(THD *thd, Statement *statement); - Statement *find_by_name(LEX_STRING *name) + Statement *find_by_name(LEX_CSTRING *name) { Statement *stmt; stmt= (Statement*)my_hash_search(&names_hash, (uchar*)name->str, @@ -1232,7 +1223,7 @@ public: ip - client IP */ const char *host; - char *user, *ip; + const char *user, *ip; char priv_user[USERNAME_LENGTH]; char proxy_user[USERNAME_LENGTH + MAX_HOSTNAME + 5]; /* The host privilege we are using */ @@ -1259,9 +1250,9 @@ public: #ifndef NO_EMBEDDED_ACCESS_CHECKS bool change_security_context(THD *thd, - LEX_STRING *definer_user, - LEX_STRING *definer_host, - LEX_STRING *db, + LEX_CSTRING *definer_user, + LEX_CSTRING *definer_host, + LEX_CSTRING *db, Security_context **backup); void @@ -2243,6 +2234,8 @@ public: chapter 'Miscellaneous functions', for functions GET_LOCK, RELEASE_LOCK. */ HASH ull_hash; + /* Hash of used seqeunces (for PREVIOUS value) */ + HASH sequences; #ifndef DBUG_OFF uint dbug_sentry; // watch out for memory corruption #endif @@ -2447,6 +2440,8 @@ private: uint binlog_table_maps; public: void issue_unsafe_warnings(); + void reset_unsafe_warnings() + { binlog_unsafe_warning_flags= 0; } uint get_binlog_table_maps() const { return binlog_table_maps; @@ -2913,7 +2908,7 @@ public: If this is a slave, the name of the connection stored here. This is used for taging error messages in the log files. */ - LEX_STRING connection_name; + LEX_CSTRING connection_name; char default_master_connection_buff[MAX_CONNECTION_NAME+1]; uint8 password; /* 0, 1 or 2 */ uint8 failed_com_change_user; @@ -3240,6 +3235,7 @@ public: { return !MY_TEST(variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES); } + const Type_handler *type_handler_for_date() const; inline my_time_t query_start() { query_start_used=1; return start_time; } inline ulong query_start_sec_part() { query_start_sec_part_used=1; return start_time_sec_part; } @@ -3391,7 +3387,20 @@ public: LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, uint length) { if (!(lex_str->str= strmake_root(mem_root, str, length))) + { + lex_str->length= 0; return 0; + } + lex_str->length= length; + return lex_str; + } + LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str, const char* str, uint length) + { + if (!(lex_str->str= strmake_root(mem_root, str, length))) + { + lex_str->length= 0; + return 0; + } lex_str->length= length; return lex_str; } @@ -3404,6 +3413,14 @@ public: return make_lex_string(lex_str, str, length); } + LEX_CSTRING *make_clex_string(const char* str, uint length) + { + LEX_CSTRING *lex_str; + if (!(lex_str= (LEX_CSTRING *)alloc_root(mem_root, sizeof(LEX_CSTRING)))) + return 0; + return make_lex_string(lex_str, str, length); + } + // Allocate LEX_STRING for character set conversion bool alloc_lex_string(LEX_STRING *dst, uint length) { @@ -3450,6 +3467,20 @@ public: bool convert_string(String *s, CHARSET_INFO *from_cs, CHARSET_INFO *to_cs); + /* + Create a string literal with optional client->connection conversion. + @param str - the string in the client character set + @param length - length of the string + @param repertoire - the repertoire of the string + */ + Item_string *make_string_literal(const char *str, size_t length, + uint repertoire); + Item_string *make_string_literal(const Lex_string_with_metadata_st &str) + { + uint repertoire= str.repertoire(variables.character_set_client); + return make_string_literal(str.str, str.length, repertoire); + } + void add_changed_table(TABLE *table); void add_changed_table(const char *key, long key_length); CHANGED_TABLE_LIST * changed_table_dup(const char *key, long key_length); @@ -3887,7 +3918,7 @@ public: allocate memory for a deep copy: current database may be freed after a statement is parsed but before it's executed. */ - bool copy_db_to(char **p_db, size_t *p_db_length) + bool copy_db_to(const char **p_db, size_t *p_db_length) { if (db == NULL) { @@ -3968,6 +3999,37 @@ public: */ void raise_note_printf(uint code, ...); + /** + @brief Push an error message into MySQL error stack with line + and position information. + + This function provides semantic action implementers with a way + to push the famous "You have a syntax error near..." error + message into the error stack, which is normally produced only if + a parse error is discovered internally by the Bison generated + parser. + */ + void parse_error(const char *err_text, const char *yytext) + { + Lex_input_stream *lip= &m_parser_state->m_lip; + if (!yytext) + { + if (!(yytext= lip->get_tok_start())) + yytext= ""; + } + /* Push an error into the error stack */ + ErrConvString err(yytext, strlen(yytext), variables.character_set_client); + my_printf_error(ER_PARSE_ERROR, ER_THD(this, ER_PARSE_ERROR), MYF(0), + err_text, err.ptr(), lip->yylineno); + } + void parse_error(uint err_number, const char *yytext= 0) + { + parse_error(ER_THD(this, err_number), yytext); + } + void parse_error() + { + parse_error(ER_SYNTAX_ERROR); + } private: /* Only the implementation of the SIGNAL and RESIGNAL statements @@ -3994,8 +4056,42 @@ private: raise_condition(uint sql_errno, const char* sqlstate, Sql_condition::enum_warning_level level, + const char* msg) + { + return raise_condition(sql_errno, sqlstate, level, + Sql_user_condition_identity(), msg); + } + + /** + Raise a generic or a user defined SQL condition. + @param ucid - the user condition identity + (or an empty identity if not a user condition) + @param sql_errno - the condition error number + @param sqlstate - the condition SQLSTATE + @param level - the condition level + @param msg - the condition message text + @return The condition raised, or NULL + */ + Sql_condition* + raise_condition(uint sql_errno, + const char* sqlstate, + Sql_condition::enum_warning_level level, + const Sql_user_condition_identity &ucid, const char* msg); + Sql_condition* + raise_condition(const Sql_condition *cond) + { + Sql_condition *raised= raise_condition(cond->get_sql_errno(), + cond->get_sqlstate(), + cond->get_level(), + *cond/*Sql_user_condition_identity*/, + cond->get_message_text()); + if (raised) + raised->copy_opt_attributes(cond); + return raised; + } + public: /** Overloaded to guard query/query_length fields */ virtual void set_statement(Statement *stmt); @@ -4073,14 +4169,14 @@ public: void binlog_invoker(bool role) { m_binlog_invoker= role ? INVOKER_ROLE : INVOKER_USER; } enum need_invoker need_binlog_invoker() { return m_binlog_invoker; } void get_definer(LEX_USER *definer, bool role); - void set_invoker(const LEX_STRING *user, const LEX_STRING *host) + void set_invoker(const LEX_CSTRING *user, const LEX_CSTRING *host) { - invoker_user= *user; - invoker_host= *host; + invoker.user= *user; + invoker.host= *host; } - LEX_STRING get_invoker_user() { return invoker_user; } - LEX_STRING get_invoker_host() { return invoker_host; } - bool has_invoker() { return invoker_user.length > 0; } + LEX_CSTRING get_invoker_user() { return invoker.user; } + LEX_CSTRING get_invoker_host() { return invoker.host; } + bool has_invoker() { return invoker.user.length > 0; } void print_aborted_warning(uint threshold, const char *reason) { @@ -4179,8 +4275,7 @@ private: TRIGGER or VIEW statements or current user in account management statements if it is not NULL. */ - LEX_STRING invoker_user; - LEX_STRING invoker_host; + AUTHID invoker; public: #ifndef EMBEDDED_LIBRARY @@ -4390,6 +4485,31 @@ public: current_linfo= 0; mysql_mutex_unlock(&LOCK_thread_count); } + + /** + Switch to a sublex, to parse a substatement or an expression. + */ + void set_local_lex(sp_lex_local *sublex) + { + DBUG_ASSERT(lex->sphead); + lex= sublex; + /* Reset part of parser state which needs this. */ + m_parser_state->m_yacc.reset_before_substatement(); + } + + /** + Switch back from a sublex (currently pointed by this->lex) to the old lex. + Sublex is merged to "oldlex" and this->lex is set to "oldlex". + + This method is called after parsing a substatement or an expression. + set_local_lex() must be previously called. + @param oldlex - The old lex which was active before set_local_lex(). + @returns - false on success, true on error (failed to merge LEX's). + + See also sp_head::merge_lex(). + */ + bool restore_from_local_lex_to_old_lex(LEX *oldlex); + }; inline void add_to_active_threads(THD *thd) @@ -4461,13 +4581,13 @@ class sql_exchange :public Sql_alloc { public: enum enum_filetype filetype; /* load XML, Added by Arnold & Erik */ - char *file_name; + const char *file_name; String *field_term,*enclosed,*line_term,*line_start,*escaped; bool opt_enclosed; bool dumpfile; ulong skip_lines; CHARSET_INFO *cs; - sql_exchange(char *name, bool dumpfile_flag, + sql_exchange(const char *name, bool dumpfile_flag, enum_filetype filetype_arg= FILETYPE_CSV); bool escaped_given(void); }; @@ -4966,17 +5086,22 @@ public: }; -class select_union :public select_result_interceptor +class select_unit :public select_result_interceptor { + uint curr_step, prev_step, curr_sel; + enum sub_select_type step; public: + Item_int *intersect_mark; TMP_TABLE_PARAM tmp_table_param; int write_err; /* Error code from the last send_data->ha_write_row call. */ -public: TABLE *table; ha_rows records; - select_union(THD *thd_arg): - select_result_interceptor(thd_arg), write_err(0), table(0), records(0) + select_unit(THD *thd_arg): + select_result_interceptor(thd_arg), + curr_step(0), prev_step(0), curr_sel(UINT_MAX), + step(UNION_TYPE), intersect_mark(0), write_err(0), table(0), + records(0) { tmp_table_param.init(); } int prepare(List<Item> &list, SELECT_LEX_UNIT *u); /** @@ -4998,12 +5123,13 @@ public: const char *alias, bool bit_fields_as_long, bool create_table, - bool keep_row_order= FALSE); + bool keep_row_order, + uint hidden); TMP_TABLE_PARAM *get_tmp_table_param() { return &tmp_table_param; } + void change_select(); }; - -class select_union_recursive :public select_union +class select_union_recursive :public select_unit { public: /* The temporary table with the new records generated by one iterative step */ @@ -5014,7 +5140,7 @@ class select_union_recursive :public select_union List<TABLE> rec_tables; select_union_recursive(THD *thd_arg): - select_union(thd_arg), + select_unit(thd_arg), incr_table(0), first_rec_table_to_update(0) {}; int send_data(List<Item> &items); @@ -5023,7 +5149,8 @@ class select_union_recursive :public select_union const char *alias, bool bit_fields_as_long, bool create_table, - bool keep_row_order= FALSE); + bool keep_row_order, + uint hidden); void cleanup(); }; @@ -5040,7 +5167,7 @@ class select_union_recursive :public select_union have a global ORDER BY clause. @see st_select_lex_unit::prepare(). */ -class select_union_direct :public select_union +class select_union_direct :public select_unit { private: /* Result object that receives all rows */ @@ -5066,7 +5193,7 @@ public: ha_rows send_records; select_union_direct(THD *thd_arg, select_result *result_arg, SELECT_LEX *last_select_lex_arg): - select_union(thd_arg), result(result_arg), + select_unit(thd_arg), result(result_arg), last_select_lex(last_select_lex_arg), done_send_result_set_metadata(false), done_initialize_tables(false), limit_found_rows(0) @@ -5149,7 +5276,7 @@ public: about NULLs. */ -class select_materialize_with_stats : public select_union +class select_materialize_with_stats : public select_unit { protected: class Column_statistics @@ -5182,14 +5309,15 @@ protected: void reset(); public: - select_materialize_with_stats(THD *thd_arg): select_union(thd_arg) + select_materialize_with_stats(THD *thd_arg): select_unit(thd_arg) { tmp_table_param.init(); } bool create_result_table(THD *thd, List<Item> *column_types, bool is_distinct, ulonglong options, const char *alias, bool bit_fields_as_long, bool create_table, - bool keep_row_order= FALSE); + bool keep_row_order, + uint hidden); bool init_result_table(ulonglong select_options); int send_data(List<Item> &items); void cleanup(); @@ -5331,22 +5459,23 @@ typedef struct st_sort_buffer { class Table_ident :public Sql_alloc { public: - LEX_STRING db; - LEX_STRING table; + LEX_CSTRING db; + LEX_CSTRING table; SELECT_LEX_UNIT *sel; - inline Table_ident(THD *thd, LEX_STRING db_arg, LEX_STRING table_arg, + inline Table_ident(THD *thd, const LEX_CSTRING *db_arg, + const LEX_CSTRING *table_arg, bool force) - :table(table_arg), sel((SELECT_LEX_UNIT *)0) + :table(*table_arg), sel((SELECT_LEX_UNIT *)0) { if (!force && (thd->client_capabilities & CLIENT_NO_SCHEMA)) - db.str=0; + db= null_clex_str; else - db= db_arg; + db= *db_arg; } - inline Table_ident(LEX_STRING table_arg) - :table(table_arg), sel((SELECT_LEX_UNIT *)0) + inline Table_ident(const LEX_CSTRING *table_arg) + :table(*table_arg), sel((SELECT_LEX_UNIT *)0) { - db.str=0; + db= null_clex_str; } /* This constructor is used only for the case when we create a derived @@ -5363,19 +5492,45 @@ public: table.length=1; } bool is_derived_table() const { return MY_TEST(sel); } - inline void change_db(char *db_name) + inline void change_db(LEX_CSTRING *db_name) { - db.str= db_name; db.length= (uint) strlen(db_name); + db= *db_name; } + bool resolve_table_rowtype_ref(THD *thd, Row_definition_list &defs); +}; + + +class Qualified_column_ident: public Table_ident +{ +public: + LEX_CSTRING m_column; +public: + Qualified_column_ident(const LEX_CSTRING *column) + :Table_ident(&null_clex_str), + m_column(*column) + { } + Qualified_column_ident(const LEX_CSTRING *table, const LEX_CSTRING *column) + :Table_ident(table), + m_column(*column) + { } + Qualified_column_ident(THD *thd, + const LEX_CSTRING *db, + const LEX_CSTRING *table, + const LEX_CSTRING *column) + :Table_ident(thd, db, table, false), + m_column(*column) + { } + bool resolve_type_ref(THD *thd, Column_definition *def); }; + // this is needed for user_vars hash class user_var_entry { CHARSET_INFO *m_charset; public: user_var_entry() {} /* Remove gcc warning */ - LEX_STRING name; + LEX_CSTRING name; char *value; ulong length; query_id_t update_query_id, used_query_id; @@ -5390,7 +5545,7 @@ class user_var_entry void set_charset(CHARSET_INFO *cs) { m_charset= cs; } }; -user_var_entry *get_variable(HASH *hash, LEX_STRING &name, +user_var_entry *get_variable(HASH *hash, LEX_CSTRING *name, bool create_if_not_exists); class SORT_INFO; @@ -5477,32 +5632,52 @@ public: class my_var : public Sql_alloc { public: - const LEX_STRING name; + const LEX_CSTRING name; enum type { SESSION_VAR, LOCAL_VAR, PARAM_VAR }; type scope; - my_var(const LEX_STRING& j, enum type s) : name(j), scope(s) { } + my_var(const LEX_CSTRING *j, enum type s) : name(*j), scope(s) { } virtual ~my_var() {} virtual bool set(THD *thd, Item *val) = 0; + virtual class my_var_sp *get_my_var_sp() { return NULL; } }; class my_var_sp: public my_var { + const Type_handler *m_type_handler; public: uint offset; - enum_field_types type; /* Routine to which this Item_splocal belongs. Used for checking if correct runtime context is used for variable handling. */ sp_head *sp; - my_var_sp(const LEX_STRING& j, uint o, enum_field_types t, sp_head *s) - : my_var(j, LOCAL_VAR), offset(o), type(t), sp(s) { } + my_var_sp(const LEX_CSTRING *j, uint o, const Type_handler *type_handler, + sp_head *s) + : my_var(j, LOCAL_VAR), m_type_handler(type_handler), offset(o), sp(s) { } ~my_var_sp() { } bool set(THD *thd, Item *val); + my_var_sp *get_my_var_sp() { return this; } + const Type_handler *type_handler() const { return m_type_handler; } +}; + +/* + This class handles fields of a ROW SP variable when it's used as a OUT + parameter in a stored procedure. +*/ +class my_var_sp_row_field: public my_var_sp +{ + uint m_field_offset; +public: + my_var_sp_row_field(const LEX_CSTRING *varname, const LEX_CSTRING *fieldname, + uint var_idx, uint field_idx, sp_head *s) + :my_var_sp(varname, var_idx, &type_handler_double/*Not really used*/, s), + m_field_offset(field_idx) + { } + bool set(THD *thd, Item *val); }; class my_var_user: public my_var { public: - my_var_user(const LEX_STRING& j) + my_var_user(const LEX_CSTRING *j) : my_var(j, SESSION_VAR) { } ~my_var_user() { } bool set(THD *thd, Item *val); @@ -5510,10 +5685,13 @@ public: class select_dumpvar :public select_result_interceptor { ha_rows row_count; + my_var_sp *m_var_sp_row; // Not NULL if SELECT INTO row_type_sp_variable + bool send_data_to_var_list(List<Item> &items); public: List<my_var> var_list; - select_dumpvar(THD *thd_arg): select_result_interceptor(thd_arg) - { var_list.empty(); row_count= 0; } + select_dumpvar(THD *thd_arg) + :select_result_interceptor(thd_arg), row_count(0), m_var_sp_row(NULL) + { var_list.empty(); } ~select_dumpvar() {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); int send_data(List<Item> &items); @@ -5640,6 +5818,14 @@ public: SP Bulk execution optimized */ #define CF_SP_BULK_OPTIMIZED (1U << 20) +/** + If command creates or drops a table +*/ +#define CF_SCHEMA_CHANGE (1U << 21) +/** + If command creates or drops a database +*/ +#define CF_DB_CHANGE (1U << 22) /* Bits in server_command_flags */ @@ -5753,6 +5939,17 @@ inline int handler::ha_write_tmp_row(uchar *buf) return error; } +inline int handler::ha_delete_tmp_row(uchar *buf) +{ + int error; + MYSQL_DELETE_ROW_START(table_share->db.str, table_share->table_name.str); + increment_statistics(&SSV::ha_tmp_delete_count); + TABLE_IO_WAIT(tracker, m_psi, PSI_TABLE_DELETE_ROW, MAX_KEY, 0, + { error= delete_row(buf); }) + MYSQL_DELETE_ROW_DONE(error); + return error; +} + inline int handler::ha_update_tmp_row(const uchar *old_data, uchar *new_data) { int error; @@ -5834,6 +6031,86 @@ class Sql_mode_save sql_mode_t old_mode; // SQL mode saved at construction time. }; + +/** + This class resembles the SQL Standard schema qualified object name: + <schema qualified name> ::= [ <schema name> <period> ] <qualified identifier> +*/ +class Database_qualified_name +{ +public: + LEX_CSTRING m_db; + LEX_CSTRING m_name; + Database_qualified_name(const LEX_CSTRING *db, const LEX_CSTRING *name) + :m_db(*db), m_name(*name) + { } + Database_qualified_name(const char *db, size_t db_length, + const char *name, size_t name_length) + { + m_db.str= db; + m_db.length= db_length; + m_name.str= name; + m_name.length= name_length; + } + + bool eq(const Database_qualified_name *other) const + { + CHARSET_INFO *cs= lower_case_table_names ? + &my_charset_utf8_general_ci : + &my_charset_utf8_bin; + return + m_db.length == other->m_db.length && + m_name.length == other->m_name.length && + !my_strnncoll(cs, + (const uchar *) m_db.str, m_db.length, + (const uchar *) other->m_db.str, other->m_db.length) && + !my_strnncoll(cs, + (const uchar *) m_name.str, m_name.length, + (const uchar *) other->m_name.str, other->m_name.length); + } + void copy(MEM_ROOT *mem_root, const LEX_CSTRING &db, + const LEX_CSTRING &name); + // Export db and name as a qualified name string: 'db.name' + size_t make_qname(char *dst, size_t dstlen) const + { + return my_snprintf(dst, dstlen, "%.*s.%.*s", + (int) m_db.length, m_db.str, + (int) m_name.length, m_name.str); + } + // Export db and name as a qualified name string, allocate on mem_root. + bool make_qname(MEM_ROOT *mem_root, LEX_CSTRING *dst) const + { + const uint dot= !!m_db.length; + char *tmp; + /* format: [database + dot] + name + '\0' */ + dst->length= m_db.length + dot + m_name.length; + if (!(dst->str= tmp= (char*) alloc_root(mem_root, dst->length + 1))) + return true; + sprintf(tmp, "%.*s%.*s%.*s", + (int) m_db.length, (m_db.length ? m_db.str : ""), + dot, ".", + (int) m_name.length, m_name.str); + DBUG_ASSERT(ok_for_lower_case_names(m_db.str)); + return false; + } +}; + + +class ErrConvDQName: public ErrConv +{ + const Database_qualified_name *m_name; +public: + ErrConvDQName(const Database_qualified_name *name) + :m_name(name) + { } + const char *ptr() const + { + m_name->make_qname(err_buffer, sizeof(err_buffer)); + return err_buffer; + } +}; + + #endif /* MYSQL_SERVER */ #endif /* SQL_CLASS_INCLUDED */ diff --git a/sql/sql_cmd.h b/sql/sql_cmd.h index e33f8e443dc..d95b1c828b9 100644 --- a/sql/sql_cmd.h +++ b/sql/sql_cmd.h @@ -96,6 +96,9 @@ enum enum_sql_command { SQLCOM_ALTER_USER, SQLCOM_SHOW_CREATE_USER, SQLCOM_EXECUTE_IMMEDIATE, + SQLCOM_CREATE_SEQUENCE, + SQLCOM_DROP_SEQUENCE, + SQLCOM_ALTER_SEQUENCE, /* When a command is added here, be sure it's also added in mysqld.cc @@ -164,4 +167,33 @@ protected: } }; + +/** + Sql_cmd_call represents the CALL statement. +*/ +class Sql_cmd_call : public Sql_cmd +{ +public: + class sp_name *m_name; + Sql_cmd_call(class sp_name *name) + :m_name(name) + {} + + virtual ~Sql_cmd_call() + {} + + /** + Execute a CALL statement at runtime. + @param thd the current thread. + @return false on success. + */ + bool execute(THD *thd); + + virtual enum_sql_command sql_command_code() const + { + return SQLCOM_CALL; + } +}; + + #endif // SQL_CMD_INCLUDED diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index d778f381a50..0e5d8d43c0c 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -382,7 +382,7 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0) static const char mysql_system_user[]= "#mysql_system#"; // Returns 'user' if it's not NULL. Returns 'mysql_system_user' otherwise. -static const char * get_valid_user_string(char* user) +static const char * get_valid_user_string(const char* user) { return user ? user : mysql_system_user; } @@ -1108,7 +1108,7 @@ bool login_connection(THD *thd) /* Updates global user connection stats. */ if (increment_connection_count(thd, TRUE)) { - my_error(ER_OUTOFMEMORY, MYF(0), 2*sizeof(USER_STATS)); + my_error(ER_OUTOFMEMORY, MYF(0), (int) (2*sizeof(USER_STATS))); error= 1; goto exit; } diff --git a/sql/sql_connect.h b/sql/sql_connect.h index 9cd31814ad7..364be401944 100644 --- a/sql/sql_connect.h +++ b/sql/sql_connect.h @@ -57,7 +57,6 @@ public: class THD; -typedef struct st_lex_user LEX_USER; typedef struct user_conn USER_CONN; void init_max_user_conn(void); diff --git a/sql/sql_const.h b/sql/sql_const.h index 95960ac131a..8d5f56de6b0 100644 --- a/sql/sql_const.h +++ b/sql/sql_const.h @@ -115,6 +115,7 @@ #define MAX_ACCEPT_RETRY 10 // Test accept this many times #define MAX_FIELDS_BEFORE_HASH 32 #define USER_VARS_HASH_SIZE 16 +#define SEQUENCES_HASH_SIZE 16 #define TABLE_OPEN_CACHE_MIN 400 #define TABLE_OPEN_CACHE_DEFAULT 2000 #define TABLE_DEF_CACHE_DEFAULT 400 diff --git a/sql/sql_cte.cc b/sql/sql_cte.cc index 1b14c5b68b7..c163044547f 100644 --- a/sql/sql_cte.cc +++ b/sql/sql_cte.cc @@ -735,8 +735,7 @@ bool With_clause::prepare_unreferenced_elements(THD *thd) bool With_element::set_unparsed_spec(THD *thd, char *spec_start, char *spec_end) { unparsed_spec.length= spec_end - spec_start; - unparsed_spec.str= (char*) thd->memdup(spec_start, unparsed_spec.length+1); - unparsed_spec.str[unparsed_spec.length]= '\0'; + unparsed_spec.str= thd->strmake(spec_start, unparsed_spec.length); if (!unparsed_spec.str) { @@ -806,7 +805,7 @@ st_select_lex_unit *With_element::clone_parsed_spec(THD *thd, TABLE_LIST *spec_tables_tail; st_select_lex *with_select; - if (parser_state.init(thd, unparsed_spec.str, unparsed_spec.length)) + if (parser_state.init(thd, (char*) unparsed_spec.str, unparsed_spec.length)) goto err; lex_start(thd); with_select= &lex->select_lex; @@ -888,9 +887,9 @@ With_element::rename_columns_of_derived_unit(THD *thd, if (column_list.elements) // The column list is optional { List_iterator_fast<Item> it(select->item_list); - List_iterator_fast<LEX_STRING> nm(column_list); + List_iterator_fast<LEX_CSTRING> nm(column_list); Item *item; - LEX_STRING *name; + LEX_CSTRING *name; if (column_list.elements != select->item_list.elements) { diff --git a/sql/sql_cte.h b/sql/sql_cte.h index 64b2f1cb1c8..85676df9d39 100644 --- a/sql/sql_cte.h +++ b/sql/sql_cte.h @@ -4,7 +4,7 @@ #include "sql_lex.h" #include "sql_select.h" -class select_union; +class select_unit; struct st_unit_ctxt_elem; @@ -73,7 +73,7 @@ private: Unparsed specification of the query that specifies this element. It used to build clones of the specification if they are needed. */ - LEX_STRING unparsed_spec; + LEX_CSTRING unparsed_spec; /* Return the map where 1 is set only in the position for this element */ table_map get_elem_map() { return (table_map) 1 << number; } @@ -83,14 +83,14 @@ public: The name of the table introduced by this with elememt. The name can be used in FROM lists of the queries in the scope of the element. */ - LEX_STRING *query_name; + LEX_CSTRING *query_name; /* Optional list of column names to name the columns of the table introduced by this with element. It is used in the case when the names are not inherited from the query that specified the table. Otherwise the list is always empty. */ - List <LEX_STRING> column_list; + List <LEX_CSTRING> column_list; /* The query that specifies the table introduced by this with element */ st_select_lex_unit *spec; /* @@ -132,8 +132,8 @@ public: /* List of Item_subselects containing recursive references to this CTE */ SQL_I_List<Item_subselect> sq_with_rec_ref; - With_element(LEX_STRING *name, - List <LEX_STRING> list, + With_element(LEX_CSTRING *name, + List <LEX_CSTRING> list, st_select_lex_unit *unit) : next(NULL), base_dep_map(0), derived_dep_map(0), sq_dep_map(0), work_dep_map(0), mutually_recursive(0), diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 118c6f23cb0..40eb7046108 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -53,6 +53,10 @@ public: virtual int open(JOIN *join __attribute__((unused))); virtual void fetch(ulong num_rows); virtual void close(); + bool export_structure(THD *thd, Row_definition_list *defs) + { + return table->export_structure(thd, defs); + } virtual ~Materialized_cursor(); void on_table_fill_finished(); @@ -68,13 +72,13 @@ public: create a Materialized_cursor. */ -class Select_materialize: public select_union +class Select_materialize: public select_unit { select_result *result; /**< the result object of the caller (PS or SP) */ public: Materialized_cursor *materialized_cursor; Select_materialize(THD *thd_arg, select_result *result_arg): - select_union(thd_arg), result(result_arg), materialized_cursor(0) {} + select_unit(thd_arg), result(result_arg), materialized_cursor(0) {} virtual bool send_result_set_metadata(List<Item> &list, uint flags); bool send_eof() { @@ -436,7 +440,7 @@ bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags) if (create_result_table(unit->thd, unit->get_column_types(true), FALSE, thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS, - "", FALSE, TRUE, TRUE)) + "", FALSE, TRUE, TRUE, 0)) return TRUE; materialized_cursor= new (&table->mem_root) diff --git a/sql/sql_cursor.h b/sql/sql_cursor.h index bff47d654b3..6fa72a2005d 100644 --- a/sql/sql_cursor.h +++ b/sql/sql_cursor.h @@ -54,6 +54,11 @@ public: virtual int open(JOIN *top_level_join)= 0; virtual void fetch(ulong num_rows)= 0; virtual void close()= 0; + virtual bool export_structure(THD *thd, Row_definition_list *defs) + { + DBUG_ASSERT(0); + return true; + } virtual ~Server_side_cursor(); static void operator delete(void *ptr, size_t size); diff --git a/sql/sql_db.cc b/sql/sql_db.cc index 813a17cdfdb..f91b92b1d4f 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -53,16 +53,16 @@ const char *del_exts[]= {".BAK", ".opt", NullS}; static TYPELIB deletable_extentions= {array_elements(del_exts)-1,"del_exts", del_exts, NULL}; -static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *, char *, +static bool find_db_tables_and_rm_known_files(THD *, MY_DIR *, const char *, const char *, TABLE_LIST **); long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path); static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error); static void mysql_change_db_impl(THD *thd, - LEX_STRING *new_db_name, + LEX_CSTRING *new_db_name, ulong new_db_access, CHARSET_INFO *new_db_charset); -static bool mysql_rm_db_internal(THD *thd, char *db, +static bool mysql_rm_db_internal(THD *thd, const char *db, bool if_exists, bool silent); @@ -122,8 +122,8 @@ uchar* dboptions_get_key(my_dbopt_t *opt, size_t *length, Helper function to write a query to binlog used by mysql_rm_db() */ -static inline int write_to_binlog(THD *thd, char *query, uint q_len, - char *db, uint db_len) +static inline int write_to_binlog(THD *thd, const char *query, uint q_len, + const char *db, uint db_len) { Query_log_event qinfo(thd, query, q_len, FALSE, TRUE, FALSE, 0); qinfo.db= db; @@ -566,7 +566,7 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name) */ static int -mysql_create_db_internal(THD *thd, char *db, +mysql_create_db_internal(THD *thd, const char *db, const DDL_options_st &options, Schema_specification_st *create_info, bool silent) @@ -583,15 +583,8 @@ mysql_create_db_internal(THD *thd, char *db, DBUG_RETURN(-1); } - char db_tmp[SAFE_NAME_LEN], *dbnorm; - if (lower_case_table_names) - { - strmake_buf(db_tmp, db); - my_casedn_str(system_charset_info, db_tmp); - dbnorm= db_tmp; - } - else - dbnorm= db; + char db_tmp[SAFE_NAME_LEN]; + const char *dbnorm= normalize_db_name(db, db_tmp, sizeof(db_tmp)); if (lock_schema_name(thd, dbnorm)) DBUG_RETURN(-1); @@ -771,7 +764,7 @@ exit: } -int mysql_create_db(THD *thd, char *db, +int mysql_create_db(THD *thd, const char *db, const DDL_options_st &options, const Schema_specification_st *create_info) { @@ -812,7 +805,7 @@ bool mysql_alter_db(THD *thd, const char *db, */ static bool -mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent) +mysql_rm_db_internal(THD *thd, const char *db, bool if_exists, bool silent) { ulong deleted_tables= 0; bool error= true, rm_mysql_schema; @@ -824,15 +817,8 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent) Drop_table_error_handler err_handler; DBUG_ENTER("mysql_rm_db"); - char db_tmp[SAFE_NAME_LEN], *dbnorm; - if (lower_case_table_names) - { - strmake_buf(db_tmp, db); - my_casedn_str(system_charset_info, db_tmp); - dbnorm= db_tmp; - } - else - dbnorm= db; + char db_tmp[SAFE_NAME_LEN]; + const char *dbnorm= normalize_db_name(db, db_tmp, sizeof(db_tmp)); if (lock_schema_name(thd, dbnorm)) DBUG_RETURN(true); @@ -898,8 +884,8 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent) { for (table= tables; table; table= table->next_local) { - LEX_STRING db_name= { table->db, table->db_length }; - LEX_STRING table_name= { table->table_name, table->table_name_length }; + LEX_CSTRING db_name= { table->db, table->db_length }; + LEX_CSTRING table_name= { table->table_name, table->table_name_length }; if (table->open_type == OT_BASE_ONLY || !thd->find_temporary_table(table)) (void) delete_statistics_for_table(thd, &db_name, &table_name); @@ -916,7 +902,8 @@ mysql_rm_db_internal(THD *thd,char *db, bool if_exists, bool silent) thd->push_internal_handler(&err_handler); if (!thd->killed && !(tables && - mysql_rm_table_no_locks(thd, tables, true, false, true, true, false))) + mysql_rm_table_no_locks(thd, tables, true, false, true, false, true, + false))) { /* We temporarily disable the binary log while dropping the objects @@ -1010,7 +997,8 @@ update_binlog: if (ha_table_exists(thd, tbl->db, tbl->table_name)) continue; - my_snprintf(quoted_name, sizeof(quoted_name), "%`s", tbl->table_name); + my_snprintf(quoted_name, sizeof(quoted_name), quoted_string, + tbl->table_name); tbl_name_len= strlen(quoted_name) + 1; /* +1 for the comma */ if (query_pos + tbl_name_len + 1 >= query_end) { @@ -1061,25 +1049,25 @@ exit: } -bool mysql_rm_db(THD *thd,char *db, bool if_exists) +bool mysql_rm_db(THD *thd, const char *db, bool if_exists) { return mysql_rm_db_internal(thd, db, if_exists, false); } static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, - char *dbname, + const char *dbname, const char *path, TABLE_LIST **tables) { char filePath[FN_REFLEN]; - LEX_STRING db= { dbname, strlen(dbname) }; + LEX_CSTRING db= { dbname, strlen(dbname) }; TABLE_LIST *tot_list=0, **tot_list_next_local, **tot_list_next_global; DBUG_ENTER("find_db_tables_and_rm_known_files"); DBUG_PRINT("enter",("path: %s", path)); /* first, get the list of tables */ - Dynamic_array<LEX_STRING*> files(dirp->number_of_files); + Dynamic_array<LEX_CSTRING*> files(dirp->number_of_files); Discovered_table_list tl(thd, &files); if (ha_discover_table_names(thd, &db, dirp, &tl, true)) DBUG_RETURN(1); @@ -1089,7 +1077,7 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, for (size_t idx=0; idx < files.elements(); idx++) { - LEX_STRING *table= files.at(idx); + LEX_CSTRING *table= files.at(idx); /* Drop the table nicely */ TABLE_LIST *table_list=(TABLE_LIST*)thd->calloc(sizeof(*table_list)); @@ -1105,7 +1093,7 @@ static bool find_db_tables_and_rm_known_files(THD *thd, MY_DIR *dirp, /* To be able to correctly look up the table in the table cache. */ if (lower_case_table_names) table_list->table_name_length= my_casedn_str(files_charset_info, - table_list->table_name); + (char*) table_list->table_name); table_list->alias= table_list->table_name; // If lower_case_table_names=2 table_list->mdl_request.init(MDL_key::TABLE, table_list->db, @@ -1311,7 +1299,7 @@ err: */ static void mysql_change_db_impl(THD *thd, - LEX_STRING *new_db_name, + LEX_CSTRING *new_db_name, ulong new_db_access, CHARSET_INFO *new_db_charset) { @@ -1343,7 +1331,7 @@ static void mysql_change_db_impl(THD *thd, the previous database name, we should do it explicitly. */ thd->set_db(NULL, 0); - thd->reset_db(new_db_name->str, new_db_name->length); + thd->reset_db(const_cast<char*>(new_db_name->str), new_db_name->length); } /* 2. Update security context. */ @@ -1455,9 +1443,10 @@ static void backup_current_db_name(THD *thd, @retval TRUE Error */ -bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) +bool mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name, + bool force_switch) { - LEX_STRING new_db_file_name; + LEX_CSTRING new_db_file_name; Security_context *sctx= thd->security_ctx; ulong db_access= sctx->db_access; @@ -1520,12 +1509,13 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) It's next to impossible however to get this error when we are called from sp_head::execute(). But let's switch the current database to NULL in this case to be sure. + The cast below ok here as new_db_file_name was just allocated */ - if (check_db_name(&new_db_file_name)) + if (check_db_name((LEX_STRING*) &new_db_file_name)) { my_error(ER_WRONG_DB_NAME, MYF(0), new_db_file_name.str); - my_free(new_db_file_name.str); + my_free(const_cast<char*>(new_db_file_name.str)); if (force_switch) mysql_change_db_impl(thd, NULL, 0, thd->variables.collation_server); @@ -1559,7 +1549,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) new_db_file_name.str); general_log_print(thd, COM_INIT_DB, ER_THD(thd, ER_DBACCESS_DENIED_ERROR), sctx->priv_user, sctx->priv_host, new_db_file_name.str); - my_free(new_db_file_name.str); + my_free(const_cast<char*>(new_db_file_name.str)); DBUG_RETURN(TRUE); } #endif @@ -1576,7 +1566,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) ER_BAD_DB_ERROR, ER_THD(thd, ER_BAD_DB_ERROR), new_db_file_name.str); - my_free(new_db_file_name.str); + my_free(const_cast<char*>(new_db_file_name.str)); /* Change db to NULL. */ @@ -1590,7 +1580,7 @@ bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, bool force_switch) /* Report an error and free new_db_file_name. */ my_error(ER_BAD_DB_ERROR, MYF(0), new_db_file_name.str); - my_free(new_db_file_name.str); + my_free(const_cast<char*>(new_db_file_name.str)); /* The operation failed. */ @@ -1635,7 +1625,7 @@ done: */ bool mysql_opt_change_db(THD *thd, - const LEX_STRING *new_db_name, + const LEX_CSTRING *new_db_name, LEX_STRING *saved_db_name, bool force_switch, bool *cur_db_changed) @@ -1665,7 +1655,8 @@ bool mysql_opt_change_db(THD *thd, @param old_db 5.0 database name, in #mysql50#name format @return 0 on success, 1 on error */ -bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) + +bool mysql_upgrade_db(THD *thd, LEX_CSTRING *old_db) { int error= 0, change_to_newdb= 0; char path[FN_REFLEN+16]; @@ -1674,7 +1665,7 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) MY_DIR *dirp; TABLE_LIST *table_list; SELECT_LEX *sl= thd->lex->current_select; - LEX_STRING new_db; + LEX_CSTRING new_db; DBUG_ENTER("mysql_upgrade_db"); if ((old_db->length <= MYSQL50_TABLE_NAME_PREFIX_LENGTH) || @@ -1730,7 +1721,7 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) { FILEINFO *file= dirp->dir_entry + idx; char *extension, tname[FN_REFLEN + 1]; - LEX_STRING table_str; + LEX_CSTRING table_str; DBUG_PRINT("info",("Examining: %s", file->name)); /* skiping non-FRM files */ @@ -1744,8 +1735,8 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db) table_str.length= filename_to_tablename(file->name, tname, sizeof(tname)-1); table_str.str= (char*) thd->memdup(tname, table_str.length + 1); - Table_ident *old_ident= new Table_ident(thd, *old_db, table_str, 0); - Table_ident *new_ident= new Table_ident(thd, new_db, table_str, 0); + Table_ident *old_ident= new Table_ident(thd, old_db, &table_str, 0); + Table_ident *new_ident= new Table_ident(thd, &new_db, &table_str, 0); if (!old_ident || !new_ident || !sl->add_table_to_list(thd, old_ident, NULL, TL_OPTION_UPDATING, TL_IGNORE, @@ -1892,3 +1883,14 @@ bool check_db_dir_existence(const char *db_name) return my_access(db_dir_path, F_OK); } + + +const char *normalize_db_name(const char *db, char *buffer, size_t buffer_size) +{ + DBUG_ASSERT(buffer_size > 1); + if (!lower_case_table_names) + return db; + strmake(buffer, db, buffer_size - 1); + my_casedn_str(system_charset_info, buffer); + return buffer; +} diff --git a/sql/sql_db.h b/sql/sql_db.h index b0d99cc1ba4..a4ada7de6a2 100644 --- a/sql/sql_db.h +++ b/sql/sql_db.h @@ -20,18 +20,18 @@ class THD; -int mysql_create_db(THD *thd, char *db, +int mysql_create_db(THD *thd, const char *db, const DDL_options_st &options, const Schema_specification_st *create); bool mysql_alter_db(THD *thd, const char *db, const Schema_specification_st *create); -bool mysql_rm_db(THD *thd, char *db, bool if_exists); -bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db); -bool mysql_change_db(THD *thd, const LEX_STRING *new_db_name, +bool mysql_rm_db(THD *thd, const char *db, bool if_exists); +bool mysql_upgrade_db(THD *thd, LEX_CSTRING *old_db); +bool mysql_change_db(THD *thd, const LEX_CSTRING *new_db_name, bool force_switch); bool mysql_opt_change_db(THD *thd, - const LEX_STRING *new_db_name, + const LEX_CSTRING *new_db_name, LEX_STRING *saved_db_name, bool force_switch, bool *cur_db_changed); @@ -45,6 +45,9 @@ CHARSET_INFO *get_default_db_collation(THD *thd, const char *db_name); bool my_dbopt_init(void); void my_dbopt_cleanup(void); +const char *normalize_db_name(const char *db, char *buffer, + size_t buffer_size); + #define MY_DB_OPT_FILE "db.opt" #endif /* SQL_DB_INCLUDED */ diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index a30f9ae2a32..17e2404fdf1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -45,6 +45,8 @@ // end_read_record #include "sql_partition.h" // make_used_partitions_str +#define MEM_STRIP_BUF_SIZE ((size_t) thd->variables.sortbuff_size) + /* @brief Print query plan of a single-table DELETE command @@ -212,6 +214,22 @@ void Update_plan::save_explain_data_intern(MEM_ROOT *mem_root, } +static bool record_should_be_deleted(THD *thd, TABLE *table, SQL_SELECT *sel, + Explain_delete *explain) +{ + explain->tracker.on_record_read(); + thd->inc_examined_row_count(1); + if (table->vfield) + (void) table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_DELETE); + if (!sel || sel->skip_record(thd) > 0) + { + explain->tracker.on_record_after_where(); + return true; + } + return false; +} + + /** Implement DELETE SQL word. @@ -244,9 +262,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool with_select= !select_lex->item_list.is_empty(); Explain_delete *explain; Delete_plan query_plan(thd->mem_root); + Unique * deltempfile= NULL; + bool delete_record, delete_while_scanning; + DBUG_ENTER("mysql_delete"); + query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; - DBUG_ENTER("mysql_delete"); create_explain_query(thd->lex, thd->mem_root); if (open_and_lock_tables(thd, table_list, TRUE, 0)) @@ -275,7 +296,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, query_plan.updating_a_view= MY_TEST(table_list->view); if (mysql_prepare_delete(thd, table_list, select_lex->with_wild, - select_lex->item_list, &conds)) + select_lex->item_list, &conds, + &delete_while_scanning)) DBUG_RETURN(TRUE); if (with_select) @@ -556,16 +578,48 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, explain= (Explain_delete*)thd->lex->explain->get_upd_del_plan(); explain->tracker.on_scan_init(); + if (!delete_while_scanning) + { + /* + The table we are going to delete appears in subqueries in the where + clause. Instead of deleting the rows, first mark them deleted. + */ + ha_rows tmplimit=limit; + deltempfile= new (thd->mem_root) Unique (refpos_order_cmp, table->file, + table->file->ref_length, + MEM_STRIP_BUF_SIZE); + while (!(error=info.read_record(&info)) && !thd->killed && + ! thd->is_error()) + { + if (record_should_be_deleted(thd, table, select, explain)) + { + table->file->position(table->record[0]); + if ((error= deltempfile->unique_add((char*) table->file->ref))) + { + error= 1; + goto terminate_delete; + } + if (!--tmplimit && using_limit) + break; + } + } + end_read_record(&info); + if (deltempfile->get(table) || table->file->ha_index_or_rnd_end() || + init_read_record(&info, thd, table, 0, &deltempfile->sort, 0, 1, false)) + { + error= 1; + goto terminate_delete; + } + delete_record= true; + } + while (!(error=info.read_record(&info)) && !thd->killed && - ! thd->is_error()) + ! thd->is_error()) { - explain->tracker.on_record_read(); - thd->inc_examined_row_count(1); - if (table->vfield) - (void) table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_DELETE); - if (!select || select->skip_record(thd) > 0) + if (delete_while_scanning) + delete_record= record_should_be_deleted(thd, table, select, explain); + if (delete_record) { - explain->tracker.on_record_after_where(); if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_DELETE, TRG_ACTION_BEFORE, FALSE)) @@ -616,6 +670,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, else break; } +terminate_delete: killed_status= thd->killed; if (killed_status != NOT_KILLED || thd->is_error()) error= 1; // Aborted @@ -647,6 +702,8 @@ cleanup: thd->lex->current_select->first_cond_optimization= 0; } + delete deltempfile; + deltempfile=NULL; delete select; select= NULL; transactional_table= table->file->has_transactions(); @@ -746,13 +803,15 @@ l TRUE error */ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, - uint wild_num, List<Item> &field_list, Item **conds) + uint wild_num, List<Item> &field_list, Item **conds, + bool *delete_while_scanning) { Item *fake_conds= 0; SELECT_LEX *select_lex= &thd->lex->select_lex; DBUG_ENTER("mysql_prepare_delete"); List<Item> all_fields; + *delete_while_scanning= true; thd->lex->allow_sum_func= 0; if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, &thd->lex->select_lex.top_join_list, @@ -772,14 +831,9 @@ l my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "DELETE"); DBUG_RETURN(TRUE); } - { - TABLE_LIST *duplicate; - if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0))) - { - update_non_unique_table_error(table_list, "DELETE", duplicate); - DBUG_RETURN(TRUE); - } - } + + if (unique_table(thd, table_list, table_list->next_global, 0)) + *delete_while_scanning= false; if (select_lex->inner_refs_list.elements && fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) @@ -794,7 +848,6 @@ l Delete multiple tables from join ***************************************************************************/ -#define MEM_STRIP_BUF_SIZE (size_t)(current_thd->variables.sortbuff_size) extern "C" int refpos_order_cmp(void* arg, const void *a,const void *b) { @@ -944,7 +997,7 @@ multi_delete::initialize_tables(JOIN *join) DBUG_RETURN(1); table_map tables_to_delete_from=0; - delete_while_scanning= 1; + delete_while_scanning= true; for (walk= delete_tables; walk; walk= walk->next_local) { TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update(); @@ -957,7 +1010,7 @@ multi_delete::initialize_tables(JOIN *join) in join, we need to defer delete. So the delete doesn't interfers with the scaning of results. */ - delete_while_scanning= 0; + delete_while_scanning= false; } } @@ -993,7 +1046,7 @@ multi_delete::initialize_tables(JOIN *join) case send_data() shouldn't delete any rows a we may touch the rows in the deleted table many times */ - delete_while_scanning= 0; + delete_while_scanning= false; } } walk= delete_tables; @@ -1006,10 +1059,9 @@ multi_delete::initialize_tables(JOIN *join) for (;walk ;walk= walk->next_local) { TABLE *table=walk->table; - *tempfiles_ptr++= new Unique (refpos_order_cmp, - (void *) table->file, - table->file->ref_length, - MEM_STRIP_BUF_SIZE); + *tempfiles_ptr++= new (thd->mem_root) Unique (refpos_order_cmp, table->file, + table->file->ref_length, + MEM_STRIP_BUF_SIZE); } init_ftfuncs(thd, thd->lex->current_select, 1); DBUG_RETURN(thd->is_fatal_error != 0); diff --git a/sql/sql_delete.h b/sql/sql_delete.h index 9cd09dc5722..54d6cf146b5 100644 --- a/sql/sql_delete.h +++ b/sql/sql_delete.h @@ -27,7 +27,8 @@ typedef class Item COND; template <typename T> class SQL_I_List; int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, - uint wild_num, List<Item> &field_list, Item **conds); + uint wild_num, List<Item> &field_list, Item **conds, + bool *delete_while_scanning); bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_I_List<ORDER> *order, ha_rows rows, ulonglong options, select_result *result); diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc index 691592c25ea..8dff001d4d0 100644 --- a/sql/sql_derived.cc +++ b/sql/sql_derived.cc @@ -674,7 +674,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) specification has been already prepared (a secondary recursive table reference. */ - if (!(derived->derived_result= new (thd->mem_root) select_union(thd))) + if (!(derived->derived_result= new (thd->mem_root) select_unit(thd))) DBUG_RETURN(TRUE); // out of memory thd->create_tmp_table_for_derived= TRUE; res= derived->derived_result->create_result_table( @@ -682,7 +682,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) (first_select->options | thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS), - derived->alias, FALSE, FALSE); + derived->alias, FALSE, FALSE, FALSE, 0); thd->create_tmp_table_for_derived= FALSE; if (!res && !derived->table) @@ -737,7 +737,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) unit->derived= derived; derived->fill_me= FALSE; - if (!(derived->derived_result= new (thd->mem_root) select_union(thd))) + if (!(derived->derived_result= new (thd->mem_root) select_unit(thd))) DBUG_RETURN(TRUE); // out of memory lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; @@ -765,7 +765,7 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) As 'distinct' parameter we always pass FALSE (0), because underlying query will control distinct condition by itself. Correct test of - distinct underlying query will be is_union && + distinct underlying query will be is_unit_op && !unit->union_distinct->next_select() (i.e. it is union and last distinct SELECT is last SELECT of UNION). */ @@ -776,7 +776,8 @@ bool mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *derived) thd->variables.option_bits | TMP_TABLE_ALL_COLUMNS), derived->alias, - FALSE, FALSE, FALSE)) + FALSE, FALSE, FALSE, + 0)) { thd->create_tmp_table_for_derived= FALSE; goto exit; @@ -874,12 +875,12 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived) bool res= FALSE; DBUG_ENTER("mysql_derived_optimize"); - if (unit->optimized) - DBUG_RETURN(FALSE); lex->current_select= first_select; - if (unit->is_union()) + if (unit->is_unit_op()) { + if (unit->optimized) + DBUG_RETURN(FALSE); // optimize union without execution res= unit->optimize(); } @@ -889,7 +890,20 @@ bool mysql_derived_optimize(THD *thd, LEX *lex, TABLE_LIST *derived) { JOIN *join= first_select->join; unit->set_limit(unit->global_parameters()); - unit->optimized= TRUE; + if (join && + join->optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE && + join->with_two_phase_optimization) + { + if (unit->optimized_2) + DBUG_RETURN(FALSE); + unit->optimized_2= TRUE; + } + else + { + if (unit->optimized) + DBUG_RETURN(FALSE); + unit->optimized= TRUE; + } if ((res= join->optimize())) goto err; if (join->table_count == join->const_tables) @@ -943,7 +957,7 @@ bool mysql_derived_create(THD *thd, LEX *lex, TABLE_LIST *derived) if (table->is_created()) DBUG_RETURN(FALSE); - select_union *result= derived->derived_result; + select_unit *result= derived->derived_result; if (table->s->db_type() == TMP_ENGINE_HTON) { result->tmp_table_param.keyinfo= table->s->key_info; @@ -1041,8 +1055,24 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) DBUG_RETURN(FALSE); /*check that table creation passed without problems. */ DBUG_ASSERT(derived->table && derived->table->is_created()); - select_union *derived_result= derived->derived_result; + select_unit *derived_result= derived->derived_result; SELECT_LEX *save_current_select= lex->current_select; + + if (unit->executed && !derived_is_recursive && + (unit->uncacheable & UNCACHEABLE_DEPENDENT)) + { + if ((res= derived->table->file->ha_delete_all_rows())) + goto err; + JOIN *join= unit->first_select()->join; + join->first_record= false; + for (uint i= join->top_join_tab_count; + i < join->top_join_tab_count + join->aggr_tables; + i++) + { + if ((res= join->join_tab[i].table->file->ha_delete_all_rows())) + goto err; + } + } if (derived_is_recursive) { @@ -1057,7 +1087,7 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) res= derived->fill_recursive(thd); } } - else if (unit->is_union()) + else if (unit->is_unit_op()) { // execute union without clean up res= unit->exec(); @@ -1090,7 +1120,8 @@ bool mysql_derived_fill(THD *thd, LEX *lex, TABLE_LIST *derived) res= TRUE; unit->executed= TRUE; } - if (res || (!lex->describe && !derived_is_recursive)) +err: + if (res || (!lex->describe && !derived_is_recursive && !unit->uncacheable)) unit->cleanup(); lex->current_select= save_current_select; @@ -1214,15 +1245,51 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) st_select_lex *save_curr_select= thd->lex->current_select; for (; sl; sl= sl->next_select()) { + Item *extracted_cond_copy; if (!sl->cond_pushdown_is_allowed()) continue; thd->lex->current_select= sl; + if (sl->have_window_funcs()) + { + if (sl->join->group_list || sl->join->implicit_grouping) + continue; + ORDER *common_partition_fields= + sl->find_common_window_func_partition_fields(thd); + if (!common_partition_fields) + continue; + extracted_cond_copy= !sl->next_select() ? + extracted_cond : + extracted_cond->build_clone(thd, thd->mem_root); + if (!extracted_cond_copy) + continue; + + Item *cond_over_partition_fields;; + sl->collect_grouping_fields(thd, common_partition_fields); + sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy, + derived); + cond_over_partition_fields= + sl->build_cond_for_grouping_fields(thd, extracted_cond_copy, true); + if (cond_over_partition_fields) + cond_over_partition_fields= cond_over_partition_fields->transform(thd, + &Item::derived_grouping_field_transformer_for_where, + (uchar*) sl); + if (cond_over_partition_fields) + { + cond_over_partition_fields->walk( + &Item::cleanup_excluding_const_fields_processor, 0, 0); + sl->cond_pushed_into_where= cond_over_partition_fields; + } + + continue; + } + /* For each select of the unit except the last one create a clone of extracted_cond */ - Item *extracted_cond_copy= !sl->next_select() ? extracted_cond : - extracted_cond->build_clone(thd, thd->mem_root); + extracted_cond_copy= !sl->next_select() ? + extracted_cond : + extracted_cond->build_clone(thd, thd->mem_root); if (!extracted_cond_copy) continue; @@ -1247,7 +1314,7 @@ bool pushdown_cond_for_derived(THD *thd, Item *cond, TABLE_LIST *derived) that could be pushed into the where clause of sl */ Item *cond_over_grouping_fields; - sl->collect_grouping_fields(thd); + sl->collect_grouping_fields(thd, sl->join->group_list); sl->check_cond_extraction_for_grouping_fields(extracted_cond_copy, derived); cond_over_grouping_fields= diff --git a/sql/sql_digest.cc b/sql/sql_digest.cc index 6605d0af0a9..27c33f1c64b 100644 --- a/sql/sql_digest.cc +++ b/sql/sql_digest.cc @@ -572,7 +572,7 @@ sql_digest_state* digest_add_token(sql_digest_state *state, case IDENT_QUOTED: { YYSTYPE *lex_token= yylval; - char *yytext= lex_token->lex_str.str; + const char *yytext= lex_token->lex_str.str; size_t yylen= lex_token->lex_str.length; /* diff --git a/sql/sql_error.cc b/sql/sql_error.cc index d14c7b83b77..144cf9d4a30 100644 --- a/sql/sql_error.cc +++ b/sql/sql_error.cc @@ -172,70 +172,6 @@ This file contains the implementation of error and warnings related See WL#2111 (Stored Procedures: Implement GET DIAGNOSTICS) */ -Sql_condition::Sql_condition() - : Sql_alloc(), - m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin), - m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin), - m_constraint_catalog((const char*) NULL, 0, & my_charset_utf8_bin), - m_constraint_schema((const char*) NULL, 0, & my_charset_utf8_bin), - m_constraint_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_catalog_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_schema_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_table_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_column_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_message_text(), - m_sql_errno(0), - m_level(Sql_condition::WARN_LEVEL_ERROR), - m_mem_root(NULL) -{ - memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate)); -} - -void Sql_condition::init(MEM_ROOT *mem_root) -{ - DBUG_ASSERT(mem_root != NULL); - DBUG_ASSERT(m_mem_root == NULL); - m_mem_root= mem_root; -} - -void Sql_condition::clear() -{ - m_class_origin.length(0); - m_subclass_origin.length(0); - m_constraint_catalog.length(0); - m_constraint_schema.length(0); - m_constraint_name.length(0); - m_catalog_name.length(0); - m_schema_name.length(0); - m_table_name.length(0); - m_column_name.length(0); - m_cursor_name.length(0); - m_message_text.length(0); - m_sql_errno= 0; - m_level= Sql_condition::WARN_LEVEL_ERROR; -} - -Sql_condition::Sql_condition(MEM_ROOT *mem_root) - : Sql_alloc(), - m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin), - m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin), - m_constraint_catalog((const char*) NULL, 0, & my_charset_utf8_bin), - m_constraint_schema((const char*) NULL, 0, & my_charset_utf8_bin), - m_constraint_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_catalog_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_schema_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_table_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_column_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin), - m_message_text(), - m_sql_errno(0), - m_level(Sql_condition::WARN_LEVEL_ERROR), - m_mem_root(mem_root) -{ - DBUG_ASSERT(mem_root != NULL); - memset(m_returned_sqlstate, 0, sizeof(m_returned_sqlstate)); -} static void copy_string(MEM_ROOT *mem_root, String* dst, const String* src) { @@ -270,21 +206,6 @@ Sql_condition::copy_opt_attributes(const Sql_condition *cond) copy_string(m_mem_root, & m_cursor_name, & cond->m_cursor_name); } -void -Sql_condition::set(uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, const char* msg) -{ - DBUG_ASSERT(sql_errno != 0); - DBUG_ASSERT(sqlstate != NULL); - DBUG_ASSERT(msg != NULL); - - m_sql_errno= sql_errno; - memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH); - m_returned_sqlstate[SQLSTATE_LENGTH]= '\0'; - - set_builtin_message_text(msg); - m_level= level; -} void Sql_condition::set_builtin_message_text(const char* str) @@ -312,13 +233,46 @@ Sql_condition::get_message_octet_length() const return m_message_text.length(); } -void -Sql_condition::set_sqlstate(const char* sqlstate) + +void Sql_state_errno_level::assign_defaults(const Sql_state_errno *from) { - memcpy(m_returned_sqlstate, sqlstate, SQLSTATE_LENGTH); - m_returned_sqlstate[SQLSTATE_LENGTH]= '\0'; + DBUG_ASSERT(from); + int sqlerrno= from->get_sql_errno(); + /* + SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions. + */ + DBUG_ASSERT(from->has_sql_state()); + set_sqlstate(from); + /* SQLSTATE class "00": illegal, rejected in the parser. */ + DBUG_ASSERT(m_sqlstate[0] != '0' || get_sqlstate()[1] != '0'); + + if (Sql_state::is_warning()) /* SQLSTATE class "01": warning. */ + { + m_level= Sql_condition::WARN_LEVEL_WARN; + m_sql_errno= sqlerrno ? sqlerrno : ER_SIGNAL_WARN; + } + else if (Sql_state::is_not_found()) /* SQLSTATE class "02": not found. */ + { + m_level= Sql_condition::WARN_LEVEL_ERROR; + m_sql_errno= sqlerrno ? sqlerrno : ER_SIGNAL_NOT_FOUND; + } + else /* other SQLSTATE classes : error. */ + { + m_level= Sql_condition::WARN_LEVEL_ERROR; + m_sql_errno= sqlerrno ? sqlerrno : ER_SIGNAL_EXCEPTION; + } } + +void Sql_condition::assign_defaults(THD *thd, const Sql_state_errno *from) +{ + if (from) + Sql_state_errno_level::assign_defaults(from); + if (!get_message_text()) + set_builtin_message_text(ER(get_sql_errno())); +} + + Diagnostics_area::Diagnostics_area(bool initialize) : is_bulk_execution(0), m_main_wi(0, false, initialize) { @@ -353,7 +307,8 @@ Diagnostics_area::reset_diagnostics_area() m_can_overwrite_status= FALSE; /** Don't take chances in production */ m_message[0]= '\0'; - m_sql_errno= 0; + Sql_state_errno::clear(); + Sql_user_condition_identity::clear(); m_affected_rows= 0; m_last_insert_id= 0; m_statement_warn_count= 0; @@ -452,6 +407,7 @@ Diagnostics_area::set_error_status(uint sql_errno) set_error_status(sql_errno, ER(sql_errno), mysql_errno_to_sqlstate(sql_errno), + Sql_user_condition_identity(), NULL); } @@ -465,6 +421,7 @@ Diagnostics_area::set_error_status(uint sql_errno) @param sql_errno SQL-condition error number @param message SQL-condition message @param sqlstate SQL-condition state + @param ucid User defined condition identity @param error_condition SQL-condition object representing the error state @note Note, that error_condition may be NULL. It happens if a) OOM error is @@ -475,6 +432,7 @@ void Diagnostics_area::set_error_status(uint sql_errno, const char *message, const char *sqlstate, + const Sql_user_condition_identity &ucid, const Sql_condition *error_condition) { DBUG_ENTER("set_error_status"); @@ -501,9 +459,8 @@ Diagnostics_area::set_error_status(uint sql_errno, return; #endif - m_sql_errno= sql_errno; - memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH); - m_sqlstate[SQLSTATE_LENGTH]= '\0'; + Sql_state_errno::set(sql_errno, sqlstate); + Sql_user_condition_identity::set(ucid); strmake_buf(m_message, message); get_warning_info()->set_error_condition(error_condition); @@ -695,8 +652,7 @@ void Warning_info::reserve_space(THD *thd, uint count) } Sql_condition *Warning_info::push_warning(THD *thd, - uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, + const Sql_condition_identity *value, const char *msg) { Sql_condition *cond= NULL; @@ -706,14 +662,11 @@ Sql_condition *Warning_info::push_warning(THD *thd, if (m_allow_unlimited_warnings || m_warn_list.elements() < thd->variables.max_error_count) { - cond= new (& m_warn_root) Sql_condition(& m_warn_root); + cond= new (& m_warn_root) Sql_condition(& m_warn_root, *value, msg); if (cond) - { - cond->set(sql_errno, sqlstate, level, msg); m_warn_list.push_back(cond); - } } - m_warn_count[(uint) level]++; + m_warn_count[(uint) value->get_level()]++; } m_current_statement_warn_count++; @@ -721,13 +674,11 @@ Sql_condition *Warning_info::push_warning(THD *thd, } -Sql_condition *Warning_info::push_warning(THD *thd, const Sql_condition *sql_condition) +Sql_condition *Warning_info::push_warning(THD *thd, + const Sql_condition *sql_condition) { - Sql_condition *new_condition= push_warning(thd, - sql_condition->get_sql_errno(), - sql_condition->get_sqlstate(), - sql_condition->get_level(), - sql_condition->get_message_text()); + Sql_condition *new_condition= push_warning(thd, sql_condition, + sql_condition->get_message_text()); if (new_condition) new_condition->copy_opt_attributes(sql_condition); @@ -1043,7 +994,7 @@ uint32 convert_error_message(char *to, uint32 to_length, CHARSET_INFO *to_cs, @retval false if it's bad. */ -bool is_sqlstate_valid(const LEX_STRING *sqlstate) +bool is_sqlstate_valid(const LEX_CSTRING *sqlstate) { if (sqlstate->length != 5) return false; diff --git a/sql/sql_error.h b/sql/sql_error.h index 0a1e7d38e01..263c5843a4a 100644 --- a/sql/sql_error.h +++ b/sql/sql_error.h @@ -27,15 +27,123 @@ class THD; class my_decimal; +class sp_condition_value; /////////////////////////////////////////////////////////////////////////// -/** - Representation of a SQL condition. - A SQL condition can be a completion condition (note, warning), - or an exception condition (error, not found). -*/ -class Sql_condition : public Sql_alloc +class Sql_state +{ +protected: + /** + This member is always NUL terminated. + */ + char m_sqlstate[SQLSTATE_LENGTH + 1]; +public: + Sql_state() + { + memset(m_sqlstate, 0, sizeof(m_sqlstate)); + } + + Sql_state(const char *sqlstate) + { + set_sqlstate(sqlstate); + } + + const char* get_sqlstate() const + { return m_sqlstate; } + + void set_sqlstate(const Sql_state *other) + { + *this= *other; + } + void set_sqlstate(const char *sqlstate) + { + memcpy(m_sqlstate, sqlstate, SQLSTATE_LENGTH); + m_sqlstate[SQLSTATE_LENGTH]= '\0'; + } + bool eq(const Sql_state *other) const + { + return strcmp(m_sqlstate, other->m_sqlstate) == 0; + } + + bool has_sql_state() const { return m_sqlstate[0] != '\0'; } + + /** + Checks if this SQL state defines a WARNING condition. + Note: m_sqlstate must contain a valid SQL-state. + + @retval true if this SQL state defines a WARNING condition. + @retval false otherwise. + */ + inline bool is_warning() const + { return m_sqlstate[0] == '0' && m_sqlstate[1] == '1'; } + + + /** + Checks if this SQL state defines a NOT FOUND condition. + Note: m_sqlstate must contain a valid SQL-state. + + @retval true if this SQL state defines a NOT FOUND condition. + @retval false otherwise. + */ + inline bool is_not_found() const + { return m_sqlstate[0] == '0' && m_sqlstate[1] == '2'; } + + + /** + Checks if this SQL state defines an EXCEPTION condition. + Note: m_sqlstate must contain a valid SQL-state. + + @retval true if this SQL state defines an EXCEPTION condition. + @retval false otherwise. + */ + inline bool is_exception() const + { return m_sqlstate[0] != '0' || m_sqlstate[1] > '2'; } + +}; + + +class Sql_state_errno: public Sql_state +{ +protected: + /** + MySQL extension, MYSQL_ERRNO condition item. + SQL error number. One of ER_ codes from share/errmsg.txt. + Set by set_error_status. + */ + uint m_sql_errno; + +public: + Sql_state_errno() + :m_sql_errno(0) + { } + Sql_state_errno(uint sql_errno) + :m_sql_errno(sql_errno) + { } + Sql_state_errno(uint sql_errno, const char *sql_state) + :Sql_state(sql_state), + m_sql_errno(sql_errno) + { } + /** + Get the SQL_ERRNO of this condition. + @return the sql error number condition item. + */ + uint get_sql_errno() const + { return m_sql_errno; } + + void set(uint sql_errno, const char *sqlstate) + { + m_sql_errno= sql_errno; + set_sqlstate(sqlstate); + } + void clear() + { + m_sql_errno= 0; + } +}; + + +class Sql_state_errno_level: public Sql_state_errno { public: /* @@ -47,6 +155,197 @@ public: enum enum_warning_level { WARN_LEVEL_NOTE, WARN_LEVEL_WARN, WARN_LEVEL_ERROR, WARN_LEVEL_END}; +protected: + /** Severity (error, warning, note) of this condition. */ + enum_warning_level m_level; + + void assign_defaults(const Sql_state_errno *value); + +public: + /** + Get the error level of this condition. + @return the error level condition item. + */ + enum_warning_level get_level() const + { return m_level; } + + Sql_state_errno_level() + :m_level(WARN_LEVEL_ERROR) + { } + + Sql_state_errno_level(uint sqlerrno, const char* sqlstate, + enum_warning_level level) + :Sql_state_errno(sqlerrno, sqlstate), + m_level(level) + { } + Sql_state_errno_level(const Sql_state_errno &state_errno, + enum_warning_level level) + :Sql_state_errno(state_errno), + m_level(level) + { } + void clear() + { + m_level= WARN_LEVEL_ERROR; + Sql_state_errno::clear(); + } +}; + + +/* + class Sql_user_condition_identity. + Instances of this class uniquely idetify user defined conditions (EXCEPTION). + + SET sql_mode=ORACLE; + CREATE PROCEDURE p1 + AS + a EXCEPTION; + BEGIN + RAISE a; + EXCEPTION + WHEN a THEN NULL; + END; + + Currently a user defined condition is identified by a pointer to + its parse time sp_condition_value instance. This can change when + we add packages. See MDEV-10591. +*/ +class Sql_user_condition_identity +{ +protected: + const sp_condition_value *m_user_condition_value; +public: + Sql_user_condition_identity() + :m_user_condition_value(NULL) + { } + Sql_user_condition_identity(const sp_condition_value *value) + :m_user_condition_value(value) + { } + const sp_condition_value *get_user_condition_value() const + { return m_user_condition_value; } + + void set(const Sql_user_condition_identity &identity) + { + *this= identity; + } + void clear() + { + m_user_condition_value= NULL; + } +}; + + +/** + class Sql_condition_identity. + Instances of this class uniquely identify conditions + (including user-defined exceptions for sql_mode=ORACLE) + and store everything that is needed for handler search + purposes in sp_pcontext::find_handler(). +*/ +class Sql_condition_identity: public Sql_state_errno_level, + public Sql_user_condition_identity +{ +public: + Sql_condition_identity() + { } + Sql_condition_identity(const Sql_state_errno_level &st, + const Sql_user_condition_identity &ucid) + :Sql_state_errno_level(st), + Sql_user_condition_identity(ucid) + { } + Sql_condition_identity(const Sql_state_errno &st, + enum_warning_level level, + const Sql_user_condition_identity &ucid) + :Sql_state_errno_level(st, level), + Sql_user_condition_identity(ucid) + { } + Sql_condition_identity(uint sqlerrno, + const char* sqlstate, + enum_warning_level level, + const Sql_user_condition_identity &ucid) + :Sql_state_errno_level(sqlerrno, sqlstate, level), + Sql_user_condition_identity(ucid) + { } + void clear() + { + Sql_state_errno_level::clear(); + Sql_user_condition_identity::clear(); + } +}; + + +class Sql_condition_items +{ +protected: + /** SQL CLASS_ORIGIN condition item. */ + String m_class_origin; + + /** SQL SUBCLASS_ORIGIN condition item. */ + String m_subclass_origin; + + /** SQL CONSTRAINT_CATALOG condition item. */ + String m_constraint_catalog; + + /** SQL CONSTRAINT_SCHEMA condition item. */ + String m_constraint_schema; + + /** SQL CONSTRAINT_NAME condition item. */ + String m_constraint_name; + + /** SQL CATALOG_NAME condition item. */ + String m_catalog_name; + + /** SQL SCHEMA_NAME condition item. */ + String m_schema_name; + + /** SQL TABLE_NAME condition item. */ + String m_table_name; + + /** SQL COLUMN_NAME condition item. */ + String m_column_name; + + /** SQL CURSOR_NAME condition item. */ + String m_cursor_name; + + Sql_condition_items() + :m_class_origin((const char*) NULL, 0, & my_charset_utf8_bin), + m_subclass_origin((const char*) NULL, 0, & my_charset_utf8_bin), + m_constraint_catalog((const char*) NULL, 0, & my_charset_utf8_bin), + m_constraint_schema((const char*) NULL, 0, & my_charset_utf8_bin), + m_constraint_name((const char*) NULL, 0, & my_charset_utf8_bin), + m_catalog_name((const char*) NULL, 0, & my_charset_utf8_bin), + m_schema_name((const char*) NULL, 0, & my_charset_utf8_bin), + m_table_name((const char*) NULL, 0, & my_charset_utf8_bin), + m_column_name((const char*) NULL, 0, & my_charset_utf8_bin), + m_cursor_name((const char*) NULL, 0, & my_charset_utf8_bin) + { } + + void clear() + { + m_class_origin.length(0); + m_subclass_origin.length(0); + m_constraint_catalog.length(0); + m_constraint_schema.length(0); + m_constraint_name.length(0); + m_catalog_name.length(0); + m_schema_name.length(0); + m_table_name.length(0); + m_column_name.length(0); + m_cursor_name.length(0); + } +}; + + +/** + Representation of a SQL condition. + A SQL condition can be a completion condition (note, warning), + or an exception condition (error, not found). +*/ +class Sql_condition : public Sql_alloc, + public Sql_condition_identity, + public Sql_condition_items +{ +public: + /** Convert a bitmask consisting of MYSQL_TIME_{NOTE|WARN}_XXX bits to WARN_LEVEL_XXX @@ -69,27 +368,6 @@ public: */ int get_message_octet_length() const; - /** - Get the SQLSTATE of this condition. - @return the sql state. - */ - const char* get_sqlstate() const - { return m_returned_sqlstate; } - - /** - Get the SQL_ERRNO of this condition. - @return the sql error number condition item. - */ - uint get_sql_errno() const - { return m_sql_errno; } - - /** - Get the error level of this condition. - @return the error level condition item. - */ - Sql_condition::enum_warning_level get_level() const - { return m_level; } - private: /* The interface of Sql_condition is mostly private, by design, @@ -116,21 +394,57 @@ private: This constructor is usefull when allocating arrays. Note that the init() method should be called to complete the Sql_condition. */ - Sql_condition(); + Sql_condition() + :m_mem_root(NULL) + { } /** Complete the Sql_condition initialisation. @param mem_root The memory root to use for the condition items of this condition */ - void init(MEM_ROOT *mem_root); + void init(MEM_ROOT *mem_root) + { + DBUG_ASSERT(mem_root != NULL); + DBUG_ASSERT(m_mem_root == NULL); + m_mem_root= mem_root; + } /** Constructor. @param mem_root The memory root to use for the condition items of this condition */ - Sql_condition(MEM_ROOT *mem_root); + Sql_condition(MEM_ROOT *mem_root) + :m_mem_root(mem_root) + { + DBUG_ASSERT(mem_root != NULL); + } + + Sql_condition(MEM_ROOT *mem_root, const Sql_user_condition_identity &ucid) + :Sql_condition_identity(Sql_state_errno_level(), ucid), + m_mem_root(mem_root) + { + DBUG_ASSERT(mem_root != NULL); + } + /** + Constructor for a fixed message text. + @param mem_root - memory root + @param value - the error number and the sql state for this condition + @param level - the error level for this condition + @param msg - the message text for this condition + */ + Sql_condition(MEM_ROOT *mem_root, + const Sql_condition_identity &value, + const char *msg) + :Sql_condition_identity(value), + m_mem_root(mem_root) + { + DBUG_ASSERT(mem_root != NULL); + DBUG_ASSERT(value.get_sql_errno() != 0); + DBUG_ASSERT(msg != NULL); + set_builtin_message_text(msg); + } /** Destructor. */ ~Sql_condition() @@ -143,27 +457,12 @@ private: void copy_opt_attributes(const Sql_condition *cond); /** - Set this condition area with a fixed message text. - @param thd the current thread. - @param code the error number for this condition. - @param str the message text for this condition. - @param level the error level for this condition. - @param MyFlags additional flags. - */ - void set(uint sql_errno, const char* sqlstate, - Sql_condition::enum_warning_level level, - const char* msg); - - /** Set the condition message test. @param str Message text, expressed in the character set derived from the server --language option */ void set_builtin_message_text(const char* str); - /** Set the SQLSTATE of this condition. */ - void set_sqlstate(const char* sqlstate); - /** Set the CLASS_ORIGIN of this condition. */ void set_class_origin(); @@ -171,56 +470,27 @@ private: void set_subclass_origin(); /** + Assign the condition items 'MYSQL_ERRNO', 'level' and 'MESSAGE_TEXT' + default values of a condition. + @param thd - current thread, to access to localized error messages + @param from - copy condition items from here (can be NULL) + */ + void assign_defaults(THD *thd, const Sql_state_errno *from); + + /** Clear this SQL condition. */ - void clear(); + void clear() + { + Sql_condition_identity::clear(); + Sql_condition_items::clear(); + m_message_text.length(0); + } private: - /** SQL CLASS_ORIGIN condition item. */ - String m_class_origin; - - /** SQL SUBCLASS_ORIGIN condition item. */ - String m_subclass_origin; - - /** SQL CONSTRAINT_CATALOG condition item. */ - String m_constraint_catalog; - - /** SQL CONSTRAINT_SCHEMA condition item. */ - String m_constraint_schema; - - /** SQL CONSTRAINT_NAME condition item. */ - String m_constraint_name; - - /** SQL CATALOG_NAME condition item. */ - String m_catalog_name; - - /** SQL SCHEMA_NAME condition item. */ - String m_schema_name; - - /** SQL TABLE_NAME condition item. */ - String m_table_name; - - /** SQL COLUMN_NAME condition item. */ - String m_column_name; - - /** SQL CURSOR_NAME condition item. */ - String m_cursor_name; - /** Message text, expressed in the character set implied by --language. */ String m_message_text; - /** MySQL extension, MYSQL_ERRNO condition item. */ - uint m_sql_errno; - - /** - SQL RETURNED_SQLSTATE condition item. - This member is always NUL terminated. - */ - char m_returned_sqlstate[SQLSTATE_LENGTH+1]; - - /** Severity (error, warning, note) of this condition. */ - Sql_condition::enum_warning_level m_level; - /** Pointers for participating in the list of conditions. */ Sql_condition *next_in_wi; Sql_condition **prev_in_wi; @@ -467,17 +737,13 @@ private: counters. @param thd Thread context. - @param sql_errno SQL-condition error number. - @param sqlstate SQL-condition state. - @param level SQL-condition level. + @param identity SQL-condition identity @param msg SQL-condition message. @return a pointer to the added SQL-condition. */ Sql_condition *push_warning(THD *thd, - uint sql_errno, - const char* sqlstate, - Sql_condition::enum_warning_level level, + const Sql_condition_identity *identity, const char* msg); /** @@ -637,7 +903,8 @@ public: Can not be assigned twice per statement. */ -class Diagnostics_area +class Diagnostics_area: public Sql_state_errno, + public Sql_user_condition_identity { private: /** The type of the counted and doubly linked list of conditions. */ @@ -689,8 +956,19 @@ public: void set_error_status(uint sql_errno, const char *message, const char *sqlstate, + const Sql_user_condition_identity &ucid, const Sql_condition *error_condition); + void set_error_status(uint sql_errno, + const char *message, + const char *sqlstate, + const Sql_condition *error_condition) + { + set_error_status(sql_errno, message, sqlstate, + Sql_user_condition_identity(), + error_condition); + } + void disable_status(); void reset_diagnostics_area(); @@ -725,10 +1003,13 @@ public: { m_skip_flush= TRUE; } uint sql_errno() const - { DBUG_ASSERT(m_status == DA_ERROR); return m_sql_errno; } + { + DBUG_ASSERT(m_status == DA_ERROR); + return Sql_state_errno::get_sql_errno(); + } const char* get_sqlstate() const - { DBUG_ASSERT(m_status == DA_ERROR); return m_sqlstate; } + { DBUG_ASSERT(m_status == DA_ERROR); return Sql_state::get_sqlstate(); } ulonglong affected_rows() const { @@ -749,6 +1030,18 @@ public: return m_statement_warn_count; } + /** + Get the current errno, state and id of the user defined condition + and return them as Sql_condition_identity. + */ + Sql_condition_identity get_error_condition_identity() const + { + DBUG_ASSERT(m_status == DA_ERROR); + return Sql_condition_identity(*this /*Sql_state_errno*/, + Sql_condition::WARN_LEVEL_ERROR, + *this /*Sql_user_condition_identity*/); + } + /* Used to count any warnings pushed after calling set_ok_status(). */ void increment_warning() { @@ -845,13 +1138,22 @@ public: uint sql_errno_arg, const char* sqlstate, Sql_condition::enum_warning_level level, + const Sql_user_condition_identity &ucid, const char* msg) { - return get_warning_info()->push_warning(thd, - sql_errno_arg, sqlstate, level, - msg); + Sql_condition_identity tmp(sql_errno_arg, sqlstate, level, ucid); + return get_warning_info()->push_warning(thd, &tmp, msg); } + Sql_condition *push_warning(THD *thd, + uint sqlerrno, + const char* sqlstate, + Sql_condition::enum_warning_level level, + const char* msg) + { + return push_warning(thd, sqlerrno, sqlstate, level, + Sql_user_condition_identity(), msg); + } void mark_sql_conditions_for_removal() { get_warning_info()->mark_sql_conditions_for_removal(); } @@ -891,14 +1193,6 @@ private: char m_message[MYSQL_ERRMSG_SIZE]; /** - SQL error number. One of ER_ codes from share/errmsg.txt. - Set by set_error_status. - */ - uint m_sql_errno; - - char m_sqlstate[SQLSTATE_LENGTH+1]; - - /** The number of rows affected by the last statement. This is semantically close to thd->m_row_count_func, but has a different life cycle. thd->m_row_count_func stores the value returned by @@ -953,7 +1247,7 @@ uint32 convert_error_message(char *to, uint32 to_length, extern const LEX_STRING warning_level_names[]; -bool is_sqlstate_valid(const LEX_STRING *sqlstate); +bool is_sqlstate_valid(const LEX_CSTRING *sqlstate); /** Checks if the specified SQL-state-string defines COMPLETION condition. This function assumes that the given string contains a valid SQL-state. @@ -967,43 +1261,4 @@ inline bool is_sqlstate_completion(const char *s) { return s[0] == '0' && s[1] == '0'; } -/** - Checks if the specified SQL-state-string defines WARNING condition. - This function assumes that the given string contains a valid SQL-state. - - @param s the condition SQLSTATE. - - @retval true if the given string defines WARNING condition. - @retval false otherwise. -*/ -inline bool is_sqlstate_warning(const char *s) -{ return s[0] == '0' && s[1] == '1'; } - - -/** - Checks if the specified SQL-state-string defines NOT FOUND condition. - This function assumes that the given string contains a valid SQL-state. - - @param s the condition SQLSTATE. - - @retval true if the given string defines NOT FOUND condition. - @retval false otherwise. -*/ -inline bool is_sqlstate_not_found(const char *s) -{ return s[0] == '0' && s[1] == '2'; } - - -/** - Checks if the specified SQL-state-string defines EXCEPTION condition. - This function assumes that the given string contains a valid SQL-state. - - @param s the condition SQLSTATE. - - @retval true if the given string defines EXCEPTION condition. - @retval false otherwise. -*/ -inline bool is_sqlstate_exception(const char *s) -{ return s[0] != '0' || s[1] > '2'; } - - #endif // SQL_ERROR_H diff --git a/sql/sql_explain.cc b/sql/sql_explain.cc index 9b474fd5dee..ca276eb87ac 100644 --- a/sql/sql_explain.cc +++ b/sql/sql_explain.cc @@ -29,6 +29,11 @@ const char * STR_DELETING_ALL_ROWS= "Deleting all rows"; const char * STR_IMPOSSIBLE_WHERE= "Impossible WHERE"; const char * STR_NO_ROWS_AFTER_PRUNING= "No matching rows after partition pruning"; +const char *unit_operation_text[4]= +{ + "UNIT RESULT","UNION RESULT","INTERSECT RESULT","EXCEPT RESULT" +}; + static void write_item(Json_writer *writer, Item *item); static void append_item_to_str(String *out, Item *item); @@ -421,8 +426,26 @@ int print_explain_row(select_result_sink *result, uint Explain_union::make_union_table_name(char *buf) { uint childno= 0; - uint len= 6, lastop= 0; - memcpy(buf, STRING_WITH_LEN("<union")); + uint len, lastop= 0; + LEX_CSTRING type; + switch (operation) + { + case OP_MIX: + lex_string_set3(&type, STRING_WITH_LEN("<unit")); + break; + case OP_UNION: + lex_string_set3(&type, STRING_WITH_LEN("<union")); + break; + case OP_INTERSECT: + lex_string_set3(&type, STRING_WITH_LEN("<intersect")); + break; + case OP_EXCEPT: + lex_string_set3(&type, STRING_WITH_LEN("<except")); + break; + default: + DBUG_ASSERT(0); + } + memcpy(buf, type.str, (len= type.length)); for (; childno < union_members.elements() && len + lastop + 5 < NAME_LEN; childno++) @@ -465,7 +488,7 @@ int Explain_union::print_explain(Explain_query *query, if (!using_tmp) return 0; - /* Print a line with "UNION RESULT" */ + /* Print a line with "UNIT RESULT" */ List<Item> item_list; Item *item_null= new (mem_root) Item_null(thd); @@ -817,6 +840,28 @@ int Explain_basic_join::print_explain(Explain_query *query, } +void Explain_select::add_linkage(Json_writer *writer) +{ + const char *operation= NULL; + switch (linkage) + { + case UNION_TYPE: + operation= "UNION"; + break; + case INTERSECT_TYPE: + operation= "INTERSECT"; + break; + case EXCEPT_TYPE: + operation= "EXCEPT"; + break; + default: + // It is the first or the only SELECT => no operation + break; + } + if (operation) + writer->add_member("operation").add_str(operation); +} + void Explain_select::print_explain_json(Explain_query *query, Json_writer *writer, bool is_analyze) { @@ -828,6 +873,7 @@ void Explain_select::print_explain_json(Explain_query *query, { writer->add_member("query_block").start_object(); writer->add_member("select_id").add_ll(select_id); + add_linkage(writer); writer->add_member("table").start_object(); writer->add_member("message").add_str(message); @@ -840,6 +886,7 @@ void Explain_select::print_explain_json(Explain_query *query, { writer->add_member("query_block").start_object(); writer->add_member("select_id").add_ll(select_id); + add_linkage(writer); if (is_analyze && time_tracker.get_loops()) { @@ -1103,7 +1150,8 @@ void Explain_index_use::set(MEM_ROOT *mem_root, KEY *key, uint key_len_arg) uint len= 0; for (uint i= 0; i < key->usable_key_parts; i++) { - key_parts_list.append_str(mem_root, key->key_part[i].field->field_name); + key_parts_list.append_str(mem_root, + key->key_part[i].field->field_name.str); len += key->key_part[i].store_length; if (len >= key_len_arg) break; diff --git a/sql/sql_explain.h b/sql/sql_explain.h index bfddf40ff33..154769fe289 100644 --- a/sql/sql_explain.h +++ b/sql/sql_explain.h @@ -207,6 +207,7 @@ public: Explain_select(MEM_ROOT *root, bool is_analyze) : Explain_basic_join(root), + linkage(UNSPECIFIED_TYPE), message(NULL), having(NULL), having_value(Item::COND_UNDEF), using_temporary(false), using_filesort(false), @@ -214,8 +215,11 @@ public: aggr_tree(NULL) {} + void add_linkage(Json_writer *writer); + public: const char *select_type; + enum sub_select_type linkage; /* If message != NULL, this is a degenerate join plan, and all subsequent @@ -316,7 +320,9 @@ public: ///////////////////////////////////////////////////////////////////////////// -/* +extern const char *unit_operation_text[4]; + +/* Explain structure for a UNION. A UNION may or may not have "Using filesort". @@ -332,6 +338,7 @@ public: {} enum explain_node_type get_type() { return EXPLAIN_UNION; } + unit_common_op operation; int get_select_id() { diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 4a27244f5b9..babb09a1267 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -286,7 +286,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen) mdl_savepoint= thd->mdl_context.mdl_savepoint(); /* for now HANDLER can be used only for real TABLES */ - tables->required_type= FRMTYPE_TABLE; + tables->required_type= TABLE_TYPE_NORMAL; /* We use open_tables() here, rather than, say, @@ -549,7 +549,7 @@ SQL_HANDLER *mysql_ha_find_handler(THD *thd, const char *name) static bool mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, - enum enum_ha_read_modes mode, char *keyname, + enum enum_ha_read_modes mode, const char *keyname, List<Item> *key_expr, Item *cond, bool in_prepare) { @@ -671,7 +671,7 @@ mysql_ha_fix_cond_and_key(SQL_HANDLER *handler, */ bool mysql_ha_read(THD *thd, TABLE_LIST *tables, - enum enum_ha_read_modes mode, char *keyname, + enum enum_ha_read_modes mode, const char *keyname, List<Item> *key_expr, enum ha_rkey_function ha_rkey_mode, Item *cond, ha_rows select_limit_cnt, ha_rows offset_limit_cnt) @@ -926,7 +926,8 @@ err0: */ SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables, - enum enum_ha_read_modes mode, char *keyname, + enum enum_ha_read_modes mode, + const char *keyname, List<Item> *key_expr, Item *cond) { SQL_HANDLER *handler; diff --git a/sql/sql_handler.h b/sql/sql_handler.h index 7fe5ae5bba8..cb1aff1d727 100644 --- a/sql/sql_handler.h +++ b/sql/sql_handler.h @@ -68,7 +68,7 @@ struct TABLE_LIST; bool mysql_ha_open(THD *thd, TABLE_LIST *tables, SQL_HANDLER *reopen); bool mysql_ha_close(THD *thd, TABLE_LIST *tables); -bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes,char *, +bool mysql_ha_read(THD *, TABLE_LIST *,enum enum_ha_read_modes, const char *, List<Item> *,enum ha_rkey_function,Item *,ha_rows,ha_rows); void mysql_ha_flush(THD *thd); void mysql_ha_flush_tables(THD *thd, TABLE_LIST *all_tables); @@ -78,6 +78,7 @@ void mysql_ha_set_explicit_lock_duration(THD *thd); void mysql_ha_rm_temporary_tables(THD *thd); SQL_HANDLER *mysql_ha_read_prepare(THD *thd, TABLE_LIST *tables, - enum enum_ha_read_modes mode, char *keyname, + enum enum_ha_read_modes mode, + const char *keyname, List<Item> *key_expr, Item *cond); #endif diff --git a/sql/sql_help.cc b/sql/sql_help.cc index fc218cb18f2..bd11397e2aa 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -92,10 +92,14 @@ static bool init_fields(THD *thd, TABLE_LIST *tables, context->resolve_in_table_list_only(tables); for (; count-- ; find_fields++) { + LEX_CSTRING field_name= {find_fields->field_name, + strlen(find_fields->field_name) }; /* We have to use 'new' here as field will be re_linked on free */ - Item_field *field= new (thd->mem_root) Item_field(thd, context, - "mysql", find_fields->table_name, - find_fields->field_name); + Item_field *field= (new (thd->mem_root) + Item_field(thd, context, + "mysql", + find_fields->table_name, + &field_name)); if (!(find_fields->field= find_field_in_tables(thd, field, tables, NULL, 0, REPORT_ALL_ERRORS, 1, TRUE))) diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index a226ea17eb3..3d81ab35373 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -289,7 +289,8 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (check_unique && thd->dup_field) { - my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dup_field->field_name); + my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), + thd->dup_field->field_name.str); DBUG_RETURN(-1); } } @@ -328,7 +329,8 @@ static bool has_no_default_value(THD *thd, Field *field, TABLE_LIST *table_list) else { push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NO_DEFAULT_FOR_FIELD, - ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD), field->field_name); + ER_THD(thd, ER_NO_DEFAULT_FOR_FIELD), + field->field_name.str); } return thd->really_abort_on_warning(); } @@ -4117,7 +4119,7 @@ static TABLE *create_table_from_items(THD *thd, if (!mysql_create_table_no_lock(thd, create_table->db, create_table->table_name, create_info, alter_info, NULL, - select_field_count)) + select_field_count, create_table)) { DEBUG_SYNC(thd,"create_table_select_before_open"); diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 7879edc3f67..169f33f81e2 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -30,6 +30,14 @@ #include "sp.h" #include "sql_select.h" #include "sql_cte.h" +#include "sql_signal.h" + + +void LEX::parse_error() +{ + thd->parse_error(); +} + static int lex_one_token(YYSTYPE *yylval, THD *thd); @@ -43,8 +51,12 @@ sys_var *trg_new_row_fake_var= (sys_var*) 0x01; /** LEX_STRING constant for null-string to be used in parser and other places. */ -const LEX_STRING null_lex_str= {NULL, 0}; -const LEX_STRING empty_lex_str= {(char *) "", 0}; +const LEX_STRING empty_lex_str= {(char *) "", 0}; +const LEX_CSTRING null_clex_str= {NULL, 0}; +const LEX_CSTRING empty_clex_str= {"", 0}; +const LEX_CSTRING star_clex_str= {"*", 1}; +const LEX_CSTRING param_clex_str= {"?", 1}; + /** @note The order of the elements of this array must correspond to the order of elements in enum_binlog_stmt_unsafe. @@ -184,8 +196,8 @@ init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex) lex_start(thd); context->init(); if ((!(table_ident= new Table_ident(thd, - table->s->table_name, - table->s->db, TRUE))) || + &table->s->table_name, + &table->s->db, TRUE))) || (!(table_list= select_lex->add_table_to_list(thd, table_ident, NULL, @@ -414,27 +426,28 @@ void Lex_input_stream::body_utf8_append(const char *ptr) */ void Lex_input_stream::body_utf8_append_ident(THD *thd, - const LEX_STRING *txt, + const LEX_CSTRING *txt, const char *end_ptr) { if (!m_cpp_utf8_processed_ptr) return; - LEX_STRING utf_txt; + LEX_CSTRING utf_txt; CHARSET_INFO *txt_cs= thd->charset(); if (!my_charset_same(txt_cs, &my_charset_utf8_general_ci)) { - thd->convert_string(&utf_txt, + LEX_STRING to; + thd->convert_string(&to, &my_charset_utf8_general_ci, txt->str, (uint) txt->length, txt_cs); + utf_txt.str= to.str; + utf_txt.length= to.length; + } else - { - utf_txt.str= txt->str; - utf_txt.length= txt->length; - } + utf_txt= *txt; /* NOTE: utf_txt.length is in bytes, not in symbols. */ @@ -595,7 +608,7 @@ Lex_input_stream::get_escape_func(THD *thd, my_wc_t sep) const @param sep - the string delimiter (single or double quote) */ void Lex_input_stream::body_utf8_append_escape(THD *thd, - const LEX_STRING *txt, + const LEX_CSTRING *txt, CHARSET_INFO *cs, const char *end_ptr, my_wc_t sep) @@ -644,115 +657,122 @@ void Lex_input_stream::reduce_digest_token(uint token_left, uint token_right) } } +void lex_start(THD *thd) +{ + DBUG_ENTER("lex_start"); + thd->lex->start(thd); + DBUG_VOID_RETURN; +} + + /* This is called before every query that is to be parsed. Because of this, it's critical to not do too much things here. (We already do too much here) */ -void lex_start(THD *thd) +void LEX::start(THD *thd_arg) { - LEX *lex= thd->lex; - DBUG_ENTER("lex_start"); + DBUG_ENTER("LEX::start"); - lex->thd= lex->unit.thd= thd; + thd= unit.thd= thd_arg; - DBUG_ASSERT(!lex->explain); + DBUG_ASSERT(!explain); - lex->context_stack.empty(); - lex->unit.init_query(); - lex->unit.init_select(); + context_stack.empty(); + unit.init_query(); + unit.init_select(); + select_lex.linkage= UNSPECIFIED_TYPE; /* 'parent_lex' is used in init_query() so it must be before it. */ - lex->select_lex.parent_lex= lex; - lex->select_lex.init_query(); - lex->curr_with_clause= 0; - lex->with_clauses_list= 0; - lex->with_clauses_list_last_next= &lex->with_clauses_list; - lex->value_list.empty(); - lex->update_list.empty(); - lex->set_var_list.empty(); - lex->param_list.empty(); - lex->view_list.empty(); - lex->with_column_list.empty(); - lex->with_persistent_for_clause= FALSE; - lex->column_list= NULL; - lex->index_list= NULL; - lex->prepared_stmt_params.empty(); - lex->auxiliary_table_list.empty(); - lex->unit.next= lex->unit.master= - lex->unit.link_next= lex->unit.return_to= 0; - lex->unit.prev= lex->unit.link_prev= 0; - lex->unit.slave= lex->current_select= - lex->all_selects_list= &lex->select_lex; - lex->select_lex.master= &lex->unit; - lex->select_lex.prev= &lex->unit.slave; - lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0; - lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list); - lex->select_lex.options= 0; - lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; - lex->select_lex.init_order(); - lex->select_lex.group_list.empty(); - if (lex->select_lex.group_list_ptrs) - lex->select_lex.group_list_ptrs->clear(); - lex->describe= 0; - lex->analyze_stmt= 0; - lex->explain_json= false; - lex->subqueries= FALSE; - lex->context_analysis_only= 0; - lex->derived_tables= 0; - lex->safe_to_cache_query= 1; - lex->parsing_options.reset(); - lex->empty_field_list_on_rset= 0; - lex->select_lex.select_number= 1; - lex->part_info= 0; - lex->select_lex.in_sum_expr=0; - lex->select_lex.ftfunc_list_alloc.empty(); - lex->select_lex.ftfunc_list= &lex->select_lex.ftfunc_list_alloc; - lex->select_lex.group_list.empty(); - lex->select_lex.order_list.empty(); - lex->select_lex.gorder_list.empty(); - lex->m_sql_cmd= NULL; - lex->duplicates= DUP_ERROR; - lex->ignore= 0; - lex->spname= NULL; - lex->spcont= NULL; - lex->proc_list.first= 0; - lex->escape_used= FALSE; - lex->query_tables= 0; - lex->reset_query_tables_list(FALSE); - lex->expr_allows_subselect= TRUE; - lex->use_only_table_context= FALSE; - lex->parse_vcol_expr= FALSE; - lex->check_exists= FALSE; - lex->create_info.lex_start(); - lex->verbose= 0; - - lex->name= null_lex_str; - lex->event_parse_data= NULL; - lex->profile_options= PROFILE_NONE; - lex->nest_level=0 ; - lex->select_lex.nest_level_base= &lex->unit; - lex->allow_sum_func= 0; - lex->in_sum_func= NULL; - - lex->used_tables= 0; - lex->only_view= FALSE; - lex->reset_slave_info.all= false; - lex->limit_rows_examined= 0; - lex->limit_rows_examined_cnt= ULONGLONG_MAX; - lex->var_list.empty(); - lex->stmt_var_list.empty(); - lex->proc_list.elements=0; - - lex->save_group_list.empty(); - lex->save_order_list.empty(); - lex->win_ref= NULL; - lex->win_frame= NULL; - lex->frame_top_bound= NULL; - lex->frame_bottom_bound= NULL; - lex->win_spec= NULL; - - lex->is_lex_started= TRUE; + select_lex.parent_lex= this; + select_lex.init_query(); + curr_with_clause= 0; + with_clauses_list= 0; + with_clauses_list_last_next= &with_clauses_list; + create_view= NULL; + value_list.empty(); + update_list.empty(); + set_var_list.empty(); + param_list.empty(); + view_list.empty(); + with_column_list.empty(); + with_persistent_for_clause= FALSE; + column_list= NULL; + index_list= NULL; + prepared_stmt_params.empty(); + auxiliary_table_list.empty(); + unit.next= unit.master= unit.link_next= unit.return_to= 0; + unit.prev= unit.link_prev= 0; + unit.slave= current_select= all_selects_list= &select_lex; + select_lex.master= &unit; + select_lex.prev= &unit.slave; + select_lex.link_next= select_lex.slave= select_lex.next= 0; + select_lex.link_prev= (st_select_lex_node**)&(all_selects_list); + select_lex.options= 0; + select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; + select_lex.init_order(); + select_lex.group_list.empty(); + if (select_lex.group_list_ptrs) + select_lex.group_list_ptrs->clear(); + describe= 0; + analyze_stmt= 0; + explain_json= false; + subqueries= FALSE; + context_analysis_only= 0; + derived_tables= 0; + safe_to_cache_query= 1; + parsing_options.reset(); + empty_field_list_on_rset= 0; + select_lex.select_number= 1; + part_info= 0; + select_lex.in_sum_expr=0; + select_lex.ftfunc_list_alloc.empty(); + select_lex.ftfunc_list= &select_lex.ftfunc_list_alloc; + select_lex.group_list.empty(); + select_lex.order_list.empty(); + select_lex.gorder_list.empty(); + m_sql_cmd= NULL; + duplicates= DUP_ERROR; + ignore= 0; + spname= NULL; + spcont= NULL; + proc_list.first= 0; + escape_used= FALSE; + query_tables= 0; + reset_query_tables_list(FALSE); + expr_allows_subselect= TRUE; + use_only_table_context= FALSE; + parse_vcol_expr= FALSE; + check_exists= FALSE; + create_info.lex_start(); + verbose= 0; + + name= null_clex_str; + event_parse_data= NULL; + profile_options= PROFILE_NONE; + nest_level=0 ; + select_lex.nest_level_base= &unit; + allow_sum_func= 0; + in_sum_func= NULL; + + used_tables= 0; + table_type= TABLE_TYPE_UNKNOWN; + reset_slave_info.all= false; + limit_rows_examined= 0; + limit_rows_examined_cnt= ULONGLONG_MAX; + var_list.empty(); + stmt_var_list.empty(); + proc_list.elements=0; + + save_group_list.empty(); + save_order_list.empty(); + win_ref= NULL; + win_frame= NULL; + frame_top_bound= NULL; + frame_bottom_bound= NULL; + win_spec= NULL; + + is_lex_started= TRUE; DBUG_VOID_RETURN; } @@ -873,17 +893,70 @@ bool is_keyword(const char *name, uint len) @retval 1 name isn't a function */ -bool is_lex_native_function(const LEX_STRING *name) +bool is_lex_native_function(const LEX_CSTRING *name) { DBUG_ASSERT(name != NULL); return (get_hash_symbol(name->str, (uint) name->length, 1) != 0); } + +bool is_native_function(THD *thd, const LEX_CSTRING *name) +{ + if (find_native_function_builder(thd, name)) + return true; + + if (is_lex_native_function(name)) + return true; + + return false; +} + + +bool is_native_function_with_warn(THD *thd, const LEX_CSTRING *name) +{ + if (!is_native_function(thd, name)) + return false; + /* + This warning will be printed when + [1] A client query is parsed, + [2] A stored function is loaded by db_load_routine. + Printing the warning for [2] is intentional, to cover the + following scenario: + - A user define a SF 'foo' using MySQL 5.N + - An application uses select foo(), and works. + - MySQL 5.{N+1} defines a new native function 'foo', as + part of a new feature. + - MySQL 5.{N+1} documentation is updated, and should mention + that there is a potential incompatible change in case of + existing stored function named 'foo'. + - The user deploys 5.{N+1}. At this point, 'select foo()' + means something different, and the user code is most likely + broken (it's only safe if the code is 'select db.foo()'). + With a warning printed when the SF is loaded (which has to + occur before the call), the warning will provide a hint + explaining the root cause of a later failure of 'select foo()'. + With no warning printed, the user code will fail with no + apparent reason. + Printing a warning each time db_load_routine is executed for + an ambiguous function is annoying, since that can happen a lot, + but in practice should not happen unless there *are* name + collisions. + If a collision exists, it should not be silenced but fixed. + */ + push_warning_printf(thd, + Sql_condition::WARN_LEVEL_NOTE, + ER_NATIVE_FCT_NAME_COLLISION, + ER_THD(thd, ER_NATIVE_FCT_NAME_COLLISION), + name->str); + return true; +} + + /* make a copy of token before ptr and set yytoklen */ -static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length) +static LEX_CSTRING get_token(Lex_input_stream *lip, uint skip, uint length) { - LEX_STRING tmp; + LEX_CSTRING tmp; lip->yyUnget(); // ptr points now after last token char tmp.length= length; tmp.str= lip->m_thd->strmake(lip->get_tok_start() + skip, tmp.length); @@ -901,18 +974,17 @@ static LEX_STRING get_token(Lex_input_stream *lip, uint skip, uint length) future to operate multichar strings (like ucs2) */ -static LEX_STRING get_quoted_token(Lex_input_stream *lip, - uint skip, - uint length, char quote) +static LEX_CSTRING get_quoted_token(Lex_input_stream *lip, + uint skip, + uint length, char quote) { - LEX_STRING tmp; + LEX_CSTRING tmp; const char *from, *end; char *to; lip->yyUnget(); // ptr points now after last token char tmp.length= length; - tmp.str=(char*) lip->m_thd->alloc(tmp.length+1); + tmp.str= to= (char*) lip->m_thd->alloc(tmp.length+1); from= lip->get_tok_start() + skip; - to= tmp.str; end= to+length; lip->m_cpp_text_start= lip->get_cpp_tok_start() + skip; @@ -1002,18 +1074,19 @@ Lex_input_stream::unescape(CHARSET_INFO *cs, char *to, Fix sometimes to do only one scan of the string */ -bool Lex_input_stream::get_text(LEX_STRING *dst, uint sep, +bool Lex_input_stream::get_text(Lex_string_with_metadata_st *dst, uint sep, int pre_skip, int post_skip) { reg1 uchar c; uint found_escape=0; CHARSET_INFO *cs= m_thd->charset(); - tok_bitmap= 0; + dst->set_8bit(false); while (! eof()) { c= yyGet(); - tok_bitmap|= c; + if (c & 0x80) + dst->set_8bit(true); #ifdef USE_MB { int l; @@ -1046,6 +1119,7 @@ bool Lex_input_stream::get_text(LEX_STRING *dst, uint sep, /* Found end. Unescape and return string */ const char *str, *end; + char *to; str= get_tok_start(); end= get_ptr(); @@ -1054,24 +1128,25 @@ bool Lex_input_stream::get_text(LEX_STRING *dst, uint sep, end -= post_skip; DBUG_ASSERT(end >= str); - if (!(dst->str= (char*) m_thd->alloc((uint) (end - str) + 1))) + if (!(to= (char*) m_thd->alloc((uint) (end - str) + 1))) { - dst->str= (char*) ""; // Sql_alloc has set error flag + dst->str= ""; // Sql_alloc has set error flag dst->length= 0; return true; } + dst->str= to; m_cpp_text_start= get_cpp_tok_start() + pre_skip; m_cpp_text_end= get_cpp_ptr() - post_skip; if (!found_escape) { - memcpy(dst->str, str, dst->length= (end - str)); - dst->str[dst->length]= 0; + memcpy(to, str, dst->length= (end - str)); + to[dst->length]= 0; } else { - dst->length= unescape(cs, dst->str, str, end, sep); + dst->length= unescape(cs, to, str, end, sep); } return false; } @@ -1281,6 +1356,11 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) return token; } +int ORAlex(YYSTYPE *yylval, THD *thd) +{ + return MYSQLlex(yylval, thd); +} + static int lex_one_token(YYSTYPE *yylval, THD *thd) { reg1 uchar UNINIT_VAR(c); @@ -1379,18 +1459,17 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd) } /* Found N'string' */ lip->yySkip(); // Skip ' - if (lip->get_text(&yylval->lex_str, (sep= lip->yyGetLast()), 2, 1)) + if (lip->get_text(&yylval->lex_string_with_metadata, + (sep= lip->yyGetLast()), 2, 1)) { state= MY_LEX_CHAR; // Read char by char break; } lip->body_utf8_append(lip->m_cpp_text_start); - lip->body_utf8_append_escape(thd, &yylval->lex_str, + lip->body_utf8_append_escape(thd, &yylval->lex_string_with_metadata, national_charset_info, lip->m_cpp_text_end, sep); - - lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1; return(NCHAR_STRING); } case MY_LEX_IDENT_OR_HEX: @@ -1641,8 +1720,13 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd) return(IDENT_QUOTED); } case MY_LEX_INT_OR_REAL: // Complete int or incomplete real - if (c != '.') - { // Found complete integer number. + if (c != '.' || lip->yyPeek() == '.') + { + /* + Found a complete integer number: + - the number is either not followed by a dot at all, or + - the number is followed by a double dot as in: FOR i IN 1..10 + */ yylval->lex_str=get_token(lip, 0, lip->yyLength()); return int_token(yylval->lex_str.str, (uint) yylval->lex_str.length); } @@ -1747,7 +1831,8 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd) case MY_LEX_STRING: // Incomplete text string { uint sep; - if (lip->get_text(&yylval->lex_str, (sep= lip->yyGetLast()), 1, 1)) + if (lip->get_text(&yylval->lex_string_with_metadata, + (sep= lip->yyGetLast()), 1, 1)) { state= MY_LEX_CHAR; // Read char by char break; @@ -1755,11 +1840,9 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd) CHARSET_INFO *strcs= lip->m_underscore_cs ? lip->m_underscore_cs : cs; lip->body_utf8_append(lip->m_cpp_text_start); - lip->body_utf8_append_escape(thd, &yylval->lex_str, strcs, - lip->m_cpp_text_end, sep); + lip->body_utf8_append_escape(thd, &yylval->lex_string_with_metadata, + strcs, lip->m_cpp_text_end, sep); lip->m_underscore_cs= NULL; - - lex->text_string_is_7bit= (lip->tok_bitmap & 0x80) ? 0 : 1; return(TEXT_STRING); } case MY_LEX_COMMENT: // Comment @@ -1945,8 +2028,13 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd) /* Actually real shouldn't start with . but allow them anyhow */ case MY_LEX_REAL_OR_POINT: - if (my_isdigit(cs,lip->yyPeek())) + if (my_isdigit(cs,(c= lip->yyPeek()))) state = MY_LEX_REAL; // Real + else if (c == '.') + { + lip->yySkip(); + return DOT_DOT_SYM; + } else { state= MY_LEX_IDENT_SEP; // return '.' @@ -2018,7 +2106,7 @@ static int lex_one_token(YYSTYPE *yylval, THD *thd) } -void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str, uint *prefix_length) +void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, uint *prefix_length) { /* TODO: @@ -2069,7 +2157,7 @@ void st_select_lex_unit::init_query() select_limit_cnt= HA_POS_ERROR; offset_limit_cnt= 0; union_distinct= 0; - prepared= optimized= executed= 0; + prepared= optimized= optimized_2= executed= 0; optimize_started= 0; item= 0; union_result= 0; @@ -2085,6 +2173,7 @@ void st_select_lex_unit::init_query() with_clause= 0; with_element= 0; columns_are_renamed= false; + intersect_mark= NULL; } void st_select_lex::init_query() @@ -2159,7 +2248,6 @@ void st_select_lex::init_select() ftfunc_list_alloc.empty(); inner_sum_func_list= 0; ftfunc_list= &ftfunc_list_alloc; - linkage= UNSPECIFIED_TYPE; order_list.elements= 0; order_list.first= 0; order_list.next= &order_list.first; @@ -2473,7 +2561,7 @@ uint st_select_lex_node::get_in_sum_expr() { return 0; } TABLE_LIST* st_select_lex_node::get_table_list() { return 0; } List<Item>* st_select_lex_node::get_item_list() { return 0; } TABLE_LIST *st_select_lex_node::add_table_to_list(THD *thd, Table_ident *table, - LEX_STRING *alias, + LEX_CSTRING *alias, ulong table_join_options, thr_lock_type flags, enum_mdl_type mdl_type, @@ -2692,11 +2780,24 @@ void st_select_lex_unit::print(String *str, enum_query_type query_type) { if (sl != first_select()) { - str->append(STRING_WITH_LEN(" union ")); - if (union_all) - str->append(STRING_WITH_LEN("all ")); - else if (union_distinct == sl) - union_all= TRUE; + switch (sl->linkage) + { + default: + DBUG_ASSERT(0); + case UNION_TYPE: + str->append(STRING_WITH_LEN(" union ")); + if (union_all) + str->append(STRING_WITH_LEN("all ")); + else if (union_distinct == sl) + union_all= TRUE; + break; + case INTERSECT_TYPE: + str->append(STRING_WITH_LEN(" intersect ")); + break; + case EXCEPT_TYPE: + str->append(STRING_WITH_LEN(" except ")); + break; + } } if (sl->braces) str->append('('); @@ -3135,7 +3236,7 @@ uint8 LEX::get_effective_with_check(TABLE_LIST *view) */ bool -LEX::copy_db_to(char **p_db, size_t *p_db_length) const +LEX::copy_db_to(const char **p_db, size_t *p_db_length) const { if (sphead && sphead->m_name.str) { @@ -3279,6 +3380,7 @@ void LEX::set_trg_event_type_for_tables() REPLACE SELECT is handled later in this method. */ case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: new_trg_event_map|= static_cast<uint8> (1 << static_cast<int>(TRG_EVENT_INSERT)); break; @@ -3717,7 +3819,7 @@ void st_select_lex::alloc_index_hints (THD *thd) RETURN VALUE 0 on success, non-zero otherwise */ -bool st_select_lex::add_index_hint (THD *thd, char *str, uint length) +bool st_select_lex::add_index_hint (THD *thd, const char *str, uint length) { return index_hints->push_front(new (thd->mem_root) Index_hint(current_index_hint_type, @@ -4231,7 +4333,8 @@ void SELECT_LEX::update_used_tables() } for (ORDER *order= group_list.first; order; order= order->next) (*order->item)->update_used_tables(); - if (!master_unit()->is_union() || master_unit()->global_parameters() != this) + if (!master_unit()->is_unit_op() || + master_unit()->global_parameters() != this) { for (ORDER *order= order_list.first; order; order= order->next) (*order->item)->update_used_tables(); @@ -4289,7 +4392,7 @@ void st_select_lex::update_correlated_cache() is_correlated|= MY_TEST((*order->item)->used_tables() & OUTER_REF_TABLE_BIT); - if (!master_unit()->is_union()) + if (!master_unit()->is_unit_op()) { for (ORDER *order= order_list.first; order; order= order->next) is_correlated|= MY_TEST((*order->item)->used_tables() & @@ -4361,7 +4464,12 @@ void st_select_lex::set_explain_type(bool on_the_fly) { /* If we're a direct child of a UNION, we're the first sibling there */ if (linkage == DERIVED_TABLE_TYPE) - type= "DERIVED"; + { + if (is_uncacheable & UNCACHEABLE_DEPENDENT) + type= "LATERAL DERIVED"; + else + type= "DERIVED"; + } else if (using_materialization) type= "MATERIALIZED"; else @@ -4377,42 +4485,53 @@ void st_select_lex::set_explain_type(bool on_the_fly) } else { - /* This a non-first sibling in UNION */ - if (is_uncacheable & UNCACHEABLE_DEPENDENT) - type= "DEPENDENT UNION"; - else if (using_materialization) - type= "MATERIALIZED UNION"; - else + switch (linkage) { - type= is_uncacheable ? "UNCACHEABLE UNION": "UNION"; - if (this == master_unit()->fake_select_lex) - type= "UNION RESULT"; - /* - join below may be =NULL when this functions is called at an early - stage. It will be later called again and we will set the correct - value. - */ - if (join) + case INTERSECT_TYPE: + type= "INTERSECT"; + break; + case EXCEPT_TYPE: + type= "EXCEPT"; + break; + default: + /* This a non-first sibling in UNION */ + if (is_uncacheable & UNCACHEABLE_DEPENDENT) + type= "DEPENDENT UNION"; + else if (using_materialization) + type= "MATERIALIZED UNION"; + else { - bool uses_cte= false; - for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, - WITH_CONST_TABLES); - tab; - tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) + type= is_uncacheable ? "UNCACHEABLE UNION": "UNION"; + if (this == master_unit()->fake_select_lex) + type= unit_operation_text[master_unit()->common_op()]; + /* + join below may be =NULL when this functions is called at an early + stage. It will be later called again and we will set the correct + value. + */ + if (join) { - /* - pos_in_table_list=NULL for e.g. post-join aggregation JOIN_TABs. - */ - if (tab->table && tab->table->pos_in_table_list && - tab->table->pos_in_table_list->with) + bool uses_cte= false; + for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, + WITH_CONST_TABLES); + tab; + tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { - uses_cte= true; - break; + /* + pos_in_table_list=NULL for e.g. post-join aggregation JOIN_TABs. + */ + if (tab->table && tab->table->pos_in_table_list && + tab->table->pos_in_table_list->with) + { + uses_cte= true; + break; + } } + if (uses_cte) + type= "RECURSIVE UNION"; } - if (uses_cte) - type= "RECURSIVE UNION"; } + break; } } } @@ -4451,8 +4570,20 @@ void SELECT_LEX::increase_derived_records(ha_rows records) return; } - select_union *result= (select_union*)unit->result; - result->records+= records; + select_unit *result= (select_unit*)unit->result; + switch (linkage) + { + case INTERSECT_TYPE: + // result of intersect can't be more then one of components + set_if_smaller(result->records, records); + case EXCEPT_TYPE: + // in worse case none of record will be removed + break; + default: + // usual UNION + result->records+= records; + break; + } } @@ -4477,7 +4608,8 @@ void SELECT_LEX::mark_const_derived(bool empty) { if (!empty) increase_derived_records(1); - if (!master_unit()->is_union() && !derived->is_merged_derived()) + if (!master_unit()->is_unit_op() && !derived->is_merged_derived() && + !(join && join->with_two_phase_optimization)) derived->fill_me= TRUE; } } @@ -4706,6 +4838,42 @@ void LEX::restore_set_statement_var() DBUG_VOID_RETURN; } +unit_common_op st_select_lex_unit::common_op() +{ + SELECT_LEX *first= first_select(); + bool first_op= TRUE; + unit_common_op operation= OP_MIX; // if no op + for (SELECT_LEX *sl= first; sl; sl= sl->next_select()) + { + if (sl != first) + { + unit_common_op op; + switch (sl->linkage) + { + case INTERSECT_TYPE: + op= OP_INTERSECT; + break; + case EXCEPT_TYPE: + op= OP_EXCEPT; + break; + default: + op= OP_UNION; + break; + } + if (first_op) + { + operation= op; + first_op= FALSE; + } + else + { + if (operation != op) + operation= OP_MIX; + } + } + } + return operation; +} /* Save explain structures of a UNION. The only variable member is whether the union has "Using filesort". @@ -4742,11 +4910,10 @@ int st_select_lex_unit::save_union_explain(Explain_query *output) Note: Non-merged semi-joins cannot be made out of UNIONs currently, so we dont ever set EXPLAIN_NODE_NON_MERGED_SJ. */ - for (SELECT_LEX *sl= first; sl; sl= sl->next_select()) eu->add_select(sl->select_number); - eu->fake_select_type= "UNION RESULT"; + eu->fake_select_type= unit_operation_text[eu->operation= common_op()]; eu->using_filesort= MY_TEST(global_parameters()->order_list.first); eu->using_tmp= union_needs_tmp_table(); @@ -4801,6 +4968,1773 @@ bool LEX::is_partition_management() const alter_info.flags == Alter_info::ALTER_REORGANIZE_PARTITION)); } + +/** + Exclude last added SELECT_LEX (current) in the UNIT and return pointer in it + (previous become currect) + + @return detached SELECT_LEX or NULL in case of error +*/ + +SELECT_LEX *LEX::exclude_last_select() +{ + DBUG_ENTER("SELECT_LEX::exclude_last_select"); + SELECT_LEX *exclude= current_select; + SELECT_LEX_UNIT *unit= exclude->master_unit(); + SELECT_LEX *sl; + DBUG_ASSERT(unit->first_select() != exclude); + /* we should go through the list to correctly set current_select */ + for(sl= unit->first_select(); + sl->next_select() && sl->next_select() != exclude; + sl= sl->next_select()); + DBUG_PRINT("info", ("excl: %p unit: %p prev: %p", exclude, unit, sl)); + if (!sl) + DBUG_RETURN(NULL); + DBUG_ASSERT(exclude->next_select() == NULL); + exclude->exclude_from_tree(); + current_select= sl; + DBUG_RETURN(exclude); +} + + +/** + Put given (new) SELECT_LEX level below after currect (last) SELECT + + LAST SELECT -> DUMMY SELECT + | + V + NEW UNIT + | + V + NEW SELECT + + SELECT (*LAST*) ... FROM (SELECT (*NEW*) ... ) + + @param nselect Select to put one level below + + @retval TRUE Error + @retval FALSE OK +*/ + +bool LEX::add_unit_in_brackets(SELECT_LEX *nselect) +{ + DBUG_ENTER("LEX::add_unit_in_brackets"); + bool distinct= nselect->master_unit()->union_distinct == nselect; + bool rc= add_select_to_union_list(distinct, nselect->linkage, 0); + if (rc) + DBUG_RETURN(TRUE); + SELECT_LEX* dummy_select= current_select; + dummy_select->automatic_brackets= TRUE; + dummy_select->linkage= nselect->linkage; + + /* stuff dummy SELECT * FROM (...) */ + Name_resolution_context *context= &dummy_select->context; + context->init(); + + /* add SELECT list*/ + Item *item= new (thd->mem_root) + Item_field(thd, context, NULL, NULL, &star_clex_str); + if (item == NULL) + DBUG_RETURN(TRUE); + if (add_item_to_list(thd, item)) + DBUG_RETURN(TRUE); + (dummy_select->with_wild)++; + + rc= mysql_new_select(this, 1, nselect); + nselect->linkage= DERIVED_TABLE_TYPE; + DBUG_ASSERT(nselect->outer_select() == dummy_select); + + current_select= dummy_select; + current_select->nest_level--; + + SELECT_LEX_UNIT *unit= nselect->master_unit(); + Table_ident *ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + DBUG_RETURN(TRUE); + char buff[10]; + LEX_CSTRING alias; + alias.length= my_snprintf(buff, sizeof(buff), + "__%u", dummy_select->select_number); + alias.str= thd->strmake(buff, alias.length); + if (!alias.str) + DBUG_RETURN(TRUE); + + TABLE_LIST *table_list; + if (!(table_list= dummy_select->add_table_to_list(thd, ti, &alias, + 0, TL_READ, + MDL_SHARED_READ))) + DBUG_RETURN(TRUE); + context->resolve_in_table_list_only(table_list); + dummy_select->add_joined_table(table_list); + + derived_tables|= DERIVED_SUBQUERY; + + current_select= nselect; + current_select->nest_level++; + DBUG_RETURN(rc); +} + + +/** + Checks if we need finish "automatic brackets" mode + + INTERSECT has higher priority then UNION and EXCEPT, so when it is need we + automatically create lower layer for INTERSECT (automatic brackets) and + here we check if we should return back one level up during parsing procedure. +*/ + +void LEX::check_automatic_up(enum sub_select_type type) +{ + if (type != INTERSECT_TYPE && + current_select->linkage == INTERSECT_TYPE && + current_select->outer_select() && + current_select->outer_select()->automatic_brackets) + { + nest_level--; + current_select= current_select->outer_select(); + } +} + + +sp_variable *LEX::sp_param_init(LEX_CSTRING *name) +{ + if (spcont->find_variable(name, true)) + { + my_error(ER_SP_DUP_PARAM, MYF(0), name->str); + return NULL; + } + sp_variable *spvar= spcont->add_variable(thd, name); + init_last_field(&spvar->field_def, name, + thd->variables.collation_database); + return spvar; +} + + +bool LEX::sp_param_fill_definition(sp_variable *spvar) +{ + return sphead->fill_spvar_definition(thd, last_field, &spvar->name); +} + + +void LEX::set_stmt_init() +{ + sql_command= SQLCOM_SET_OPTION; + mysql_init_select(this); + option_type= OPT_SESSION; + autocommit= 0; +}; + + +bool LEX::init_internal_variable(struct sys_var_with_base *variable, + const LEX_CSTRING *name) +{ + sp_variable *spv; + + /* Best effort lookup for system variable. */ + if (!spcont || !(spv = spcont->find_variable(name, false))) + { + struct sys_var_with_base tmp= {NULL, *name}; + + /* Not an SP local variable */ + if (find_sys_var_null_base(thd, &tmp)) + return true; + + *variable= tmp; + return false; + } + + /* + Possibly an SP local variable (or a shadowed sysvar). + Will depend on the context of the SET statement. + */ + variable->var= NULL; + variable->base_name= *name; + return false; +} + + +bool LEX::is_trigger_new_or_old_reference(const LEX_CSTRING *name) +{ + return sphead && sphead->m_handler->type() == TYPE_ENUM_TRIGGER && + name->length == 3 && + (!my_strcasecmp(system_charset_info, name->str, "NEW") || + !my_strcasecmp(system_charset_info, name->str, "OLD")); +} + + +bool LEX::init_internal_variable(struct sys_var_with_base *variable, + const LEX_CSTRING *dbname, + const LEX_CSTRING *name) +{ + if (check_reserved_words(dbname)) + { + my_error(ER_UNKNOWN_STRUCTURED_VARIABLE, MYF(0), + (int) dbname->length, dbname->str); + return true; + } + if (is_trigger_new_or_old_reference(dbname)) + { + if (dbname->str[0]=='O' || dbname->str[0]=='o') + { + my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", ""); + return true; + } + if (trg_chistics.event == TRG_EVENT_DELETE) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); + return true; + } + if (trg_chistics.action_time == TRG_ACTION_AFTER) + { + my_error(ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after "); + return true; + } + /* This special combination will denote field of NEW row */ + variable->var= trg_new_row_fake_var; + variable->base_name= *name; + return false; + } + + sys_var *tmp= find_sys_var_ex(thd, name->str, name->length, true, false); + if (!tmp) + { + my_error(ER_UNKNOWN_STRUCTURED_VARIABLE, MYF(0), + (int) dbname->length, dbname->str); + return true; + } + if (!tmp->is_struct()) + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name->str); + variable->var= tmp; + variable->base_name= *dbname; + return false; +} + + +bool LEX::init_default_internal_variable(struct sys_var_with_base *variable, + LEX_CSTRING name) +{ + sys_var *tmp= find_sys_var(thd, name.str, name.length); + if (!tmp) + return true; + if (!tmp->is_struct()) + my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name.str); + variable->var= tmp; + variable->base_name.str= (char*) "default"; + variable->base_name.length= 7; + return false; +} + +void LEX::sp_variable_declarations_init(THD *thd, int nvars) +{ + sp_variable *spvar= spcont->get_last_context_variable(); + + sphead->reset_lex(thd); + spcont->declare_var_boundary(nvars); + thd->lex->init_last_field(&spvar->field_def, &spvar->name, + thd->variables.collation_database); +} + +bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars, + const Column_definition *cdef, + Row_definition_list *row, + Item *dflt_value_item) +{ + if (!dflt_value_item && + !(dflt_value_item= new (thd->mem_root) Item_null(thd))) + return true; + + /* + Prepare all row fields. + Note, we do it only one time outside of the below loop. + The converted list in "row" is further reused by all variable + declarations processed by the current call. + Example: + DECLARE + a, b, c ROW(x VARCHAR(10) CHARACTER SET utf8); + BEGIN + ... + END; + */ + if (row && sphead->row_fill_field_definitions(thd, row)) + return true; + + for (uint i= 0 ; i < (uint) nvars ; i++) + { + sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i); + bool last= i + 1 == (uint) nvars; + + if (!spvar) + return true; + + spvar->default_value= dflt_value_item; + + if (cdef) + { + if (!last) + spvar->field_def.set_column_definition(cdef); + } + if (sphead->fill_spvar_definition(thd, &spvar->field_def, &spvar->name)) + return true; + spvar->field_def.set_row_field_definitions(row); + + /* The last instruction is responsible for freeing LEX. */ + sp_instr_set *is= new (this->thd->mem_root) + sp_instr_set(sphead->instructions(), + spcont, spvar->offset, dflt_value_item, + this, last); + if (is == NULL || sphead->add_instr(is)) + return true; + } + + spcont->declare_var_boundary(0); + return sphead->restore_lex(thd); +} + + +/** + Finalize a %ROWTYPE declaration, e.g.: + DECLARE a,b,c,d t1%ROWTYPE := ROW(1,2,3); + + @param thd - the current thd + @param nvars - the number of variables in the declaration + @param ref - the table or cursor name (see comments below) + @param def - the default value, e.g., ROW(1,2,3), or NULL (no default). +*/ +bool +LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars, + Qualified_column_ident *ref, + Item *def) +{ + uint coffp; + const sp_pcursor *pcursor= ref->table.str && ref->db.str ? NULL : + spcont->find_cursor(&ref->m_column, &coffp, + false); + + if (!def && !(def= new (thd->mem_root) Item_null(thd))) + return true; + + // Loop through all variables in the same declaration + for (uint i= 0 ; i < (uint) nvars; i++) + { + bool last= i + 1 == (uint) nvars; + sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i); + + if (pcursor) + { + spvar->field_def.set_cursor_rowtype_ref(true); + sp_instr_cursor_copy_struct *instr= + new (thd->mem_root) sp_instr_cursor_copy_struct(sphead->instructions(), + spcont, pcursor->lex(), + spvar->offset); + if (instr == NULL || sphead->add_instr(instr)) + return true; + } + else + { + /* + When parsing a qualified identifier chain, the parser does not know yet + if it's going to be a qualified column name (for %TYPE), + or a qualified table name (for %ROWTYPE). So it collects the chain + into Qualified_column_ident. + Now we know that it was actually a qualified table name (%ROWTYPE). + Create a new Table_ident from Qualified_column_ident, + shifting fields as follows: + - ref->m_column becomes table_ref->table + - ref->table becomes table_ref->db + */ + Table_ident *table_ref; + if (!(table_ref= new (thd->mem_root) Table_ident(thd, + &ref->table, + &ref->m_column, + false))) + return true; + spvar->field_def.set_table_rowtype_ref(table_ref); + } + sphead->fill_spvar_definition(thd, &spvar->field_def, &spvar->name); + spvar->default_value= def; + /* The last instruction is responsible for freeing LEX. */ + sp_instr_set *is= new (this->thd->mem_root) + sp_instr_set(sphead->instructions(), + spcont, spvar->offset, def, + this, last); + if (is == NULL || sphead->add_instr(is)) + return true; + } + // Make sure sp_rcontext is created using the invoker security context: + sphead->m_flags|= sp_head::HAS_COLUMN_TYPE_REFS; + spcont->declare_var_boundary(0); + return sphead->restore_lex(thd); +} + + +bool +LEX::sp_variable_declarations_with_ref_finalize(THD *thd, int nvars, + Qualified_column_ident *ref, + Item *def) +{ + for (uint i= 0 ; i < (uint) nvars; i++) + { + sp_variable *spvar= spcont->get_last_context_variable((uint) nvars - 1 - i); + spvar->field_def.set_column_type_ref(ref); + spvar->field_def.field_name= spvar->name; + } + sphead->m_flags|= sp_head::HAS_COLUMN_TYPE_REFS; + return sp_variable_declarations_finalize(thd, nvars, NULL, NULL, def); +} + + +/********************************************************************** + The FOR LOOP statement + + This syntax: + FOR i IN lower_bound .. upper_bound + LOOP + statements; + END LOOP; + + is translated into: + + DECLARE + i INT := lower_bound; + j INT := upper_bound; + BEGIN + WHILE i <= j + LOOP + statements; + i:= i + 1; + END LOOP; + END; +*/ + + +sp_variable *LEX::sp_add_for_loop_variable(THD *thd, const LEX_CSTRING *name, + Item *value) +{ + sp_variable *spvar= spcont->add_variable(thd, name); + spcont->declare_var_boundary(1); + spvar->field_def.field_name= spvar->name; + spvar->field_def.set_handler(&type_handler_longlong); + type_handler_longlong.Column_definition_prepare_stage2(&spvar->field_def, + NULL, HA_CAN_GEOMETRY); + if (!value && !(value= new (thd->mem_root) Item_null(thd))) + return NULL; + + spvar->default_value= value; + sp_instr_set *is= new (this->thd->mem_root) + sp_instr_set(sphead->instructions(), + spcont, spvar->offset, value, + this, true); + if (is == NULL || sphead->add_instr(is)) + return NULL; + spcont->declare_var_boundary(0); + return spvar; +} + + +sp_variable * +LEX::sp_add_for_loop_cursor_variable(THD *thd, + const LEX_CSTRING *name, + const sp_pcursor *pcursor, + uint coffset, + sp_assignment_lex *param_lex, + Item_args *parameters) +{ + sp_variable *spvar= spcont->add_variable(thd, name); + if (!spvar) + return NULL; + spcont->declare_var_boundary(1); + sphead->fill_spvar_definition(thd, &spvar->field_def, &spvar->name); + if (!(spvar->default_value= new (thd->mem_root) Item_null(thd))) + return NULL; + + spvar->field_def.set_cursor_rowtype_ref(true); + + if (sphead->add_for_loop_open_cursor(thd, spcont, spvar, pcursor, coffset, + param_lex, parameters)) + return NULL; + + spcont->declare_var_boundary(0); + return spvar; +} + + +/** + Generate a code for a FOR loop condition: + - Make Item_splocal for the FOR loop index variable + - Make Item_splocal for the FOR loop upper bound variable + - Make a comparison function item on top of these two variables +*/ +bool LEX::sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop) +{ + Item_splocal *args[2]; + for (uint i= 0 ; i < 2; i++) + { + sp_variable *src= i == 0 ? loop.m_index : loop.m_upper_bound; + args[i]= new (thd->mem_root) + Item_splocal(thd, &src->name, src->offset, src->sql_type()); + if (args[i] == NULL) + return true; +#ifndef DBUG_OFF + args[i]->m_sp= sphead; +#endif + } + + Item *expr= loop.m_direction > 0 ? + (Item *) new (thd->mem_root) Item_func_le(thd, args[0], args[1]) : + (Item *) new (thd->mem_root) Item_func_ge(thd, args[0], args[1]); + return !expr || sp_while_loop_expression(thd, expr); +} + + +/** + Generate the FOR LOOP condition code in its own lex +*/ +bool LEX::sp_for_loop_intrange_condition_test(THD *thd, + const Lex_for_loop_st &loop) +{ + spcont->set_for_loop(loop); + sphead->reset_lex(thd); + if (thd->lex->sp_for_loop_condition(thd, loop)) + return true; + return thd->lex->sphead->restore_lex(thd); +} + + +bool LEX::sp_for_loop_cursor_condition_test(THD *thd, + const Lex_for_loop_st &loop) +{ + const LEX_CSTRING *cursor_name; + Item *expr; + spcont->set_for_loop(loop); + sphead->reset_lex(thd); + cursor_name= spcont->find_cursor(loop.m_cursor_offset); + DBUG_ASSERT(cursor_name); + if (!(expr= new (thd->mem_root) Item_func_cursor_found(thd, cursor_name, + loop.m_cursor_offset))) + return true; + if (thd->lex->sp_while_loop_expression(thd, expr)) + return true; + return thd->lex->sphead->restore_lex(thd); +} + + +bool LEX::sp_for_loop_intrange_declarations(THD *thd, Lex_for_loop_st *loop, + const LEX_CSTRING *index, + const Lex_for_loop_bounds_st &bounds) +{ + if (!(loop->m_index= + bounds.m_index->sp_add_for_loop_variable(thd, index, + bounds.m_index->get_item()))) + return true; + if (!(loop->m_upper_bound= + bounds.m_upper_bound->sp_add_for_loop_upper_bound(thd, + bounds.m_upper_bound->get_item()))) + return true; + loop->m_direction= bounds.m_direction; + loop->m_implicit_cursor= 0; + return false; +} + + +bool LEX::sp_for_loop_cursor_declarations(THD *thd, + Lex_for_loop_st *loop, + const LEX_CSTRING *index, + const Lex_for_loop_bounds_st &bounds) +{ + Item *item= bounds.m_index->get_item(); + Item_splocal *item_splocal; + Item_field *item_field; + Item_func_sp *item_func_sp= NULL; + LEX_CSTRING name; + uint coffs, param_count= 0; + const sp_pcursor *pcursor; + + if ((item_splocal= item->get_item_splocal())) + name= item_splocal->m_name; + else if ((item_field= item->type() == Item::FIELD_ITEM ? + static_cast<Item_field *>(item) : NULL) && + item_field->table_name == NULL) + name= item_field->field_name; + else if (item->type() == Item::FUNC_ITEM && + static_cast<Item_func*>(item)->functype() == Item_func::FUNC_SP && + !static_cast<Item_func_sp*>(item)->get_sp_name()->m_explicit_name) + { + /* + When a FOR LOOP for a cursor with parameters is parsed: + FOR index IN cursor(1,2,3) LOOP + statements; + END LOOP; + the parser scans "cursor(1,2,3)" using the "expr" rule, + so it thinks that cursor(1,2,3) is a stored function call. + It's not easy to implement this without using "expr" because + of grammar conflicts. + As a side effect, the Item_func_sp and its arguments in the parentheses + belong to the same LEX. This is different from an explicit + "OPEN cursor(1,2,3)" where every expression belongs to a separate LEX. + */ + item_func_sp= static_cast<Item_func_sp*>(item); + name= item_func_sp->get_sp_name()->m_name; + param_count= item_func_sp->argument_count(); + } + else + { + thd->parse_error(); + return true; + } + if (!(pcursor= spcont->find_cursor_with_error(&name, &coffs, false)) || + pcursor->check_param_count_with_error(param_count)) + return true; + + if (!(loop->m_index= sp_add_for_loop_cursor_variable(thd, index, + pcursor, coffs, + bounds.m_index, + item_func_sp))) + return true; + loop->m_upper_bound= NULL; + loop->m_direction= bounds.m_direction; + loop->m_cursor_offset= coffs; + loop->m_implicit_cursor= bounds.m_implicit_cursor; + return false; +} + + +/** + Generate a code for a FOR loop index increment +*/ +bool LEX::sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop) +{ + Item_splocal *splocal= new (thd->mem_root) + Item_splocal(thd, &loop.m_index->name, loop.m_index->offset, + loop.m_index->sql_type()); + if (splocal == NULL) + return true; +#ifndef DBUG_OFF + splocal->m_sp= sphead; +#endif + Item_int *inc= new (thd->mem_root) Item_int(thd, loop.m_direction); + if (!inc) + return true; + Item *expr= new (thd->mem_root) Item_func_plus(thd, splocal, inc); + if (!expr || + sphead->set_local_variable(thd, spcont, loop.m_index, expr, this, true)) + return true; + return false; +} + + +bool LEX::sp_for_loop_intrange_finalize(THD *thd, const Lex_for_loop_st &loop) +{ + sphead->reset_lex(thd); + + // Generate FOR LOOP index increment in its own lex + DBUG_ASSERT(this != thd->lex); + if (thd->lex->sp_for_loop_increment(thd, loop) || + thd->lex->sphead->restore_lex(thd)) + return true; + + // Generate a jump to the beginning of the loop + DBUG_ASSERT(this == thd->lex); + return sp_while_loop_finalize(thd); +} + + +bool LEX::sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &loop) +{ + sp_instr_cfetch *instr= + new (thd->mem_root) sp_instr_cfetch(sphead->instructions(), + spcont, loop.m_cursor_offset); + if (instr == NULL || sphead->add_instr(instr)) + return true; + instr->add_to_varlist(loop.m_index); + // Generate a jump to the beginning of the loop + return sp_while_loop_finalize(thd); +} + +/***************************************************************************/ + +bool LEX::sp_declare_cursor(THD *thd, const LEX_CSTRING *name, + sp_lex_cursor *cursor_stmt, + sp_pcontext *param_ctx, bool add_cpush_instr) +{ + uint offp; + sp_instr_cpush *i; + + if (spcont->find_cursor(name, &offp, true)) + { + my_error(ER_SP_DUP_CURS, MYF(0), name->str); + return true; + } + cursor_stmt->set_cursor_name(name); + + if (spcont->add_cursor(name, param_ctx, cursor_stmt)) + return true; + + if (add_cpush_instr) + { + i= new (thd->mem_root) + sp_instr_cpush(sphead->instructions(), spcont, cursor_stmt, + spcont->current_cursor_count() - 1); + return i == NULL || sphead->add_instr(i); + } + return false; +} + + +/** + Generate an SP code for an "OPEN cursor_name" statement. + @param thd + @param name - Name of the cursor + @param parameters - Cursor parameters, e.g. OPEN c(1,2,3) + @returns - false on success, true on error +*/ +bool LEX::sp_open_cursor(THD *thd, const LEX_CSTRING *name, + List<sp_assignment_lex> *parameters) +{ + uint offset; + const sp_pcursor *pcursor; + uint param_count= parameters ? parameters->elements : 0; + return !(pcursor= spcont->find_cursor_with_error(name, &offset, false)) || + pcursor->check_param_count_with_error(param_count) || + sphead->add_open_cursor(thd, spcont, offset, + pcursor->param_context(), parameters); +} + + +bool LEX::sp_handler_declaration_init(THD *thd, int type) +{ + sp_handler *h= spcont->add_handler(thd, (sp_handler::enum_type) type); + + spcont= spcont->push_context(thd, sp_pcontext::HANDLER_SCOPE); + + sp_instr_hpush_jump *i= + new (thd->mem_root) sp_instr_hpush_jump(sphead->instructions(), spcont, h); + + if (i == NULL || sphead->add_instr(i)) + return true; + + /* For continue handlers, mark end of handler scope. */ + if (type == sp_handler::CONTINUE && + sphead->push_backpatch(thd, i, spcont->last_label())) + return true; + + if (sphead->push_backpatch(thd, i, spcont->push_label(thd, &empty_clex_str, 0))) + return true; + + return false; +} + + +bool LEX::sp_handler_declaration_finalize(THD *thd, int type) +{ + sp_label *hlab= spcont->pop_label(); /* After this hdlr */ + sp_instr_hreturn *i; + + if (type == sp_handler::CONTINUE) + { + i= new (thd->mem_root) sp_instr_hreturn(sphead->instructions(), spcont); + if (i == NULL || + sphead->add_instr(i)) + return true; + } + else + { /* EXIT or UNDO handler, just jump to the end of the block */ + i= new (thd->mem_root) sp_instr_hreturn(sphead->instructions(), spcont); + if (i == NULL || + sphead->add_instr(i) || + sphead->push_backpatch(thd, i, spcont->last_label())) /* Block end */ + return true; + } + sphead->backpatch(hlab); + spcont= spcont->pop_context(); + return false; +} + + +void LEX::sp_block_init(THD *thd, const LEX_CSTRING *label) +{ + spcont->push_label(thd, label, sphead->instructions(), sp_label::BEGIN); + spcont= spcont->push_context(thd, sp_pcontext::REGULAR_SCOPE); +} + + +bool LEX::sp_block_finalize(THD *thd, const Lex_spblock_st spblock, + class sp_label **splabel) +{ + sp_head *sp= sphead; + sp_pcontext *ctx= spcont; + sp_instr *i; + + sp->backpatch(ctx->last_label()); /* We always have a label */ + if (spblock.hndlrs) + { + i= new (thd->mem_root) + sp_instr_hpop(sp->instructions(), ctx, spblock.hndlrs); + if (i == NULL || + sp->add_instr(i)) + return true; + } + if (spblock.curs) + { + i= new (thd->mem_root) + sp_instr_cpop(sp->instructions(), ctx, spblock.curs); + if (i == NULL || + sp->add_instr(i)) + return true; + } + spcont= ctx->pop_context(); + *splabel= spcont->pop_label(); + return false; +} + + +bool LEX::sp_block_finalize(THD *thd, const Lex_spblock_st spblock, + const LEX_CSTRING *end_label) +{ + sp_label *splabel; + if (sp_block_finalize(thd, spblock, &splabel)) + return true; + if (end_label->str && + my_strcasecmp(system_charset_info, + end_label->str, splabel->name.str) != 0) + { + my_error(ER_SP_LABEL_MISMATCH, MYF(0), end_label->str); + return true; + } + return false; +} + + +sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name) +{ + sp_name *res; + LEX_CSTRING db; + if (check_routine_name(name) || + copy_db_to(&db.str, &db.length) || + (!(res= new (thd->mem_root) sp_name(&db, name, false)))) + return NULL; + return res; +} + + +sp_name *LEX::make_sp_name(THD *thd, const LEX_CSTRING *name1, + const LEX_CSTRING *name2) +{ + sp_name *res; + LEX_CSTRING norm_name1; + if (!name1->str || + !thd->make_lex_string(&norm_name1, name1->str, name1->length) || + check_db_name((LEX_STRING *) &norm_name1)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), name1->str); + return NULL; + } + if (check_routine_name(name2) || + (!(res= new (thd->mem_root) sp_name(&norm_name1, name2, true)))) + return NULL; + return res; +} + + +sp_head *LEX::make_sp_head(THD *thd, const sp_name *name, + const Sp_handler *sph) +{ + sp_head *sp; + + /* Order is important here: new - reset - init */ + if ((sp= new sp_head(sph))) + { + sp->reset_thd_mem_root(thd); + sp->init(this); + if (name) + sp->init_sp_name(name); + sphead= sp; + } + sp_chistics.init(); + return sp; +} + + +bool LEX::sp_body_finalize_procedure(THD *thd) +{ + if (sphead->check_unresolved_goto()) + return true; + sphead->set_stmt_end(thd); + sphead->restore_thd_mem_root(thd); + return false; +} + + +bool LEX::sp_body_finalize_function(THD *thd) +{ + if (sphead->is_not_allowed_in_function("function")) + return true; + if (!(sphead->m_flags & sp_head::HAS_RETURN)) + { + my_error(ER_SP_NORETURN, MYF(0), ErrConvDQName(sphead).ptr()); + return true; + } + if (sp_body_finalize_procedure(thd)) + return true; + (void) is_native_function_with_warn(thd, &sphead->m_name); + return false; +} + + +bool LEX::sp_block_with_exceptions_finalize_declarations(THD *thd) +{ + /* + [ DECLARE declarations ] + BEGIN executable_section + [ EXCEPTION exceptions ] + END + + We are now at the "BEGIN" keyword. + We have collected all declarations, including DECLARE HANDLER directives. + But there will be possibly more handlers in the EXCEPTION section. + + Generate a forward jump from the end of the DECLARE section to the + beginning of the EXCEPTION section, over the executable section. + */ + return sphead->add_instr_jump(thd, spcont); +} + + +bool +LEX::sp_block_with_exceptions_finalize_executable_section(THD *thd, + uint executable_section_ip) +{ + /* + We're now at the end of "executable_section" of the block, + near the "EXCEPTION" or the "END" keyword. + Generate a jump to the END of the block over the EXCEPTION section. + */ + if (sphead->add_instr_jump_forward_with_backpatch(thd, spcont)) + return true; + /* + Set the destination for the jump that we added in + sp_block_with_exceptions_finalize_declarations(). + */ + sp_instr *instr= sphead->get_instr(executable_section_ip - 1); + instr->backpatch(sphead->instructions(), spcont); + return false; +} + + +bool +LEX::sp_block_with_exceptions_finalize_exceptions(THD *thd, + uint executable_section_ip, + uint exception_count) +{ + if (!exception_count) + { + /* + The jump from the end of DECLARE section to + the beginning of the EXCEPTION section that we added in + sp_block_with_exceptions_finalize_declarations() is useless + if there were no exceptions. + Replace it to "no operation". + */ + return sphead->replace_instr_to_nop(thd, executable_section_ip - 1); + } + /* + Generate a jump from the end of the EXCEPTION code + to the executable section. + */ + return sphead->add_instr_jump(thd, spcont, executable_section_ip); +} + + +bool LEX::sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive) +{ + uint n; + uint ip= sphead->instructions(); + if ((n= spcont->diff_handlers(ctx, exclusive))) + { + sp_instr_hpop *hpop= new (thd->mem_root) sp_instr_hpop(ip++, spcont, n); + if (hpop == NULL || sphead->add_instr(hpop)) + return true; + } + if ((n= spcont->diff_cursors(ctx, exclusive))) + { + sp_instr_cpop *cpop= new (thd->mem_root) sp_instr_cpop(ip++, spcont, n); + if (cpop == NULL || sphead->add_instr(cpop)) + return true; + } + return false; +} + + +bool LEX::sp_leave_statement(THD *thd, const LEX_CSTRING *label_name) +{ + sp_label *lab= spcont->find_label(label_name); + if (!lab) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", label_name->str); + return true; + } + return sp_exit_block(thd, lab, NULL); +} + +bool LEX::sp_goto_statement(THD *thd, const LEX_CSTRING *label_name) +{ + sp_label *lab= spcont->find_goto_label(label_name); + if (!lab || lab->ip == 0) + { + sp_label *delayedlabel; + if (!lab) + { + // Label not found --> add forward jump to an unknown label + spcont->push_goto_label(thd, label_name, 0, sp_label::GOTO); + delayedlabel= spcont->last_goto_label(); + } + else + { + delayedlabel= lab; + } + return sphead->push_backpatch_goto(thd, spcont, delayedlabel); + } + else + { + // Label found (backward goto) + return sp_change_context(thd, lab->ctx, false) || + sphead->add_instr_jump(thd, spcont, lab->ip); /* Jump back */ + } + return false; +} + +bool LEX::sp_push_goto_label(THD *thd, const LEX_CSTRING *label_name) +{ + sp_label *lab= spcont->find_goto_label(label_name, false); + if (lab) + { + if (lab->ip != 0) + { + my_error(ER_SP_LABEL_REDEFINE, MYF(0), label_name->str); + return true; + } + lab->ip= sphead->instructions(); + + sp_label *beginblocklabel= spcont->find_label(&empty_clex_str); + sphead->backpatch_goto(thd, lab, beginblocklabel); + } + else + { + spcont->push_goto_label(thd, label_name, sphead->instructions()); + } + return false; +} + +bool LEX::sp_exit_block(THD *thd, sp_label *lab) +{ + /* + When jumping to a BEGIN-END block end, the target jump + points to the block hpop/cpop cleanup instructions, + so we should exclude the block context here. + When jumping to something else (i.e., SP_LAB_ITER), + there are no hpop/cpop at the jump destination, + so we should include the block context here for cleanup. + */ + bool exclusive= (lab->type == sp_label::BEGIN); + return sp_change_context(thd, lab->ctx, exclusive) || + sphead->add_instr_jump_forward_with_backpatch(thd, spcont, lab); +} + + +bool LEX::sp_exit_block(THD *thd, sp_label *lab, Item *when) +{ + if (!when) + return sp_exit_block(thd, lab); + + DBUG_ASSERT(sphead == thd->lex->sphead); + DBUG_ASSERT(spcont == thd->lex->spcont); + sp_instr_jump_if_not *i= new (thd->mem_root) + sp_instr_jump_if_not(sphead->instructions(), + spcont, + when, thd->lex); + if (i == NULL || + sphead->add_instr(i) || + sp_exit_block(thd, lab)) + return true; + i->backpatch(sphead->instructions(), spcont); + return false; +} + + +bool LEX::sp_exit_statement(THD *thd, Item *item) +{ + sp_label *lab= spcont->find_label_current_loop_start(); + if (!lab) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "EXIT", ""); + return true; + } + DBUG_ASSERT(lab->type == sp_label::ITERATION); + return sp_exit_block(thd, lab, item); +} + + +bool LEX::sp_exit_statement(THD *thd, const LEX_CSTRING *label_name, Item *item) +{ + sp_label *lab= spcont->find_label(label_name); + if (!lab || lab->type != sp_label::ITERATION) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "EXIT", label_name->str); + return true; + } + return sp_exit_block(thd, lab, item); +} + + +bool LEX::sp_iterate_statement(THD *thd, const LEX_CSTRING *label_name) +{ + sp_label *lab= spcont->find_label(label_name); + if (!lab || lab->type != sp_label::ITERATION) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", label_name->str); + return true; + } + return sp_continue_loop(thd, lab); +} + + +bool LEX::sp_continue_loop(THD *thd, sp_label *lab) +{ + if (lab->ctx->for_loop().m_index) + { + // We're in a FOR loop, increment the index variable before backward jump + sphead->reset_lex(thd); + DBUG_ASSERT(this != thd->lex); + if (thd->lex->sp_for_loop_increment(thd, lab->ctx->for_loop()) || + thd->lex->sphead->restore_lex(thd)) + return true; + } + return sp_change_context(thd, lab->ctx, false) || + sphead->add_instr_jump(thd, spcont, lab->ip); /* Jump back */ +} + + +bool LEX::sp_continue_loop(THD *thd, sp_label *lab, Item *when) +{ + if (!when) + return sp_continue_loop(thd, lab); + + DBUG_ASSERT(sphead == thd->lex->sphead); + DBUG_ASSERT(spcont == thd->lex->spcont); + sp_instr_jump_if_not *i= new (thd->mem_root) + sp_instr_jump_if_not(sphead->instructions(), + spcont, + when, thd->lex); + if (i == NULL || + sphead->add_instr(i) || + sp_continue_loop(thd, lab)) + return true; + i->backpatch(sphead->instructions(), spcont); + return false; +} + + +bool LEX::sp_continue_statement(THD *thd, Item *when) +{ + sp_label *lab= spcont->find_label_current_loop_start(); + if (!lab) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "CONTINUE", ""); + return true; + } + DBUG_ASSERT(lab->type == sp_label::ITERATION); + return sp_continue_loop(thd, lab, when); +} + + +bool LEX::sp_continue_statement(THD *thd, const LEX_CSTRING *label_name, + Item *when) +{ + sp_label *lab= spcont->find_label(label_name); + if (!lab || lab->type != sp_label::ITERATION) + { + my_error(ER_SP_LILABEL_MISMATCH, MYF(0), "CONTINUE", label_name->str); + return true; + } + return sp_continue_loop(thd, lab, when); +} + + +bool LEX::maybe_start_compound_statement(THD *thd) +{ + if (!sphead) + { + if (!make_sp_head(thd, NULL, &sp_handler_procedure)) + return true; + sphead->set_suid(SP_IS_NOT_SUID); + sphead->set_body_start(thd, thd->m_parser_state->m_lip.get_cpp_ptr()); + } + return false; +} + + +bool LEX::sp_push_loop_label(THD *thd, const LEX_CSTRING *label_name) +{ + sp_label *lab= spcont->find_label(label_name); + if (lab) + { + my_error(ER_SP_LABEL_REDEFINE, MYF(0), label_name->str); + return true; + } + spcont->push_label(thd, label_name, sphead->instructions(), + sp_label::ITERATION); + return false; +} + + +bool LEX::sp_push_loop_empty_label(THD *thd) +{ + if (maybe_start_compound_statement(thd)) + return true; + /* Unlabeled controls get an empty label. */ + spcont->push_label(thd, &empty_clex_str, sphead->instructions(), + sp_label::ITERATION); + return false; +} + + +bool LEX::sp_pop_loop_label(THD *thd, const LEX_CSTRING *label_name) +{ + sp_label *lab= spcont->pop_label(); + sphead->backpatch(lab); + if (label_name->str && + my_strcasecmp(system_charset_info, label_name->str, + lab->name.str) != 0) + { + my_error(ER_SP_LABEL_MISMATCH, MYF(0), label_name->str); + return true; + } + return false; +} + + +void LEX::sp_pop_loop_empty_label(THD *thd) +{ + sp_label *lab= spcont->pop_label(); + sphead->backpatch(lab); + DBUG_ASSERT(lab->name.length == 0); +} + + +bool LEX::sp_while_loop_expression(THD *thd, Item *expr) +{ + sp_instr_jump_if_not *i= new (thd->mem_root) + sp_instr_jump_if_not(sphead->instructions(), spcont, expr, this); + return i == NULL || + /* Jumping forward */ + sphead->push_backpatch(thd, i, spcont->last_label()) || + sphead->new_cont_backpatch(i) || + sphead->add_instr(i); +} + + +bool LEX::sp_while_loop_finalize(THD *thd) +{ + sp_label *lab= spcont->last_label(); /* Jumping back */ + sp_instr_jump *i= new (thd->mem_root) + sp_instr_jump(sphead->instructions(), spcont, lab->ip); + if (i == NULL || + sphead->add_instr(i)) + return true; + sphead->do_cont_backpatch(); + return false; +} + + +Item *LEX::create_and_link_Item_trigger_field(THD *thd, + const LEX_CSTRING *name, + bool new_row) +{ + Item_trigger_field *trg_fld; + + if (trg_chistics.event == TRG_EVENT_INSERT && !new_row) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT"); + return NULL; + } + + if (trg_chistics.event == TRG_EVENT_DELETE && new_row) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); + return NULL; + } + + DBUG_ASSERT(!new_row || + (trg_chistics.event == TRG_EVENT_INSERT || + trg_chistics.event == TRG_EVENT_UPDATE)); + + const bool tmp_read_only= + !(new_row && trg_chistics.action_time == TRG_ACTION_BEFORE); + trg_fld= new (thd->mem_root) + Item_trigger_field(thd, current_context(), + new_row ? + Item_trigger_field::NEW_ROW: + Item_trigger_field::OLD_ROW, + name, SELECT_ACL, tmp_read_only); + /* + Let us add this item to list of all Item_trigger_field objects + in trigger. + */ + if (trg_fld) + trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field); + + return trg_fld; +} + + +Item_param *LEX::add_placeholder(THD *thd, const LEX_CSTRING *name, + const char *start, const char *end) +{ + if (!parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + return NULL; + } + + Query_fragment pos(thd, sphead, start, end); + Item_param *item= new (thd->mem_root) Item_param(thd, name, + pos.pos(), pos.length()); + if (!item || param_list.push_back(item, thd->mem_root)) + { + my_error(ER_OUT_OF_RESOURCES, MYF(0)); + return NULL; + } + return item; +} + + +bool LEX::add_signal_statement(THD *thd, const sp_condition_value *v) +{ + Yacc_state *state= &thd->m_parser_state->m_yacc; + sql_command= SQLCOM_SIGNAL; + m_sql_cmd= new (thd->mem_root) Sql_cmd_signal(v, state->m_set_signal_info); + return m_sql_cmd == NULL; +} + + +bool LEX::add_resignal_statement(THD *thd, const sp_condition_value *v) +{ + Yacc_state *state= &thd->m_parser_state->m_yacc; + sql_command= SQLCOM_RESIGNAL; + m_sql_cmd= new (thd->mem_root) Sql_cmd_resignal(v, state->m_set_signal_info); + return m_sql_cmd == NULL; +} + + +Item *LEX::create_item_ident_nospvar(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b) +{ + DBUG_ASSERT(this == thd->lex); + /* + FIXME This will work ok in simple_ident_nospvar case because + we can't meet simple_ident_nospvar in trigger now. But it + should be changed in future. + */ + if (is_trigger_new_or_old_reference(a)) + { + bool new_row= (a->str[0]=='N' || a->str[0]=='n'); + + return create_and_link_Item_trigger_field(thd, b, new_row); + } + + if (current_select->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), a->str, thd->where); + return NULL; + } + if ((current_select->parsing_place != IN_HAVING) || + (current_select->get_in_sum_expr() > 0)) + return new (thd->mem_root) Item_field(thd, current_context(), + NullS, a->str, b); + return new (thd->mem_root) Item_ref(thd, current_context(), + NullS, a->str, b); +} + + +Item_splocal *LEX::create_item_spvar_row_field(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b, + sp_variable *spv, + const char *start, + const char *end) +{ + if (!parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + return NULL; + } + + Query_fragment pos(thd, sphead, start, end); + Item_splocal *item; + if (spv->field_def.is_table_rowtype_ref() || + spv->field_def.is_cursor_rowtype_ref()) + { + if (!(item= new (thd->mem_root) + Item_splocal_row_field_by_name(thd, a, b, spv->offset, + MYSQL_TYPE_NULL, + pos.pos(), pos.length()))) + return NULL; + } + else + { + uint row_field_offset; + const Spvar_definition *def; + if (!(def= spv->find_row_field(a, b, &row_field_offset))) + return NULL; + + if (!(item= new (thd->mem_root) + Item_splocal_row_field(thd, a, b, + spv->offset, row_field_offset, + def->real_field_type(), + pos.pos(), pos.length()))) + return NULL; + } +#ifndef DBUG_OFF + item->m_sp= sphead; +#endif + safe_to_cache_query=0; + return item; +} + + +my_var *LEX::create_outvar(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b) +{ + sp_variable *t; + if (!spcont || !(t= spcont->find_variable(a, false))) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str); + return NULL; + } + uint row_field_offset; + if (!t->find_row_field(a, b, &row_field_offset)) + return NULL; + return result ? + new (thd->mem_root) my_var_sp_row_field(a, b, t->offset, + row_field_offset, sphead) : + NULL; +} + + +Item *LEX::create_item_func_nextval(THD *thd, Table_ident *table_ident) +{ + TABLE_LIST *table; + if (!(table= current_select->add_table_to_list(thd, table_ident, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + return NULL; + return new (thd->mem_root) Item_func_nextval(thd, table); +} + + +Item *LEX::create_item_func_lastval(THD *thd, Table_ident *table_ident) +{ + TABLE_LIST *table; + if (!(table= current_select->add_table_to_list(thd, table_ident, 0, + TL_OPTION_SEQUENCE, + TL_READ, + MDL_SHARED_READ))) + return NULL; + return new (thd->mem_root) Item_func_lastval(thd, table); +} + + +Item *LEX::create_item_func_nextval(THD *thd, + const LEX_CSTRING *db, + const LEX_CSTRING *name) +{ + Table_ident *table_ident; + if (!(table_ident= new (thd->mem_root) Table_ident(thd, db, name, false))) + return NULL; + return create_item_func_nextval(thd, table_ident); +} + + +Item *LEX::create_item_func_lastval(THD *thd, + const LEX_CSTRING *db, + const LEX_CSTRING *name) +{ + Table_ident *table_ident; + if (!(table_ident= new (thd->mem_root) Table_ident(thd, db, name, false))) + return NULL; + return create_item_func_lastval(thd, table_ident); +} + + +Item *LEX::create_item_func_setval(THD *thd, Table_ident *table_ident, + longlong nextval, ulonglong round, + bool is_used) +{ + TABLE_LIST *table; + if (!(table= current_select->add_table_to_list(thd, table_ident, 0, + TL_OPTION_SEQUENCE, + TL_WRITE_ALLOW_WRITE, + MDL_SHARED_WRITE))) + return NULL; + return new (thd->mem_root) Item_func_setval(thd, table, nextval, round, + is_used); +} + + +Item *LEX::create_item_ident(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b, + const char *start, const char *end) +{ + sp_variable *spv; + if (spcont && (spv= spcont->find_variable(a, false)) && + (spv->field_def.is_row() || + spv->field_def.is_table_rowtype_ref() || + spv->field_def.is_cursor_rowtype_ref())) + return create_item_spvar_row_field(thd, a, b, spv, start, end); + + if ((thd->variables.sql_mode & MODE_ORACLE) && b->length == 7) + { + if (!my_strnncoll(system_charset_info, + (const uchar *) b->str, 7, + (const uchar *) "NEXTVAL", 7)) + return create_item_func_nextval(thd, &null_clex_str, a); + else if (!my_strnncoll(system_charset_info, + (const uchar *) b->str, 7, + (const uchar *) "CURRVAL", 7)) + return create_item_func_lastval(thd, &null_clex_str, a); + } + + return create_item_ident_nospvar(thd, a, b); +} + + +Item *LEX::create_item_ident(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b, + const LEX_CSTRING *c) +{ + const char *schema= (thd->client_capabilities & CLIENT_NO_SCHEMA ? + NullS : a->str); + + if ((thd->variables.sql_mode & MODE_ORACLE) && c->length == 7) + { + if (!my_strnncoll(system_charset_info, + (const uchar *) c->str, 7, + (const uchar *) "NEXTVAL", 7)) + return create_item_func_nextval(thd, a, b); + else if (!my_strnncoll(system_charset_info, + (const uchar *) c->str, 7, + (const uchar *) "CURRVAL", 7)) + return create_item_func_lastval(thd, a, b); + } + + if (current_select->no_table_names_allowed) + { + my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), b->str, thd->where); + return NULL; + } + if (current_select->parsing_place != IN_HAVING || + current_select->get_in_sum_expr() > 0) + return new (thd->mem_root) Item_field(thd, current_context(), + schema, b->str, c); + return new (thd->mem_root) Item_ref(thd, current_context(), + schema, b->str, c); +} + + +Item *LEX::create_item_limit(THD *thd, + const LEX_CSTRING *a, + const char *start, const char *end) +{ + sp_variable *spv; + if (!spcont || !(spv= spcont->find_variable(a, false))) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str); + return NULL; + } + + Query_fragment pos(thd, sphead, start, end); + Item_splocal *item; + if (!(item= new (thd->mem_root) Item_splocal(thd, a, + spv->offset, spv->sql_type(), + pos.pos(), pos.length()))) + return NULL; +#ifndef DBUG_OFF + item->m_sp= sphead; +#endif + safe_to_cache_query= 0; + + if (item->type() != Item::INT_ITEM) + { + my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0)); + return NULL; + } + item->limit_clause_param= true; + return item; +} + + +Item *LEX::create_item_limit(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b, + const char *start, const char *end) +{ + sp_variable *spv; + if (!spcont || !(spv= spcont->find_variable(a, false))) + { + my_error(ER_SP_UNDECLARED_VAR, MYF(0), a->str); + return NULL; + } + // Qualified %TYPE variables are not possible + DBUG_ASSERT(!spv->field_def.column_type_ref()); + Item_splocal *item; + if (!(item= create_item_spvar_row_field(thd, a, b, spv, start, end))) + return NULL; + if (item->type() != Item::INT_ITEM) + { + my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0)); + return NULL; + } + item->limit_clause_param= true; + return item; +} + + +bool LEX::set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val) +{ + Item_func_set_user_var *item; + set_var_user *var; + if (!(item= new (thd->mem_root) Item_func_set_user_var(thd, name, val)) || + !(var= new (thd->mem_root) set_var_user(item))) + return true; + var_list.push_back(var, thd->mem_root); + return false; +} + + +/* + Perform assignment for a trigger, a system variable, or an SP variable. + "variable" be previously set by init_internal_variable(variable, name). +*/ +bool LEX::set_variable(struct sys_var_with_base *variable, Item *item) +{ + if (variable->var == trg_new_row_fake_var) + { + /* We are in trigger and assigning value to field of new row */ + return set_trigger_new_row(&variable->base_name, item); + } + if (variable->var) + { + /* It is a system variable. */ + return set_system_variable(variable, option_type, item); + } + + /* + spcont and spv should not be NULL, as the variable + was previously checked by init_internal_variable(). + */ + DBUG_ASSERT(spcont); + sp_variable *spv= spcont->find_variable(&variable->base_name, false); + DBUG_ASSERT(spv); + /* It is a local variable. */ + return sphead->set_local_variable(thd, spcont, spv, item, this, true); +} + + +Item *LEX::create_item_ident_nosp(THD *thd, LEX_CSTRING *name) +{ + if (current_select->parsing_place != IN_HAVING || + current_select->get_in_sum_expr() > 0) + return new (thd->mem_root) Item_field(thd, current_context(), + NullS, NullS, name); + + return new (thd->mem_root) Item_ref(thd, current_context(), + NullS, NullS, name); +} + + +Item *LEX::create_item_ident_sp(THD *thd, LEX_CSTRING *name, + const char *start, + const char *end) +{ + sp_variable *spv; + DBUG_ASSERT(spcont); + DBUG_ASSERT(sphead); + if ((spv= spcont->find_variable(name, false))) + { + /* We're compiling a stored procedure and found a variable */ + if (!parsing_options.allows_variable) + { + my_error(ER_VIEW_SELECT_VARIABLE, MYF(0)); + return NULL; + } + + Query_fragment pos(thd, sphead, start, end); + Item_splocal *splocal= spv->field_def.is_column_type_ref() ? + new (thd->mem_root) Item_splocal_with_delayed_data_type(thd, name, + spv->offset, + pos.pos(), + pos.length()) : + spv->field_def.is_row() || spv->field_def.is_table_rowtype_ref() ? + new (thd->mem_root) Item_splocal_row(thd, name, spv->offset, + pos.pos(), pos.length()) : + new (thd->mem_root) Item_splocal(thd, name, + spv->offset, spv->sql_type(), + pos.pos(), pos.length()); + if (splocal == NULL) + return NULL; +#ifndef DBUG_OFF + splocal->m_sp= sphead; +#endif + safe_to_cache_query= 0; + return splocal; + } + + if (thd->variables.sql_mode & MODE_ORACLE) + { + if (!my_strcasecmp(system_charset_info, name->str, "SQLCODE")) + return new (thd->mem_root) Item_func_sqlcode(thd); + if (!my_strcasecmp(system_charset_info, name->str, "SQLERRM")) + return new (thd->mem_root) Item_func_sqlerrm(thd); + } + return create_item_ident_nosp(thd, name); +} + + +/** + Generate instructions for: + SET x.y= expr; +*/ +bool LEX::set_variable(const LEX_CSTRING *name1, + const LEX_CSTRING *name2, + Item *item) +{ + sp_variable *spv; + if (spcont && (spv= spcont->find_variable(name1, false))) + { + if (spv->field_def.is_table_rowtype_ref() || + spv->field_def.is_cursor_rowtype_ref()) + return sphead->set_local_variable_row_field_by_name(thd, spcont, + spv, name2, + item, this); + // A field of a ROW variable + uint row_field_offset; + return !spv->find_row_field(name1, name2, &row_field_offset) || + sphead->set_local_variable_row_field(thd, spcont, + spv, row_field_offset, + item, this); + } + + // A trigger field or a system variable + sys_var_with_base sysvar; + return init_internal_variable(&sysvar, name1, name2) || + set_variable(&sysvar, item); +} + + #ifdef MYSQL_SERVER uint binlog_unsafe_map[256]; @@ -4960,14 +6894,15 @@ void binlog_unsafe_map_init() st_select_lex and saves this fields. */ -void st_select_lex::collect_grouping_fields(THD *thd) +void st_select_lex::collect_grouping_fields(THD *thd, + ORDER *grouping_list) { grouping_tmp_fields.empty(); List_iterator<Item> li(join->fields_list); Item *item= li++; for (uint i= 0; i < master_unit()->derived->table->s->fields; i++, (item=li++)) { - for (ORDER *ord= join->group_list; ord; ord= ord->next) + for (ORDER *ord= grouping_list; ord; ord= ord->next) { if ((*ord->item)->eq((Item*)item, 0)) { @@ -5140,3 +7075,119 @@ Item *st_select_lex::build_cond_for_grouping_fields(THD *thd, Item *cond, } return 0; } + + +bool LEX::sp_add_cfetch(THD *thd, const LEX_CSTRING *name) +{ + uint offset; + sp_instr_cfetch *i; + + if (!spcont->find_cursor(name, &offset, false)) + { + my_error(ER_SP_CURSOR_MISMATCH, MYF(0), name->str); + return true; + } + i= new (thd->mem_root) + sp_instr_cfetch(sphead->instructions(), spcont, offset); + if (i == NULL || sphead->add_instr(i)) + return true; + return false; +} + + +bool LEX::create_or_alter_view_finalize(THD *thd, Table_ident *table_ident) +{ + sql_command= SQLCOM_CREATE_VIEW; + /* first table in list is target VIEW name */ + if (!select_lex.add_table_to_list(thd, table_ident, NULL, + TL_OPTION_UPDATING, + TL_IGNORE, + MDL_EXCLUSIVE)) + return true; + query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + return false; +} + + +bool LEX::add_alter_view(THD *thd, uint16 algorithm, + enum_view_suid suid, + Table_ident *table_ident) +{ + if (sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW"); + return true; + } + if (!(create_view= new (thd->mem_root) + Create_view_info(VIEW_ALTER, algorithm, suid))) + return true; + return create_or_alter_view_finalize(thd, table_ident); +} + + +bool LEX::add_create_view(THD *thd, DDL_options_st ddl, + uint16 algorithm, enum_view_suid suid, + Table_ident *table_ident) +{ + if (set_create_options_with_check(ddl)) + return true; + if (!(create_view= new (thd->mem_root) + Create_view_info(ddl.or_replace() ? + VIEW_CREATE_OR_REPLACE : + VIEW_CREATE_NEW, + algorithm, suid))) + return true; + return create_or_alter_view_finalize(thd, table_ident); +} + + +bool LEX::call_statement_start(THD *thd, sp_name *name) +{ + sql_command= SQLCOM_CALL; + spname= name; + value_list.empty(); + if (!(m_sql_cmd= new (thd->mem_root) Sql_cmd_call(name))) + return true; + sp_handler_procedure.add_used_routine(this, thd, name); + return false; +} + + +bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *name) +{ + sp_name *spname= make_sp_name(thd, name); + return !spname || call_statement_start(thd, spname); +} + + +bool LEX::call_statement_start(THD *thd, const LEX_CSTRING *name1, + const LEX_CSTRING *name2) +{ + sp_name *spname= make_sp_name(thd, name1, name2); + return !spname || call_statement_start(thd, spname); +} + + +bool LEX::add_grant_command(THD *thd, enum_sql_command sql_command_arg, + stored_procedure_type type_arg) +{ + if (columns.elements) + { + thd->parse_error(); + return true; + } + sql_command= sql_command_arg, + type= type_arg; + return false; +} + + +Item *LEX::make_item_func_replace(THD *thd, + Item *org, + Item *find, + Item *replace) +{ + return (thd->variables.sql_mode & MODE_ORACLE) ? + new (thd->mem_root) Item_func_replace_oracle(thd, org, find, replace) : + new (thd->mem_root) Item_func_replace(thd, org, find, replace); +} diff --git a/sql/sql_lex.h b/sql/sql_lex.h index a1ef42861b4..7be918f2c0b 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -30,9 +30,59 @@ #include "sql_alter.h" // Alter_info #include "sql_window.h" #include "sql_trigger.h" +#include "sp.h" // enum stored_procedure_type + /* YACC and LEX Definitions */ + +/** + A string with metadata. + We'll add more flags here eventually, to know if the string has, e.g.: + - multi-byte characters + - bad byte sequences + - backslash escapes: 'a\nb' + - separator escapes: 'a''b' + and reuse the original query fragments instead of making the string + copy too early, in Lex_input_stream::get_text(). + This will allow to avoid unnecessary copying, as well as + create more optimal Item types in sql_yacc.yy +*/ +struct Lex_string_with_metadata_st: public LEX_CSTRING +{ + bool m_is_8bit; // True if the string has 8bit characters +public: + void set_8bit(bool is_8bit) { m_is_8bit= is_8bit; } + // Get string repertoire by the 8-bit flag and the character set + uint repertoire(CHARSET_INFO *cs) const + { + return !m_is_8bit && my_charset_is_ascii_based(cs) ? + MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; + } + // Get string repertoire by the 8-bit flag, for ASCII-based character sets + uint repertoire() const + { + return !m_is_8bit ? MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; + } +}; + + +enum sub_select_type +{ + UNSPECIFIED_TYPE, + /* following 3 enums should be as they are*/ + UNION_TYPE, INTERSECT_TYPE, EXCEPT_TYPE, + GLOBAL_OPTIONS_TYPE, DERIVED_TABLE_TYPE, OLAP_TYPE +}; +enum unit_common_op {OP_MIX, OP_UNION, OP_INTERSECT, OP_EXCEPT}; + +enum enum_view_suid +{ + VIEW_SUID_INVOKER= 0, + VIEW_SUID_DEFINER= 1, + VIEW_SUID_DEFAULT= 2 +}; + /* These may not be declared yet */ class Table_ident; class sql_exchange; @@ -41,6 +91,8 @@ class sp_head; class sp_name; class sp_instr; class sp_pcontext; +class sp_variable; +class sp_assignment_lex; class st_alter_tablespace; class partition_info; class Event_parse_data; @@ -52,7 +104,7 @@ class Key_part_spec; class Item_window_func; struct sql_digest_state; class With_clause; - +class my_var; #define ALLOC_ROOT_SET 1024 @@ -109,7 +161,7 @@ void binlog_unsafe_map_init(); struct sys_var_with_base { sys_var *var; - LEX_STRING base_name; + LEX_CSTRING base_name; }; struct LEX_TYPE @@ -153,8 +205,10 @@ struct LEX_TYPE #ifdef MYSQL_SERVER -extern const LEX_STRING null_lex_str; -extern const LEX_STRING empty_lex_str; +extern const LEX_STRING empty_lex_str; +extern const LEX_CSTRING empty_clex_str; +extern const LEX_CSTRING star_clex_str; +extern const LEX_CSTRING param_clex_str; enum enum_sp_suid_behaviour { @@ -192,6 +246,27 @@ enum enum_view_create_mode VIEW_CREATE_OR_REPLACE // check only that there are not such table }; + +class Create_view_info: public Sql_alloc +{ +public: + LEX_CSTRING select; // The SELECT statement of CREATE VIEW + enum enum_view_create_mode mode; + uint16 algorithm; + uint8 check; + enum enum_view_suid suid; + Create_view_info(enum_view_create_mode mode_arg, + uint16 algorithm_arg, + enum_view_suid suid_arg) + :select(null_clex_str), + mode(mode_arg), + algorithm(algorithm_arg), + check(VIEW_CHECK_NONE), + suid(suid_arg) + { } +}; + + enum enum_drop_mode { DROP_DEFAULT, // mode is not specified @@ -204,6 +279,7 @@ enum enum_drop_mode #define TL_OPTION_FORCE_INDEX 2 #define TL_OPTION_IGNORE_LEAVES 4 #define TL_OPTION_ALIAS 8 +#define TL_OPTION_SEQUENCE 16 typedef List<Item> List_item; typedef Mem_root_array<ORDER*, true> Group_list_ptrs; @@ -212,11 +288,11 @@ typedef Mem_root_array<ORDER*, true> Group_list_ptrs; typedef struct st_lex_server_options { long port; - LEX_STRING server_name, host, db, username, password, scheme, socket, owner; - void reset(LEX_STRING name) + LEX_CSTRING server_name, host, db, username, password, scheme, socket, owner; + void reset(LEX_CSTRING name) { server_name= name; - host= db= username= password= scheme= socket= owner= null_lex_str; + host= db= username= password= scheme= socket= owner= null_clex_str; port= -1; } } LEX_SERVER_OPTIONS; @@ -234,13 +310,13 @@ struct LEX_MASTER_INFO DYNAMIC_ARRAY repl_ignore_server_ids; DYNAMIC_ARRAY repl_do_domain_ids; DYNAMIC_ARRAY repl_ignore_domain_ids; - char *host, *user, *password, *log_file_name; - char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher; - char *ssl_crl, *ssl_crlpath; - char *relay_log_name; - LEX_STRING connection_name; + const char *host, *user, *password, *log_file_name; + const char *ssl_key, *ssl_cert, *ssl_ca, *ssl_capath, *ssl_cipher; + const char *ssl_crl, *ssl_crlpath; + const char *relay_log_name; + LEX_CSTRING connection_name; /* Value in START SLAVE UNTIL master_gtid_pos=xxx */ - LEX_STRING gtid_pos_str; + LEX_CSTRING gtid_pos_str; ulonglong pos; ulong relay_log_pos; ulong server_id; @@ -286,7 +362,7 @@ struct LEX_MASTER_INFO ssl= ssl_verify_server_cert= heartbeat_opt= repl_ignore_server_ids_opt= repl_do_domain_ids_opt= repl_ignore_domain_ids_opt= LEX_MI_UNCHANGED; - gtid_pos_str= null_lex_str; + gtid_pos_str= null_clex_str; use_gtid_opt= LEX_GTID_UNCHANGED; sql_delay= -1; } @@ -297,12 +373,6 @@ typedef struct st_lex_reset_slave bool all; } LEX_RESET_SLAVE; -enum sub_select_type -{ - UNSPECIFIED_TYPE,UNION_TYPE, INTERSECT_TYPE, - EXCEPT_TYPE, GLOBAL_OPTIONS_TYPE, DERIVED_TABLE_TYPE, OLAP_TYPE -}; - enum olap_type { UNSPECIFIED_OLAP_TYPE, CUBE_TYPE, ROLLUP_TYPE @@ -340,10 +410,10 @@ public: The index name. Empty (str=NULL) name represents an empty list USE INDEX () clause */ - LEX_STRING key_name; + LEX_CSTRING key_name; Index_hint (enum index_hint_type type_arg, index_clause_map clause_arg, - char *str, uint length) : + const char *str, uint length) : type(type_arg), clause(clause_arg) { key_name.str= str; @@ -540,7 +610,7 @@ public: virtual List<Item>* get_item_list(); virtual ulong get_table_join_options(); virtual TABLE_LIST *add_table_to_list(THD *thd, Table_ident *table, - LEX_STRING *alias, + LEX_CSTRING *alias, ulong table_options, thr_lock_type flags= TL_UNLOCK, enum_mdl_type mdl_type= MDL_SHARED_READ, @@ -562,7 +632,7 @@ public: st_select_lex_node *insert_chain_before(st_select_lex_node **ptr_pos_to_insert, st_select_lex_node *end_chain_node); friend class st_select_lex_unit; - friend bool mysql_new_select(LEX *lex, bool move_down); + friend bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); friend bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, bool open_view_no_parse); friend bool mysql_derived_prepare(THD *thd, LEX *lex, @@ -582,7 +652,7 @@ typedef class st_select_lex_node SELECT_LEX_NODE; class THD; class select_result; class JOIN; -class select_union; +class select_unit; class Procedure; class Explain_query; @@ -594,10 +664,18 @@ bool print_explain_for_slow_log(LEX *lex, THD *thd, String *str); class st_select_lex_unit: public st_select_lex_node { protected: TABLE_LIST result_table_list; - select_union *union_result; + select_unit *union_result; ulonglong found_rows_for_union; bool saved_error; + bool prepare_join(THD *thd, SELECT_LEX *sl, select_result *result, + ulong additional_options, + bool is_union_select); + bool join_union_item_types(THD *thd, List<Item> &types, uint count); + bool join_union_type_handlers(THD *thd, + class Type_holder *holders, uint count); + bool join_union_type_attributes(THD *thd, + class Type_holder *holders, uint count); public: // Ensures that at least all members used during cleanup() are initialized. st_select_lex_unit() @@ -612,6 +690,7 @@ public: select_result *result; bool prepared, // prepare phase already performed for UNION (unit) optimized, // optimize phase already performed for UNION (unit) + optimized_2, executed, // already executed cleaned; @@ -628,6 +707,11 @@ public: */ List<Item> types; /** + There is INTERSECT and it is item used in creating temporary + table for it + */ + Item_int *intersect_mark; + /** Pointer to 'last' select, or pointer to select where we stored global parameters for union. @@ -718,19 +802,20 @@ public: select_result_interceptor *old_result); void set_limit(st_select_lex *values); void set_thd(THD *thd_arg) { thd= thd_arg; } - inline bool is_union (); + inline bool is_unit_op (); bool union_needs_tmp_table(); void set_unique_exclude(); - friend void lex_start(THD *thd); + friend struct LEX; friend int subselect_union_engine::exec(); List<Item> *get_column_types(bool for_cursor); - select_union *get_union_result() { return union_result; } + select_unit *get_union_result() { return union_result; } int save_union_explain(Explain_query *output); int save_union_explain_part2(Explain_query *output); + unit_common_op common_op(); }; typedef class st_select_lex_unit SELECT_LEX_UNIT; @@ -758,7 +843,7 @@ class st_select_lex: public st_select_lex_node { public: Name_resolution_context context; - char *db; + const char *db; Item *where, *having; /* WHERE & HAVING clauses */ Item *prep_where; /* saved WHERE clause for prepared statement processing */ Item *prep_having;/* saved HAVING clause for prepared statement processing */ @@ -871,7 +956,8 @@ public: int nest_level; /* nesting level of select */ Item_sum *inner_sum_func_list; /* list of sum func in nested selects */ uint with_wild; /* item list contain '*' */ - bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */ + bool braces; /* SELECT ... UNION (SELECT ... ) <- this braces */ + bool automatic_brackets; /* dummy select for INTERSECT precedence */ /* TRUE when having fix field called in processing of this SELECT */ bool having_fix_field; /* List of references to fields referenced from inner selects */ @@ -987,7 +1073,7 @@ public: bool add_order_to_list(THD *thd, Item *item, bool asc); bool add_gorder_to_list(THD *thd, Item *item, bool asc); TABLE_LIST* add_table_to_list(THD *thd, Table_ident *table, - LEX_STRING *alias, + LEX_CSTRING *alias, ulong table_options, thr_lock_type flags= TL_UNLOCK, enum_mdl_type mdl_type= MDL_SHARED_READ, @@ -1034,8 +1120,8 @@ public: */ ha_rows get_limit(); - friend void lex_start(THD *thd); - st_select_lex() : group_list_ptrs(NULL), braces(0), + friend struct LEX; + st_select_lex() : group_list_ptrs(NULL), braces(0), automatic_brackets(0), n_sum_items(0), n_child_sum_items(0) {} void make_empty_select() @@ -1067,7 +1153,7 @@ public: Add a index hint to the tagged list of hints. The type and clause of the hint will be the current ones (set by set_index_hint()) */ - bool add_index_hint (THD *thd, char *str, uint length); + bool add_index_hint (THD *thd, const char *str, uint length); /* make a list to hold index hints */ void alloc_index_hints (THD *thd); @@ -1080,7 +1166,7 @@ public: } void clear_index_hints(void) { index_hints= NULL; } - bool is_part_of_union() { return master_unit()->is_union(); } + bool is_part_of_union() { return master_unit()->is_unit_op(); } bool is_top_level_node() { return (select_number == 1) && !is_part_of_union(); @@ -1137,7 +1223,7 @@ public: With_element *find_table_def_in_with_clauses(TABLE_LIST *table); bool check_unrestricted_recursive(bool only_standard_compliant); bool check_subqueries_with_recursive_references(); - void collect_grouping_fields(THD *thd); + void collect_grouping_fields(THD *thd, ORDER *grouping_list); void check_cond_extraction_for_grouping_fields(Item *cond, TABLE_LIST *derived); Item *build_cond_for_grouping_fields(THD *thd, Item *cond, @@ -1145,11 +1231,11 @@ public: List<Window_spec> window_specs; void prepare_add_window_spec(THD *thd); - bool add_window_def(THD *thd, LEX_STRING *win_name, LEX_STRING *win_ref, + bool add_window_def(THD *thd, LEX_CSTRING *win_name, LEX_CSTRING *win_ref, SQL_I_List<ORDER> win_partition_list, SQL_I_List<ORDER> win_order_list, Window_frame *win_frame); - bool add_window_spec(THD *thd, LEX_STRING *win_ref, + bool add_window_spec(THD *thd, LEX_CSTRING *win_ref, SQL_I_List<ORDER> win_partition_list, SQL_I_List<ORDER> win_order_list, Window_frame *win_frame); @@ -1160,9 +1246,10 @@ public: } bool have_window_funcs() const { return (window_funcs.elements !=0); } + ORDER *find_common_window_func_partition_fields(THD *thd); bool cond_pushdown_is_allowed() const - { return !have_window_funcs() && !olap && !explicit_limit; } + { return !olap && !explicit_limit; } private: bool m_non_agg_field_used; @@ -1183,22 +1270,35 @@ public: }; typedef class st_select_lex SELECT_LEX; -inline bool st_select_lex_unit::is_union () -{ - return first_select()->next_select() && - first_select()->next_select()->linkage == UNION_TYPE; +inline bool st_select_lex_unit::is_unit_op () +{ + if (!first_select()->next_select()) + return 0; + + enum sub_select_type linkage= first_select()->next_select()->linkage; + return linkage == UNION_TYPE || linkage == INTERSECT_TYPE || + linkage == EXCEPT_TYPE; } struct st_sp_chistics { - LEX_STRING comment; + LEX_CSTRING comment; enum enum_sp_suid_behaviour suid; bool detistic; enum enum_sp_data_access daccess; + void init() { bzero(this, sizeof(*this)); } + void set(const st_sp_chistics &other) { *this= other; } + bool read_from_mysql_proc_row(THD *thd, TABLE *table); }; +class Sp_chistics: public st_sp_chistics +{ +public: + Sp_chistics() { init(); } +}; + struct st_trg_chistics: public st_trg_execution_order { @@ -2183,6 +2283,20 @@ public: return m_cpp_tok_end; } + /** + Get the token end position in the pre-processed buffer, + with trailing spaces removed. + */ + const char *get_cpp_tok_end_rtrim() + { + const char *p; + for (p= m_cpp_tok_end; + p > m_cpp_buf && my_isspace(system_charset_info, p[-1]); + p--) + { } + return p; + } + /** Get the current stream pointer, in the pre-processed buffer. */ const char *get_cpp_ptr() { @@ -2211,10 +2325,10 @@ public: void body_utf8_append(const char *ptr); void body_utf8_append(const char *ptr, const char *end_ptr); void body_utf8_append_ident(THD *thd, - const LEX_STRING *txt, + const LEX_CSTRING *txt, const char *end_ptr); void body_utf8_append_escape(THD *thd, - const LEX_STRING *txt, + const LEX_CSTRING *txt, CHARSET_INFO *txt_cs, const char *end_ptr, my_wc_t sep); @@ -2238,7 +2352,8 @@ public: /** LALR(2) resolution, value of the look ahead token.*/ LEX_YYSTYPE lookahead_yylval; - bool get_text(LEX_STRING *to, uint sep, int pre_skip, int post_skip); + bool get_text(Lex_string_with_metadata_st *to, + uint sep, int pre_skip, int post_skip); void add_digest_token(uint token, LEX_YYSTYPE yylval); @@ -2317,9 +2432,6 @@ public: */ const char *found_semicolon; - /** Token character bitmaps, to detect 7bit strings. */ - uchar tok_bitmap; - /** SQL_MODE = IGNORE_SPACE. */ bool ignore_space; @@ -2534,21 +2646,23 @@ struct LEX: public Query_tables_list */ With_clause **with_clauses_list_last_next; + Create_view_info *create_view; + /* Query Plan Footprint of a currently running select */ Explain_query *explain; // type information CHARSET_INFO *charset; - LEX_STRING name; - char *help_arg; - char *backup_dir; /* For RESTORE/BACKUP */ - char* to_log; /* For PURGE MASTER LOGS TO */ - char* x509_subject,*x509_issuer,*ssl_cipher; + LEX_CSTRING name; + const char *help_arg; + const char *backup_dir; /* For RESTORE/BACKUP */ + const char* to_log; /* For PURGE MASTER LOGS TO */ + const char* x509_subject,*x509_issuer,*ssl_cipher; String *wild; /* Wildcard in SHOW {something} LIKE 'wild'*/ sql_exchange *exchange; select_result *result; - LEX_STRING comment, ident; + LEX_CSTRING comment, ident; LEX_USER *grant_user; XID *xid; THD *thd; @@ -2557,11 +2671,6 @@ struct LEX: public Query_tables_list DYNAMIC_ARRAY plugins; plugin_ref plugins_static_buffer[INITIAL_LEX_PLUGIN_LIST_SIZE]; - bool text_string_is_7bit; - - /** SELECT of CREATE VIEW statement */ - LEX_STRING create_view_select; - uint number_of_selects; // valid only for view /** Start of 'ON table', in trigger statements. */ @@ -2578,6 +2687,7 @@ struct LEX: public Query_tables_list */ LEX_USER *definer; + Table_type table_type; /* Used for SHOW CREATE */ List<Key_part_spec> ref_list; List<LEX_USER> users_list; List<LEX_COLUMN> columns; @@ -2590,6 +2700,18 @@ private: Query_arena_memroot *arena_for_set_stmt; MEM_ROOT *mem_root_for_set_stmt; void parse_error(); + bool sp_block_finalize(THD *thd, const Lex_spblock_st spblock, + class sp_label **splabel); + bool sp_change_context(THD *thd, const sp_pcontext *ctx, bool exclusive); + bool sp_exit_block(THD *thd, sp_label *lab); + bool sp_exit_block(THD *thd, sp_label *lab, Item *when); + + bool sp_continue_loop(THD *thd, sp_label *lab); + bool sp_continue_loop(THD *thd, sp_label *lab, Item *when); + + bool sp_for_loop_condition(THD *thd, const Lex_for_loop_st &loop); + bool sp_for_loop_increment(THD *thd, const Lex_for_loop_st &loop); + public: inline bool is_arena_for_set_stmt() {return arena_for_set_stmt != 0;} bool set_arena_for_set_stmt(Query_arena *backup); @@ -2599,7 +2721,7 @@ public: List<Item_func_set_user_var> set_var_list; // in-query assignment list List<Item_param> param_list; List<LEX_STRING> view_list; // view list (list of field names in view) - List<LEX_STRING> with_column_list; // list of column names in with_list_element + List<LEX_CSTRING> with_column_list; // list of column names in with_list_element List<LEX_STRING> *column_list; // list of column names (in ANALYZE) List<LEX_STRING> *index_list; // list of index names (in ANALYZE) /* @@ -2627,7 +2749,7 @@ public: Key *last_key; LEX_MASTER_INFO mi; // used by CHANGE MASTER LEX_SERVER_OPTIONS server_options; - LEX_STRING relay_log_connection_name; + LEX_CSTRING relay_log_connection_name; USER_RESOURCES mqh; LEX_RESET_SLAVE reset_slave_info; ulonglong type; @@ -2675,7 +2797,6 @@ public: bool with_persistent_for_clause; // uses PERSISTENT FOR clause (in ANALYZE) }; enum enum_var_type option_type; - enum enum_view_create_mode create_view_mode; enum enum_drop_mode drop_mode; uint profile_query_id; @@ -2701,8 +2822,6 @@ public: DERIVED_SUBQUERY and DERIVED_VIEW). */ uint8 derived_tables; - uint16 create_view_algorithm; - uint8 create_view_check; uint8 context_analysis_only; bool local_file; bool check_exists; @@ -2721,7 +2840,7 @@ public: */ TABLE_LIST *create_last_non_select_table; /* Prepared statements SQL syntax:*/ - LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */ + LEX_CSTRING prepared_stmt_name; /* Statement name (in all queries) */ /* PREPARE or EXECUTE IMMEDIATE source expression */ Item *prepared_stmt_code; /* Names of user variables holding parameters (in EXECUTE) */ @@ -2738,16 +2857,11 @@ public: Event_parse_data *event_parse_data; - bool only_view; /* used for SHOW CREATE TABLE/VIEW */ /* field_list was created for view and should be removed before PS/SP rexecuton */ bool empty_field_list_on_rset; - /* - view created to be run from definer (standard behaviour) - */ - uint8 create_view_suid; /* Characterstics of trigger being created */ st_trg_chistics trg_chistics; /* @@ -2843,7 +2957,7 @@ public: SQL_I_List<ORDER> save_group_list; SQL_I_List<ORDER> save_order_list; - LEX_STRING *win_ref; + LEX_CSTRING *win_ref; Window_frame *win_frame; Window_frame_bound *frame_top_bound; Window_frame_bound *frame_bottom_bound; @@ -2870,6 +2984,16 @@ public: delete_dynamic(&plugins); } + virtual class Query_arena *query_arena() + { + DBUG_ASSERT(0); + return NULL; + } + + virtual const LEX_CSTRING *cursor_name() const { return &null_clex_str; } + + void start(THD *thd); + inline bool is_ps_or_view_context_analysis() { return (context_analysis_only & @@ -2955,7 +3079,7 @@ public: context_stack.pop(); } - bool copy_db_to(char **p_db, size_t *p_db_length) const; + bool copy_db_to(const char **p_db, size_t *p_db_length) const; Name_resolution_context *current_context() { @@ -3000,8 +3124,8 @@ public: bool is_analyze, bool *printed_anything); void restore_set_statement_var(); - void init_last_field(Column_definition *field, const char *name, CHARSET_INFO *cs); - void set_last_field_type(const Lex_field_type_st &type); + void init_last_field(Column_definition *field, const LEX_CSTRING *name, + const CHARSET_INFO *cs); bool set_bincmp(CHARSET_INFO *cs, bool bin); bool get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer); @@ -3016,6 +3140,394 @@ public: } return false; } + sp_variable *sp_param_init(LEX_CSTRING *name); + bool sp_param_fill_definition(sp_variable *spvar); + + int case_stmt_action_expr(Item* expr); + int case_stmt_action_when(Item *when, bool simple); + int case_stmt_action_then(); + bool add_select_to_union_list(bool is_union_distinct, + enum sub_select_type type, + bool is_top_level); + bool setup_select_in_parentheses(); + bool set_trigger_new_row(LEX_CSTRING *name, Item *val); + bool set_system_variable(struct sys_var_with_base *tmp, + enum enum_var_type var_type, Item *val); + bool set_user_variable(THD *thd, const LEX_CSTRING *name, Item *val); + void set_stmt_init(); + sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name); + sp_name *make_sp_name(THD *thd, const LEX_CSTRING *name1, + const LEX_CSTRING *name2); + sp_head *make_sp_head(THD *thd, const sp_name *name, const Sp_handler *sph); + sp_head *make_sp_head_no_recursive(THD *thd, const sp_name *name, + const Sp_handler *sph) + { + if (!sphead) + return make_sp_head(thd, name, sph); + my_error(ER_SP_NO_RECURSIVE_CREATE, MYF(0), sph->type_str()); + return NULL; + } + sp_head *make_sp_head_no_recursive(THD *thd, + DDL_options_st options, sp_name *name, + const Sp_handler *sph) + { + if (add_create_options_with_check(options)) + return NULL; + return make_sp_head_no_recursive(thd, name, sph); + } + bool sp_body_finalize_function(THD *); + bool sp_body_finalize_procedure(THD *); + bool call_statement_start(THD *thd, sp_name *name); + bool call_statement_start(THD *thd, const LEX_CSTRING *name); + bool call_statement_start(THD *thd, const LEX_CSTRING *name1, + const LEX_CSTRING *name2); + bool init_internal_variable(struct sys_var_with_base *variable, + const LEX_CSTRING *name); + bool init_internal_variable(struct sys_var_with_base *variable, + const LEX_CSTRING *dbname, + const LEX_CSTRING *name); + bool init_default_internal_variable(struct sys_var_with_base *variable, + LEX_CSTRING name); + bool set_variable(struct sys_var_with_base *variable, Item *item); + bool set_variable(const LEX_CSTRING *name1, const LEX_CSTRING *name2, + Item *item); + void sp_variable_declarations_init(THD *thd, int nvars); + bool sp_variable_declarations_finalize(THD *thd, int nvars, + const Column_definition *cdef, + Row_definition_list *row, + Item *def); + bool sp_variable_declarations_finalize(THD *thd, int nvars, + const Column_definition *cdef, + Item *def) + { + return sp_variable_declarations_finalize(thd, nvars, cdef, NULL, def); + } + bool sp_variable_declarations_row_finalize(THD *thd, int nvars, + Row_definition_list *row, + Item *def) + { + return sp_variable_declarations_finalize(thd, nvars, NULL, row, def); + } + bool sp_variable_declarations_with_ref_finalize(THD *thd, int nvars, + Qualified_column_ident *col, + Item *def); + bool sp_variable_declarations_rowtype_finalize(THD *thd, int nvars, + Qualified_column_ident *, + Item *def); + bool sp_handler_declaration_init(THD *thd, int type); + bool sp_handler_declaration_finalize(THD *thd, int type); + + bool sp_declare_cursor(THD *thd, const LEX_CSTRING *name, + class sp_lex_cursor *cursor_stmt, + sp_pcontext *param_ctx, bool add_cpush_instr); + + bool sp_open_cursor(THD *thd, const LEX_CSTRING *name, + List<sp_assignment_lex> *parameters); + Item_splocal *create_item_for_sp_var(LEX_CSTRING *name, sp_variable *spvar, + const char *start, const char *end); + + Item *create_item_ident_nosp(THD *thd, LEX_CSTRING *name); + Item *create_item_ident_sp(THD *thd, LEX_CSTRING *name, + const char *start, const char *end); + Item *create_item_ident(THD *thd, LEX_CSTRING *name, + const char *start, const char *end) + { + return sphead ? + create_item_ident_sp(thd, name, start, end) : + create_item_ident_nosp(thd, name); + } + + /* + Create an Item corresponding to a qualified name: a.b + when the parser is out of an SP context. + @param THD - THD, for mem_root + @param a - the first name + @param b - the second name + @retval - a pointer to a created item, or NULL on error. + + Possible Item types that can be created: + - Item_trigger_field + - Item_field + - Item_ref + */ + Item *create_item_ident_nospvar(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b); + /* + Create an Item corresponding to a ROW field valiable: var.field + @param THD - THD, for mem_root + @param var - the ROW variable name + @param field - the ROW variable field name + @param spvar - the variable that was previously found by name + using "var_name". + @param start - position in the query (for binary log) + @param end - end in the query (for binary log) + */ + Item_splocal *create_item_spvar_row_field(THD *thd, + const LEX_CSTRING *var, + const LEX_CSTRING *field, + sp_variable *spvar, + const char *start, + const char *end); + /* + Create an item from its qualified name. + Depending on context, it can be either a ROW variable field, + or trigger, table field, table field reference. + See comments to create_item_spvar_row_field() and + create_item_ident_nospvar(). + @param thd - THD, for mem_root + @param a - the first name + @param b - the second name + @param start - position in the query (for binary log) + @param end - end in the query (for binary log) + @retval - NULL on error, or a pointer to a new Item. + */ + Item *create_item_ident(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b, + const char *start, + const char *end); + /* + Create an item from its qualified name. + Depending on context, it can be a table field, a table field reference, + or a sequence NEXTVAL and CURRVAL. + @param thd - THD, for mem_root + @param a - the first name + @param b - the second name + @param c - the third name + @retval - NULL on error, or a pointer to a new Item. + */ + Item *create_item_ident(THD *thd, + const LEX_CSTRING *a, + const LEX_CSTRING *b, + const LEX_CSTRING *c); + + /* + Create an item for "NEXT VALUE FOR sequence_name" + */ + Item *create_item_func_nextval(THD *thd, Table_ident *ident); + Item *create_item_func_nextval(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *name); + /* + Create an item for "PREVIOUS VALUE FOR sequence_name" + */ + Item *create_item_func_lastval(THD *thd, Table_ident *ident); + Item *create_item_func_lastval(THD *thd, const LEX_CSTRING *db, + const LEX_CSTRING *name); + + /* + Create an item for "SETVAL(sequence_name, value [, is_used [, round]]) + */ + Item *create_item_func_setval(THD *thd, Table_ident *ident, longlong value, + ulonglong round, bool is_used); + + /* + Create an item for a name in LIMIT clause: LIMIT var + @param THD - THD, for mem_root + @param var_name - the variable name + @param start - position in the query (for binary log) + @param end - end in the query (for binary log) + @retval - a new Item corresponding to the SP variable, + or NULL on error + (non in SP, unknown variable, wrong data type). + */ + Item *create_item_limit(THD *thd, + const LEX_CSTRING *var_name, + const char *start, + const char *end); + + /* + Create an item for a qualified name in LIMIT clause: LIMIT var.field + @param THD - THD, for mem_root + @param var_name - the variable name + @param field_name - the variable field name + @param start - start in the query (for binary log) + @param end - end in the query (for binary log) + @retval - a new Item corresponding to the SP variable, + or NULL on error + (non in SP, unknown variable, unknown ROW field, + wrong data type). + */ + Item *create_item_limit(THD *thd, + const LEX_CSTRING *var_name, + const LEX_CSTRING *field_name, + const char *start, + const char *end); + + Item *make_item_func_replace(THD *thd, Item *org, Item *find, Item *replace); + + /* + Create a my_var instance for a ROW field variable that was used + as an OUT SP parameter: CALL p1(var.field); + @param THD - THD, for mem_root + @param var_name - the variable name + @param field_name - the variable field name + */ + my_var *create_outvar(THD *thd, + const LEX_CSTRING *var_name, + const LEX_CSTRING *field_name); + + bool is_trigger_new_or_old_reference(const LEX_CSTRING *name); + + Item *create_and_link_Item_trigger_field(THD *thd, const LEX_CSTRING *name, + bool new_row); + + void sp_block_init(THD *thd, const LEX_CSTRING *label); + void sp_block_init(THD *thd) + { + // Unlabeled blocks get an empty label + sp_block_init(thd, &empty_clex_str); + } + bool sp_block_finalize(THD *thd, const Lex_spblock_st spblock) + { + class sp_label *tmp; + return sp_block_finalize(thd, spblock, &tmp); + } + bool sp_block_finalize(THD *thd) + { + return sp_block_finalize(thd, Lex_spblock()); + } + bool sp_block_finalize(THD *thd, const Lex_spblock_st spblock, + const LEX_CSTRING *end_label); + bool sp_block_finalize(THD *thd, const LEX_CSTRING *end_label) + { + return sp_block_finalize(thd, Lex_spblock(), end_label); + } + bool sp_declarations_join(Lex_spblock_st *res, + const Lex_spblock_st b1, + const Lex_spblock_st b2) const + { + if ((b2.vars || b2.conds) && (b1.curs || b1.hndlrs)) + { + my_error(ER_SP_VARCOND_AFTER_CURSHNDLR, MYF(0)); + return true; + } + if (b2.curs && b1.hndlrs) + { + my_error(ER_SP_CURSOR_AFTER_HANDLER, MYF(0)); + return true; + } + res->join(b1, b2); + return false; + } + bool sp_block_with_exceptions_finalize_declarations(THD *thd); + bool sp_block_with_exceptions_finalize_executable_section(THD *thd, + uint executable_section_ip); + bool sp_block_with_exceptions_finalize_exceptions(THD *thd, + uint executable_section_ip, + uint exception_count); + bool sp_exit_statement(THD *thd, Item *when); + bool sp_exit_statement(THD *thd, const LEX_CSTRING *label_name, Item *item); + bool sp_leave_statement(THD *thd, const LEX_CSTRING *label_name); + bool sp_goto_statement(THD *thd, const LEX_CSTRING *label_name); + + bool sp_continue_statement(THD *thd, Item *when); + bool sp_continue_statement(THD *thd, const LEX_CSTRING *label_name, Item *when); + bool sp_iterate_statement(THD *thd, const LEX_CSTRING *label_name); + + bool maybe_start_compound_statement(THD *thd); + bool sp_push_loop_label(THD *thd, const LEX_CSTRING *label_name); + bool sp_push_loop_empty_label(THD *thd); + bool sp_pop_loop_label(THD *thd, const LEX_CSTRING *label_name); + void sp_pop_loop_empty_label(THD *thd); + bool sp_while_loop_expression(THD *thd, Item *expr); + bool sp_while_loop_finalize(THD *thd); + bool sp_push_goto_label(THD *thd, const LEX_CSTRING *label_name); + + Item_param *add_placeholder(THD *thd, const LEX_CSTRING *name, + const char *start, const char *end); + + /* Integer range FOR LOOP methods */ + sp_variable *sp_add_for_loop_variable(THD *thd, const LEX_CSTRING *name, + Item *value); + sp_variable *sp_add_for_loop_upper_bound(THD *thd, Item *value) + { + LEX_CSTRING name= { C_STRING_WITH_LEN("[upper_bound]") }; + return sp_add_for_loop_variable(thd, &name, value); + } + bool sp_for_loop_intrange_declarations(THD *thd, Lex_for_loop_st *loop, + const LEX_CSTRING *index, + const Lex_for_loop_bounds_st &bounds); + bool sp_for_loop_intrange_condition_test(THD *thd, const Lex_for_loop_st &loop); + bool sp_for_loop_intrange_finalize(THD *thd, const Lex_for_loop_st &loop); + + /* Cursor FOR LOOP methods */ + bool sp_for_loop_cursor_declarations(THD *thd, Lex_for_loop_st *loop, + const LEX_CSTRING *index, + const Lex_for_loop_bounds_st &bounds); + sp_variable *sp_add_for_loop_cursor_variable(THD *thd, + const LEX_CSTRING *name, + const class sp_pcursor *cur, + uint coffset, + sp_assignment_lex *param_lex, + Item_args *parameters); + bool sp_for_loop_cursor_condition_test(THD *thd, const Lex_for_loop_st &loop); + bool sp_for_loop_cursor_finalize(THD *thd, const Lex_for_loop_st &); + + /* Generic FOR LOOP methods*/ + + /* + Generate FOR loop declarations and + initialize "loop" from "index" and "bounds". + + @param [IN] thd - current THD, for mem_root and error reporting + @param [OUT] loop - the loop generated SP variables are stored here, + together with additional loop characteristics. + @param [IN] index - the loop index variable name + @param [IN] bounds - the loop bounds (in sp_assignment_lex format) + and additional loop characteristics, + as created by the sp_for_loop_bounds rule. + @retval true - on error + @retval false - on success + + This methods adds declarations: + - An explicit integer or cursor%ROWTYPE "index" variable + - An implicit integer upper bound variable, in case of integer range loops + - A CURSOR, in case of an implicit CURSOR loops + The generated variables are stored into "loop". + Additional loop characteristics are copied from "bounds" to "loop". + */ + bool sp_for_loop_declarations(THD *thd, Lex_for_loop_st *loop, + const LEX_CSTRING *index, + const Lex_for_loop_bounds_st &bounds) + { + return bounds.is_for_loop_cursor() ? + sp_for_loop_cursor_declarations(thd, loop, index, bounds) : + sp_for_loop_intrange_declarations(thd, loop, index, bounds); + } + + /* + Generate a conditional jump instruction to leave the loop, + using a proper condition depending on the loop type: + - Item_func_le -- integer range loops + - Item_func_ge -- integer range reverse loops + - Item_func_cursor_found -- cursor loops + */ + bool sp_for_loop_condition_test(THD *thd, const Lex_for_loop_st &loop) + { + return loop.is_for_loop_cursor() ? + sp_for_loop_cursor_condition_test(thd, loop) : + sp_for_loop_intrange_condition_test(thd, loop); + } + + /* + Generate "increment" instructions followed by a jump to the + condition test in the beginnig of the loop. + "Increment" depends on the loop type and can be: + - index:= index + 1; -- integer range loops + - index:= index - 1; -- integer range reverse loops + - FETCH cursor INTO index; -- cursor loops + */ + bool sp_for_loop_finalize(THD *thd, const Lex_for_loop_st &loop) + { + return loop.is_for_loop_cursor() ? + sp_for_loop_cursor_finalize(thd, loop) : + sp_for_loop_intrange_finalize(thd, loop); + } + /* End of FOR LOOP methods */ + + bool add_signal_statement(THD *thd, const class sp_condition_value *value); + bool add_resignal_statement(THD *thd, const class sp_condition_value *value); // Check if "KEY IF NOT EXISTS name" used outside of ALTER context bool check_add_key(DDL_options_st ddl) @@ -3028,7 +3540,7 @@ public: return false; } // Add a key as a part of CREATE TABLE or ALTER TABLE - bool add_key(Key::Keytype key_type, const LEX_STRING &key_name, + bool add_key(Key::Keytype key_type, const LEX_CSTRING *key_name, ha_key_alg algorithm, DDL_options_st ddl) { if (check_add_key(ddl) || @@ -3038,7 +3550,7 @@ public: return false; } // Add a key for a CREATE INDEX statement - bool add_create_index(Key::Keytype key_type, const LEX_STRING &key_name, + bool add_create_index(Key::Keytype key_type, const LEX_CSTRING *key_name, ha_key_alg algorithm, DDL_options_st ddl) { if (check_create_options(ddl) || @@ -3047,8 +3559,27 @@ public: alter_info.key_list.push_back(last_key); return false; } + bool add_create_index_prepare(Table_ident *table) + { + sql_command= SQLCOM_CREATE_INDEX; + if (!current_select->add_table_to_list(thd, table, NULL, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_UPGRADABLE)) + return true; + alter_info.reset(); + alter_info.flags= Alter_info::ALTER_ADD_INDEX; + option_list= NULL; + return false; + } + /* + Add an UNIQUE or PRIMARY key which is a part of a column definition: + CREATE TABLE t1 (a INT PRIMARY KEY); + */ + void add_key_to_list(LEX_CSTRING *field_name, + enum Key::Keytype type, bool check_exists); // Add a constraint as a part of CREATE TABLE or ALTER TABLE - bool add_constraint(LEX_STRING *name, Virtual_column_info *constr, + bool add_constraint(LEX_CSTRING *name, Virtual_column_info *constr, bool if_not_exists) { constr->name= *name; @@ -3079,11 +3610,18 @@ public: } return false; } + bool set_create_options_with_check(DDL_options_st options) + { + create_info.set(options); + return check_create_options(create_info); + } bool add_create_options_with_check(DDL_options_st options) { create_info.add(options); return check_create_options(create_info); } + bool sp_add_cfetch(THD *thd, const LEX_CSTRING *name); + bool set_command_with_check(enum_sql_command command, uint scope, DDL_options_st options) @@ -3104,6 +3642,19 @@ public: */ bool tmp_table() const { return create_info.tmp_table(); } bool if_exists() const { return create_info.if_exists(); } + + SELECT_LEX *exclude_last_select(); + bool add_unit_in_brackets(SELECT_LEX *nselect); + void check_automatic_up(enum sub_select_type type); + bool create_or_alter_view_finalize(THD *thd, Table_ident *table_ident); + bool add_alter_view(THD *thd, uint16 algorithm, enum_view_suid suid, + Table_ident *table_ident); + bool add_create_view(THD *thd, DDL_options_st ddl, + uint16 algorithm, enum_view_suid suid, + Table_ident *table_ident); + + bool add_grant_command(THD *thd, enum_sql_command sql_command_arg, + stored_procedure_type type_arg); }; @@ -3285,6 +3836,92 @@ struct st_lex_local: public LEX, public Sql_alloc { }; + +/** + An st_lex_local extension with automatic initialization for SP purposes. + Used to parse sub-expressions and SP sub-statements. + + This class is reused for: + 1. sp_head::reset_lex() based constructs + - SP variable assignments (e.g. SET x=10;) + - FOR loop conditions and index variable increments + - Cursor statements + - SP statements + - SP function RETURN statements + - CASE statements + - REPEAT..UNTIL expressions + - WHILE expressions + - EXIT..WHEN and CONTINUE..WHEN statements + 2. sp_assignment_lex based constructs: + - CURSOR parameter assignments +*/ +class sp_lex_local: public st_lex_local +{ +public: + sp_lex_local(THD *thd, const LEX *oldlex) + { + /* Reset most stuff. */ + start(thd); + /* Keep the parent SP stuff */ + sphead= oldlex->sphead; + spcont= oldlex->spcont; + /* Keep the parent trigger stuff too */ + trg_chistics= oldlex->trg_chistics; + trg_table_fields.empty(); + sp_lex_in_use= false; + } +}; + + +/** + An assignment specific LEX, which additionally has an Item (an expression) + and an associated with the Item free_list, which is usually freed + after the expression is calculated. + + Note, consider changing some of sp_lex_local to sp_assignment_lex, + as the latter allows to use a simpler grammar in sql_yacc.yy (IMO). + + If the expression is simple (e.g. does not have function calls), + then m_item and m_free_list point to the same Item. + + If the expressions is complex (e.g. have function calls), + then m_item points to the leftmost Item, while m_free_list points + to the rightmost item. + For example: + f1(COALESCE(f2(10), f2(20))) + - m_item points to Item_func_sp for f1 (the leftmost Item) + - m_free_list points to Item_int for 20 (the rightmost Item) + + Note, we could avoid storing m_item at all, as we can always reach + the leftmost item from the rightmost item by iterating through m_free_list. + But with a separate m_item the code should be faster. +*/ +class sp_assignment_lex: public sp_lex_local +{ + Item *m_item; // The expression + Item *m_free_list; // The associated free_list (sub-expressions) +public: + sp_assignment_lex(THD *thd, LEX *oldlex) + :sp_lex_local(thd, oldlex), + m_item(NULL), + m_free_list(NULL) + { } + void set_item_and_free_list(Item *item, Item *free_list) + { + m_item= item; + m_free_list= free_list; + } + Item *get_item() const + { + return m_item; + } + Item *get_free_list() const + { + return m_free_list; + } +}; + + extern void lex_init(void); extern void lex_free(void); extern void lex_start(THD *thd); @@ -3294,18 +3931,28 @@ extern void lex_end_stage2(LEX *lex); void end_lex_with_single_table(THD *thd, TABLE *table, LEX *old_lex); int init_lex_with_single_table(THD *thd, TABLE *table, LEX *lex); extern int MYSQLlex(union YYSTYPE *yylval, THD *thd); +extern int ORAlex(union YYSTYPE *yylval, THD *thd); -extern void trim_whitespace(CHARSET_INFO *cs, LEX_STRING *str, +extern void trim_whitespace(CHARSET_INFO *cs, LEX_CSTRING *str, uint *prefix_removed); -extern bool is_lex_native_function(const LEX_STRING *name); +extern bool is_lex_native_function(const LEX_CSTRING *name); +extern bool is_native_function(THD *thd, const LEX_CSTRING *name); +extern bool is_native_function_with_warn(THD *thd, const LEX_CSTRING *name); /** @} (End of group Semantic_Analysis) */ -void my_missing_function_error(const LEX_STRING &token, const char *name); +void my_missing_function_error(const LEX_CSTRING &token, const char *name); bool is_keyword(const char *name, uint len); +Virtual_column_info *add_virtual_expression(THD *thd, Item *expr); +Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, + Item *expr); + +void sp_create_assignment_lex(THD *thd, bool no_lookahead); +bool sp_create_assignment_instr(THD *thd, bool no_lookahead); + #endif /* MYSQL_SERVER */ #endif /* SQL_LEX_INCLUDED */ diff --git a/sql/sql_list.h b/sql/sql_list.h index 8f6f7337f1c..111826495f7 100644 --- a/sql/sql_list.h +++ b/sql/sql_list.h @@ -32,11 +32,13 @@ class Sql_alloc public: static void *operator new(size_t size) throw () { - return thd_alloc(thd_get_current_thd(), size); + DBUG_ASSERT(size < UINT_MAX32); + return thd_alloc(thd_get_current_thd(), (uint) size); } static void *operator new[](size_t size) throw () { - return thd_alloc(thd_get_current_thd(), size); + DBUG_ASSERT(size < UINT_MAX32); + return thd_alloc(thd_get_current_thd(), (uint) size); } static void *operator new[](size_t size, MEM_ROOT *mem_root) throw () { return alloc_root(mem_root, size); } @@ -334,16 +336,15 @@ public: friend class error_list; friend class error_list_iterator; -#ifndef DBUG_OFF /* - Debugging help: return N-th element in the list, or NULL if the list has + Return N-th element in the list, or NULL if the list has less than N elements. */ - void *elem(int n) + void *elem(uint n) { list_node *node= first; void *data= NULL; - for (int i=0; i <= n; i++) + for (uint i= 0; i <= n; i++) { if (node == &end_of_list) { @@ -355,7 +356,6 @@ public: } return data; } -#endif #ifdef LIST_EXTRA_DEBUG /* @@ -524,10 +524,10 @@ public: base_list(tmp, mem_root) {} inline bool push_back(T *a) { return base_list::push_back(a); } inline bool push_back(T *a, MEM_ROOT *mem_root) - { return base_list::push_back(a, mem_root); } + { return base_list::push_back((void*) a, mem_root); } inline bool push_front(T *a) { return base_list::push_front(a); } inline bool push_front(T *a, MEM_ROOT *mem_root) - { return base_list::push_front(a, mem_root); } + { return base_list::push_front((void*) a, mem_root); } inline T* head() {return (T*) base_list::head(); } inline T** head_ref() {return (T**) base_list::head_ref(); } inline T* pop() {return (T*) base_list::pop(); } @@ -546,9 +546,7 @@ public: } empty(); } -#ifndef DBUG_OFF - T *elem(int n) { return (T*)base_list::elem(n); } -#endif + T *elem(uint n) { return (T*) base_list::elem(n); } }; diff --git a/sql/sql_load.cc b/sql/sql_load.cc index ea05d42673f..4d5d1a4d943 100644 --- a/sql/sql_load.cc +++ b/sql/sql_load.cc @@ -283,13 +283,13 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, killed_state killed_status; bool is_concurrent; #endif - char *db = table_list->db; // This is never null + const char *db = table_list->db; // This is never null /* If path for file is not defined, we will use the current database. If this is not set, we will use the directory where the table to be loaded is located */ - char *tdb= thd->db ? thd->db : db; // Result is never null + const char *tdb= thd->db ? thd->db : db; // Result is never null ulong skip_lines= ex->skip_lines; bool transactional_table __attribute__((unused)); DBUG_ENTER("mysql_load"); @@ -812,7 +812,7 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, if (n++) query_str.append(", "); if (item->real_type() == Item::FIELD_ITEM) - append_identifier(thd, &query_str, item->name, strlen(item->name)); + append_identifier(thd, &query_str, item->name.str, item->name.length); else { /* Actually Item_user_var_as_out_param despite claiming STRING_ITEM. */ @@ -836,8 +836,8 @@ static bool write_execute_load_query_log_event(THD *thd, sql_exchange* ex, val= lv++; if (n++) query_str.append(STRING_WITH_LEN(", ")); - append_identifier(thd, &query_str, item->name, strlen(item->name)); - query_str.append(val->name); + append_identifier(thd, &query_str, item->name.str, item->name.length); + query_str.append(val->name.str, val->name.length); } } @@ -1087,7 +1087,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, } else if (!real_item) { - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str); DBUG_RETURN(1); } else @@ -1095,7 +1095,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, Field *field= real_item->field; if (field->reset()) { - my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name, + my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0), field->field_name.str, thd->get_stmt_da()->current_row_for_warning()); DBUG_RETURN(1); } @@ -1126,7 +1126,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, } else if (!real_item) { - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str); DBUG_RETURN(1); } else @@ -1166,7 +1166,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, } else if (!real_item) { - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str); DBUG_RETURN(1); } else @@ -1174,7 +1174,7 @@ read_sep_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, Field *field= real_item->field; if (field->reset()) { - my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name, + my_error(ER_WARN_NULL_TO_NOTNULL, MYF(0),field->field_name.str, thd->get_stmt_da()->current_row_for_warning()); DBUG_RETURN(1); } @@ -1296,7 +1296,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, xmlit.rewind(); tag= xmlit++; - while(tag && strcmp(tag->field.c_ptr(), item->name) != 0) + while(tag && strcmp(tag->field.c_ptr(), item->name.str) != 0) tag= xmlit++; Item_field *real_item= item->field_for_view_update(); @@ -1306,7 +1306,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ((Item_user_var_as_out_param *) item)->set_null_value(cs); else if (!real_item) { - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str); DBUG_RETURN(1); } else @@ -1336,7 +1336,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, tag->value.length(), cs); else if (!real_item) { - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str); DBUG_RETURN(1); } else @@ -1373,7 +1373,7 @@ read_xml_field(THD *thd, COPY_INFO &info, TABLE_LIST *table_list, ((Item_user_var_as_out_param *)item)->set_null_value(cs); else if (!real_item) { - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str); DBUG_RETURN(1); } else diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 21f0cfd0434..145a12afb52 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -101,6 +101,7 @@ #include "set_var.h" #include "log_slow.h" #include "sql_bootstrap.h" +#include "sql_sequence.h" #include "my_json_writer.h" @@ -123,14 +124,6 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, @{ */ -/* Used in error handling only */ -#define SP_COM_STRING(LP) \ - ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \ - (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \ - (LP)->sql_command == SQLCOM_SHOW_CREATE_FUNC || \ - (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \ - "FUNCTION" : "PROCEDURE") - static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables); static void sql_kill(THD *thd, longlong id, killed_state state, killed_type type); static void sql_kill_user(THD *thd, LEX_USER *user, killed_state state); @@ -448,14 +441,17 @@ static bool stmt_causes_implicit_commit(THD *thd, uint mask) switch (lex->sql_command) { case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: skip= (lex->tmp_table() || (thd->variables.option_bits & OPTION_GTID_BEGIN)); break; case SQLCOM_ALTER_TABLE: + case SQLCOM_ALTER_SEQUENCE: /* If ALTER TABLE of non-temporary table, do implicit commit */ skip= (lex->tmp_table()); break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: /* If CREATE TABLE of non-temporary table and the table is not part if a BEGIN GTID ... COMMIT group, do a implicit commit. @@ -542,19 +538,27 @@ void init_update_queries(void) */ sql_command_flags[SQLCOM_CREATE_TABLE]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS | - CF_CAN_GENERATE_ROW_EVENTS; + CF_CAN_GENERATE_ROW_EVENTS | + CF_SCHEMA_CHANGE; + sql_command_flags[SQLCOM_CREATE_SEQUENCE]= (CF_CHANGES_DATA | + CF_REEXECUTION_FRAGILE | + CF_AUTO_COMMIT_TRANS | + CF_SCHEMA_CHANGE); sql_command_flags[SQLCOM_CREATE_INDEX]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS; sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS | CF_REPORT_PROGRESS | CF_INSERTS_DATA; + sql_command_flags[SQLCOM_ALTER_SEQUENCE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | + CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE; sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE; + sql_command_flags[SQLCOM_DROP_SEQUENCE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_SCHEMA_CHANGE; sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE | CF_CAN_GENERATE_ROW_EVENTS | CF_REPORT_PROGRESS | CF_INSERTS_DATA; - sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; - sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; + sql_command_flags[SQLCOM_CREATE_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE; + sql_command_flags[SQLCOM_DROP_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS | CF_DB_CHANGE; sql_command_flags[SQLCOM_ALTER_DB_UPGRADE]= CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_ALTER_DB]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; sql_command_flags[SQLCOM_RENAME_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS; @@ -721,6 +725,7 @@ void init_update_queries(void) sql_command_flags[SQLCOM_TRUNCATE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; /* We don't want to replicate DROP for temp tables in row format */ sql_command_flags[SQLCOM_DROP_TABLE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; /* One can change replication mode with SET */ sql_command_flags[SQLCOM_SET_OPTION]|= CF_FORCE_ORIGINAL_BINLOG_FORMAT; @@ -763,6 +768,7 @@ void init_update_queries(void) */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_PREOPEN_TMP_TABLES; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_PREOPEN_TMP_TABLES; sql_command_flags[SQLCOM_TRUNCATE]|= CF_PREOPEN_TMP_TABLES; @@ -795,7 +801,9 @@ void init_update_queries(void) have to be closed before temporary tables are pre-opened. */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_CREATE_SEQUENCE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_HA_CLOSE; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_TRUNCATE]|= CF_HA_CLOSE; sql_command_flags[SQLCOM_REPAIR]|= CF_HA_CLOSE; @@ -813,8 +821,10 @@ void init_update_queries(void) even temporary table DDL should be disallowed. */ sql_command_flags[SQLCOM_CREATE_TABLE]|= CF_DISALLOW_IN_RO_TRANS; + sql_command_flags[SQLCOM_CREATE_SEQUENCE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_ALTER_TABLE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_DROP_TABLE]|= CF_DISALLOW_IN_RO_TRANS; + sql_command_flags[SQLCOM_DROP_SEQUENCE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_RENAME_TABLE]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_CREATE_INDEX]|= CF_DISALLOW_IN_RO_TRANS; sql_command_flags[SQLCOM_DROP_INDEX]|= CF_DISALLOW_IN_RO_TRANS; @@ -1406,7 +1416,7 @@ out: This is a helper function to mysql_execute_command. - @note SQLCOM_MULTI_UPDATE is an exception and delt with elsewhere. + @note SQLCOM_MULTI_UPDATE is an exception and dealt with elsewhere. @see mysql_execute_command @returns Status code @@ -1424,13 +1434,12 @@ static my_bool deny_updates_if_read_only_option(THD *thd, LEX *lex= thd->lex; - const my_bool user_is_super= - ((ulong)(thd->security_ctx->master_access & SUPER_ACL) == - (ulong)SUPER_ACL); - - if (user_is_super) + /* Super user is allowed to do changes */ + if (((ulong)(thd->security_ctx->master_access & SUPER_ACL) == + (ulong)SUPER_ACL)) DBUG_RETURN(FALSE); + /* Check if command doesn't update anything */ if (!(sql_command_flags[lex->sql_command] & CF_CHANGES_DATA)) DBUG_RETURN(FALSE); @@ -1438,25 +1447,16 @@ static my_bool deny_updates_if_read_only_option(THD *thd, if (lex->sql_command == SQLCOM_UPDATE_MULTI) DBUG_RETURN(FALSE); - /* - a table-to-be-created is not in the temp table list yet, - so CREATE TABLE needs a special treatment - */ - const bool update_real_tables= lex->sql_command == SQLCOM_CREATE_TABLE ? - !lex->tmp_table() : some_non_temp_table_to_be_updated(thd, all_tables); - - const bool create_or_drop_databases= - (lex->sql_command == SQLCOM_CREATE_DB) || - (lex->sql_command == SQLCOM_DROP_DB); + /* a table-to-create is not in the temp table list, needs a special check */ + if (lex->sql_command == SQLCOM_CREATE_TABLE) + DBUG_RETURN(!lex->tmp_table()); - if (update_real_tables || create_or_drop_databases) - { - /* - An attempt was made to modify one or more non-temporary tables. - */ - DBUG_RETURN(TRUE); - } + /* Check if we created or dropped databases */ + if ((sql_command_flags[lex->sql_command] & CF_DB_CHANGE)) + DBUG_RETURN(TRUE); + if (some_non_temp_table_to_be_updated(thd, all_tables)) + DBUG_RETURN(TRUE); /* Assuming that only temporary tables are modified. */ DBUG_RETURN(FALSE); @@ -1485,7 +1485,7 @@ uint maria_multi_check(THD *thd, char *packet, uint packet_length) size_t length_length= packet_start - packet; // length of command + 3 bytes where that length was stored DBUG_PRINT("info", ("sub-packet length: %ld + %d command: %x", - (ulong)subpacket_length, length_length, + (ulong)subpacket_length, (int) length_length, packet_start[3])); if (subpacket_length == 0 || @@ -1569,7 +1569,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (thd->wsrep_conflict_state == ABORTED && command != COM_STMT_CLOSE && command != COM_QUIT) { - my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction"); + my_message(ER_LOCK_DEADLOCK, "wsrep aborted transaction", MYF(0)); WSREP_DEBUG("Deadlock error for: %s", thd->query()); mysql_mutex_unlock(&thd->LOCK_wsrep_thd); thd->reset_killed(); @@ -1649,9 +1649,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, switch (command) { case COM_INIT_DB: { - LEX_STRING tmp; + LEX_CSTRING tmp; status_var_increment(thd->status_var.com_stat[SQLCOM_CHANGE_DB]); - if (thd->copy_with_error(system_charset_info, &tmp, + if (thd->copy_with_error(system_charset_info, (LEX_STRING*) &tmp, thd->charset(), packet, packet_length)) break; if (!mysql_change_db(thd, &tmp, FALSE)) @@ -1721,7 +1721,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (auth_rc) { /* Free user if allocated by acl_authenticate */ - my_free(thd->security_ctx->user); + my_free(const_cast<char*>(thd->security_ctx->user)); *thd->security_ctx= save_security_ctx; if (thd->user_connect) decrease_user_connections(thd->user_connect); @@ -1740,7 +1740,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, decrease_user_connections(save_user_connect); #endif /* NO_EMBEDDED_ACCESS_CHECKS */ my_free(save_db); - my_free(save_security_ctx.user); + my_free(const_cast<char*>(save_security_ctx.user)); } break; } @@ -1917,7 +1917,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* Locked closure of all tables */ TABLE_LIST table_list; LEX_STRING table_name; - LEX_STRING db; + LEX_CSTRING db; /* SHOW statements should not add the used tables to the list of tables used in a transaction. @@ -1964,7 +1964,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (lower_case_table_names) { table_name.length= my_casedn_str(files_charset_info, table_name.str); - db.length= my_casedn_str(files_charset_info, db.str); + db.length= my_casedn_str(files_charset_info, (char*) db.str); } table_list.init_one_table(db.str, db.length, table_name.str, table_name.length, table_name.str, TL_READ); @@ -2111,7 +2111,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, else #endif { - thd->lex->relay_log_connection_name= empty_lex_str; + thd->lex->relay_log_connection_name= empty_clex_str; if (reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, ¬_used)) break; } @@ -2292,7 +2292,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, if (server_command_flags[subcommand] & CF_NO_COM_MULTI) { - my_error(ER_BAD_COMMAND_IN_MULTI, MYF(0), command_name[subcommand]); + my_error(ER_BAD_COMMAND_IN_MULTI, MYF(0), + command_name[subcommand].str); goto com_multi_end; } @@ -2535,10 +2536,16 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, DBUG_RETURN(1); } schema_select_lex= new (thd->mem_root) SELECT_LEX(); - db.str= schema_select_lex->db= lex->select_lex.db; schema_select_lex->table_list.first= NULL; + if (lower_case_table_names == 1) + lex->select_lex.db= thd->strdup(lex->select_lex.db); + schema_select_lex->db= lex->select_lex.db; + /* + check_db_name() may change db.str if lower_case_table_names == 1, + but that's ok as the db is allocted above in this case. + */ + db.str= (char*) lex->select_lex.db; db.length= strlen(db.str); - if (check_db_name(&db)) { my_error(ER_WRONG_DB_NAME, MYF(0), db.str); @@ -2700,7 +2707,7 @@ bool sp_process_definer(THD *thd) DBUG_RETURN(TRUE); if (thd->slave_thread && lex->sphead) - lex->sphead->m_chistics->suid= SP_IS_NOT_SUID; + lex->sphead->set_suid(SP_IS_NOT_SUID); } else { @@ -2865,7 +2872,7 @@ static bool do_execute_sp(THD *thd, sp_head *sp) if (!(thd->client_capabilities & CLIENT_MULTI_RESULTS)) { /* The client does not support multiple result sets being sent back */ - my_error(ER_SP_BADSELECT, MYF(0), sp->m_qname.str); + my_error(ER_SP_BADSELECT, MYF(0), ErrConvDQName(sp).ptr()); return 1; } /* @@ -2904,6 +2911,237 @@ static bool do_execute_sp(THD *thd, sp_head *sp) } +static int mysql_create_routine(THD *thd, LEX *lex) +{ + DBUG_ASSERT(lex->sphead != 0); + DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */ + /* + Verify that the database name is allowed, optionally + lowercase it. + */ + if (check_db_name((LEX_STRING*) &lex->sphead->m_db)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str); + return true; + } + + if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, + NULL, NULL, 0, 0)) + return true; + + /* Checking the drop permissions if CREATE OR REPLACE is used */ + if (lex->create_info.or_replace()) + { + if (check_routine_access(thd, ALTER_PROC_ACL, lex->sphead->m_db.str, + lex->sphead->m_name.str, + Sp_handler::handler(lex->sql_command), 0)) + return true; + } + + const LEX_CSTRING *name= lex->sphead->name(); +#ifdef HAVE_DLOPEN + if (lex->sphead->m_handler->type() == TYPE_ENUM_FUNCTION) + { + udf_func *udf = find_udf(name->str, name->length); + + if (udf) + { + my_error(ER_UDF_EXISTS, MYF(0), name->str); + return true; + } + } +#endif + + if (sp_process_definer(thd)) + return true; + + WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) + if (!lex->sphead->m_handler->sp_create_routine(thd, lex->sphead)) + { +#ifndef NO_EMBEDDED_ACCESS_CHECKS + /* only add privileges if really neccessary */ + + Security_context security_context; + bool restore_backup_context= false; + Security_context *backup= NULL; + LEX_USER *definer= thd->lex->definer; + /* + We're going to issue an implicit GRANT statement so we close all + open tables. We have to keep metadata locks as this ensures that + this statement is atomic against concurent FLUSH TABLES WITH READ + LOCK. Deadlocks which can arise due to fact that this implicit + statement takes metadata locks should be detected by a deadlock + detector in MDL subsystem and reported as errors. + + No need to commit/rollback statement transaction, it's not started. + + TODO: Long-term we should either ensure that implicit GRANT statement + is written into binary log as a separate statement or make both + creation of routine and implicit GRANT parts of one fully atomic + statement. + */ + DBUG_ASSERT(thd->transaction.stmt.is_empty()); + close_thread_tables(thd); + /* + Check if the definer exists on slave, + then use definer privilege to insert routine privileges to mysql.procs_priv. + + For current user of SQL thread has GLOBAL_ACL privilege, + which doesn't any check routine privileges, + so no routine privilege record will insert into mysql.procs_priv. + */ + if (thd->slave_thread && is_acl_user(definer->host.str, definer->user.str)) + { + security_context.change_security_context(thd, + &thd->lex->definer->user, + &thd->lex->definer->host, + &thd->lex->sphead->m_db, + &backup); + restore_backup_context= true; + } + + if (sp_automatic_privileges && !opt_noacl && + check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS, + lex->sphead->m_db.str, name->str, + Sp_handler::handler(lex->sql_command), 1)) + { + if (sp_grant_privileges(thd, lex->sphead->m_db.str, name->str, + Sp_handler::handler(lex->sql_command))) + push_warning(thd, Sql_condition::WARN_LEVEL_WARN, + ER_PROC_AUTO_GRANT_FAIL, ER_THD(thd, ER_PROC_AUTO_GRANT_FAIL)); + thd->clear_error(); + } + + /* + Restore current user with GLOBAL_ACL privilege of SQL thread + */ + if (restore_backup_context) + { + DBUG_ASSERT(thd->slave_thread == 1); + thd->security_ctx->restore_security_context(thd, backup); + } + +#endif + return false; + } +#ifdef WITH_WSREP +error: /* Used by WSREP_TO_ISOLATION_BEGIN */ +#endif + return true; +} + + +/** + Prepare for CREATE DATABASE, ALTER DATABASE, DROP DATABASE. + + @param thd - current THD + @param want_access - access needed + @param dbname - the database name + + @retval false - Ok to proceed with CREATE/ALTER/DROP + @retval true - not OK to proceed (error, or filtered) + + Note, on slave this function returns true if the database + is in the ignore filter. The caller must distinguish this case + from other cases: bad database error, no access error. + This can be done by testing thd->is_error(). +*/ +static bool prepare_db_action(THD *thd, ulong want_access, LEX_CSTRING *dbname) +{ + if (check_db_name((LEX_STRING*)dbname)) + { + my_error(ER_WRONG_DB_NAME, MYF(0), dbname->str); + return true; + } + /* + If in a slave thread : + - CREATE DATABASE DB was certainly not preceded by USE DB. + - ALTER DATABASE DB may not be preceded by USE DB. + - DROP DATABASE DB may not be preceded by USE DB. + For that reason, db_ok() in sql/slave.cc did not check the + do_db/ignore_db. And as this query involves no tables, tables_ok() + was not called. So we have to check rules again here. + */ +#ifdef HAVE_REPLICATION + if (thd->slave_thread) + { + Rpl_filter *rpl_filter; + rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; + if (!rpl_filter->db_ok(dbname->str) || + !rpl_filter->db_ok_with_wild_table(dbname->str)) + { + my_message(ER_SLAVE_IGNORED_TABLE, + ER_THD(thd, ER_SLAVE_IGNORED_TABLE), MYF(0)); + return true; + } + } +#endif + return check_access(thd, want_access, dbname->str, NULL, NULL, 1, 0); +} + + +bool Sql_cmd_call::execute(THD *thd) +{ + TABLE_LIST *all_tables= thd->lex->query_tables; + sp_head *sp; + /* + This will cache all SP and SF and open and lock all tables + required for execution. + */ + if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, + UINT_MAX, FALSE) || + open_and_lock_tables(thd, all_tables, TRUE, 0)) + return true; + + /* + By this moment all needed SPs should be in cache so no need to look + into DB. + */ + if (!(sp= sp_handler_procedure.sp_find_routine(thd, m_name, true))) + { + /* + If the routine is not found, let's still check EXECUTE_ACL to decide + whether to return "Access denied" or "Routine does not exist". + */ + if (check_routine_access(thd, EXECUTE_ACL, m_name->m_db.str, + m_name->m_name.str, + &sp_handler_procedure, + false)) + return true; + /* + sp_find_routine can have issued an ER_SP_RECURSION_LIMIT error. + Send message ER_SP_DOES_NOT_EXIST only if procedure is not found in + cache. + */ + if (!sp_cache_lookup(&thd->sp_proc_cache, m_name)) + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE", + ErrConvDQName(m_name).ptr()); + return true; + } + else + { + if (sp->check_execute_access(thd)) + return true; + /* + Check that the stored procedure doesn't contain Dynamic SQL + and doesn't return result sets: such stored procedures can't + be called from a function or trigger. + */ + if (thd->in_sub_stmt) + { + const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ? + "trigger" : "function"); + if (sp->is_not_allowed_in_function(where)) + return true; + } + + if (do_execute_sp(thd, sp)) + return true; + } + return false; +} + + /** Execute command saved in thd and lex->sql_command. @@ -3080,7 +3318,8 @@ mysql_execute_command(THD *thd) */ if (!(lex->sql_command == SQLCOM_UPDATE_MULTI) && !(lex->sql_command == SQLCOM_SET_OPTION) && - !(lex->sql_command == SQLCOM_DROP_TABLE && + !((lex->sql_command == SQLCOM_DROP_TABLE || + lex->sql_command == SQLCOM_DROP_SEQUENCE) && lex->tmp_table() && lex->if_exists()) && all_tables_not_ok(thd, all_tables)) { @@ -3708,6 +3947,7 @@ mysql_execute_command(THD *thd) res = ha_show_status(thd, lex->create_info.db_type, HA_ENGINE_MUTEX); break; } + case SQLCOM_CREATE_SEQUENCE: case SQLCOM_CREATE_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); @@ -3965,6 +4205,7 @@ mysql_execute_command(THD *thd) /* Regular CREATE TABLE */ res= mysql_create_table(thd, create_table, &create_info, &alter_info); } + if (!res) { /* So that CREATE TEMPORARY TABLE gets to binlog at commit/rollback */ @@ -4172,9 +4413,9 @@ end_with_restore_list: */ DBUG_PRINT("debug", ("lex->only_view: %d, table: %s.%s", - lex->only_view, + lex->table_type == TABLE_TYPE_VIEW, first_table->db, first_table->table_name)); - if (lex->only_view) + if (lex->table_type == TABLE_TYPE_VIEW) { if (check_table_access(thd, SELECT_ACL, first_table, FALSE, 1, FALSE)) { @@ -4188,7 +4429,6 @@ end_with_restore_list: /* Ignore temporary tables if this is "SHOW CREATE VIEW" */ first_table->open_type= OT_BASE_ONLY; - } else { @@ -4694,6 +4934,7 @@ end_with_restore_list: } break; } + case SQLCOM_DROP_SEQUENCE: case SQLCOM_DROP_TABLE: { DBUG_ASSERT(first_table == all_tables && first_table != 0); @@ -4704,7 +4945,7 @@ end_with_restore_list: } else { - status_var_decrement(thd->status_var.com_stat[SQLCOM_DROP_TABLE]); + status_var_decrement(thd->status_var.com_stat[lex->sql_command]); status_var_increment(thd->status_var.com_drop_tmp_table); /* So that DROP TEMPORARY TABLE gets to binlog at commit/rollback */ @@ -4735,10 +4976,13 @@ end_with_restore_list: } /* DDL and binlog write order are protected by metadata locks. */ - res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table()); + res= mysql_rm_table(thd, first_table, lex->if_exists(), lex->tmp_table(), + lex->table_type == TABLE_TYPE_SEQUENCE); - /* when dropping temporary tables if @@session_track_state_change is ON then - send the boolean tracker in the OK packet */ + /* + When dropping temporary tables if @@session_track_state_change is ON + then send the boolean tracker in the OK packet + */ if(!res && (lex->create_info.options & HA_LEX_CREATE_TMP_TABLE)) { SESSION_TRACKER_CHANGED(thd, SESSION_STATE_CHANGE_TRACKER, NULL); @@ -4779,7 +5023,7 @@ end_with_restore_list: #endif case SQLCOM_CHANGE_DB: { - LEX_STRING db_str= { (char *) select_lex->db, strlen(select_lex->db) }; + LEX_CSTRING db_str= { (char *) select_lex->db, strlen(select_lex->db) }; if (!mysql_change_db(thd, &db_str, FALSE)) my_ok(thd); @@ -4903,33 +5147,9 @@ end_with_restore_list: break; case SQLCOM_CREATE_DB: { - if (check_db_name(&lex->name)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); - break; - } - /* - If in a slave thread : - CREATE DATABASE DB was certainly not preceded by USE DB. - For that reason, db_ok() in sql/slave.cc did not check the - do_db/ignore_db. And as this query involves no tables, tables_ok() - above was not called. So we have to check rules again here. - */ -#ifdef HAVE_REPLICATION - if (thd->slave_thread) - { - rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; - if (!rpl_filter->db_ok(lex->name.str) || - !rpl_filter->db_ok_with_wild_table(lex->name.str)) - { - my_message(ER_SLAVE_IGNORED_TABLE, ER_THD(thd, ER_SLAVE_IGNORED_TABLE), MYF(0)); - break; - } - } -#endif - if (check_access(thd, lex->create_info.or_replace() ? + if (prepare_db_action(thd, lex->create_info.or_replace() ? (CREATE_ACL | DROP_ACL) : CREATE_ACL, - lex->name.str, NULL, NULL, 1, 0)) + &lex->name)) break; WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL) res= mysql_create_db(thd, lex->name.str, @@ -4938,31 +5158,7 @@ end_with_restore_list: } case SQLCOM_DROP_DB: { - if (check_db_name(&lex->name)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->name.str); - break; - } - /* - If in a slave thread : - DROP DATABASE DB may not be preceded by USE DB. - For that reason, maybe db_ok() in sql/slave.cc did not check the - do_db/ignore_db. And as this query involves no tables, tables_ok() - above was not called. So we have to check rules again here. - */ -#ifdef HAVE_REPLICATION - if (thd->slave_thread) - { - rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; - if (!rpl_filter->db_ok(lex->name.str) || - !rpl_filter->db_ok_with_wild_table(lex->name.str)) - { - my_message(ER_SLAVE_IGNORED_TABLE, ER_THD(thd, ER_SLAVE_IGNORED_TABLE), MYF(0)); - break; - } - } -#endif - if (check_access(thd, DROP_ACL, lex->name.str, NULL, NULL, 1, 0)) + if (prepare_db_action(thd, DROP_ACL, &lex->name)) break; WSREP_TO_ISOLATION_BEGIN(lex->name.str, NULL, NULL) res= mysql_rm_db(thd, lex->name.str, lex->if_exists()); @@ -4970,7 +5166,7 @@ end_with_restore_list: } case SQLCOM_ALTER_DB_UPGRADE: { - LEX_STRING *db= & lex->name; + LEX_CSTRING *db= &lex->name; #ifdef HAVE_REPLICATION if (thd->slave_thread) { @@ -4984,7 +5180,7 @@ end_with_restore_list: } } #endif - if (check_db_name(db)) + if (check_db_name((LEX_STRING*) db)) { my_error(ER_WRONG_DB_NAME, MYF(0), db->str); break; @@ -5004,32 +5200,8 @@ end_with_restore_list: } case SQLCOM_ALTER_DB: { - LEX_STRING *db= &lex->name; - if (check_db_name(db)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), db->str); - break; - } - /* - If in a slave thread : - ALTER DATABASE DB may not be preceded by USE DB. - For that reason, maybe db_ok() in sql/slave.cc did not check the - do_db/ignore_db. And as this query involves no tables, tables_ok() - above was not called. So we have to check rules again here. - */ -#ifdef HAVE_REPLICATION - if (thd->slave_thread) - { - rpl_filter= thd->system_thread_info.rpl_sql_info->rpl_filter; - if (!rpl_filter->db_ok(db->str) || - !rpl_filter->db_ok_with_wild_table(db->str)) - { - my_message(ER_SLAVE_IGNORED_TABLE, ER_THD(thd, ER_SLAVE_IGNORED_TABLE), MYF(0)); - break; - } - } -#endif - if (check_access(thd, ALTER_ACL, db->str, NULL, NULL, 1, 0)) + LEX_CSTRING *db= &lex->name; + if (prepare_db_action(thd, ALTER_ACL, db)) break; WSREP_TO_ISOLATION_BEGIN(db->str, NULL, NULL) res= mysql_alter_db(thd, db->str, &lex->create_info); @@ -5038,17 +5210,17 @@ end_with_restore_list: case SQLCOM_SHOW_CREATE_DB: { char db_name_buff[NAME_LEN+1]; - LEX_STRING db_name; + LEX_CSTRING db_name; DBUG_EXECUTE_IF("4x_server_emul", my_error(ER_UNKNOWN_ERROR, MYF(0)); goto error;); db_name.str= db_name_buff; db_name.length= lex->name.length; - strmov(db_name.str, lex->name.str); + strmov(db_name_buff, lex->name.str); WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW); - if (check_db_name(&db_name)) + if (check_db_name((LEX_STRING*) &db_name)) { my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str); break; @@ -5102,13 +5274,13 @@ end_with_restore_list: break; case SQLCOM_SHOW_CREATE_EVENT: WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW); - res= Events::show_create_event(thd, lex->spname->m_db, - lex->spname->m_name); + res= Events::show_create_event(thd, &lex->spname->m_db, + &lex->spname->m_name); break; case SQLCOM_DROP_EVENT: WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) if (!(res= Events::drop_event(thd, - lex->spname->m_db, lex->spname->m_name, + &lex->spname->m_db, &lex->spname->m_name, lex->if_exists()))) my_ok(thd); break; @@ -5237,16 +5409,16 @@ end_with_restore_list: if (lex->type == TYPE_ENUM_PROCEDURE || lex->type == TYPE_ENUM_FUNCTION) { + const Sp_handler *sph= Sp_handler::handler((stored_procedure_type) + lex->type); uint grants= lex->all_privileges ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL) : lex->grant; - if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, - lex->type == TYPE_ENUM_PROCEDURE, 0)) + if (check_grant_routine(thd, grants | GRANT_ACL, all_tables, sph, 0)) goto error; /* Conditionally writes to binlog */ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) - res= mysql_routine_grant(thd, all_tables, - lex->type == TYPE_ENUM_PROCEDURE, + res= mysql_routine_grant(thd, all_tables, sph, lex->users_list, grants, lex->sql_command == SQLCOM_REVOKE, TRUE); if (!res) @@ -5635,172 +5807,11 @@ end_with_restore_list: case SQLCOM_CREATE_PROCEDURE: case SQLCOM_CREATE_SPFUNCTION: { - uint namelen; - char *name; - - DBUG_ASSERT(lex->sphead != 0); - DBUG_ASSERT(lex->sphead->m_db.str); /* Must be initialized in the parser */ - /* - Verify that the database name is allowed, optionally - lowercase it. - */ - if (check_db_name(&lex->sphead->m_db)) - { - my_error(ER_WRONG_DB_NAME, MYF(0), lex->sphead->m_db.str); - goto error; - } - - if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, - NULL, NULL, 0, 0)) - goto error; - - /* Checking the drop permissions if CREATE OR REPLACE is used */ - if (lex->create_info.or_replace()) - { - if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str, - lex->spname->m_name.str, - lex->sql_command == SQLCOM_CREATE_PROCEDURE, 0)) - goto error; - } - - name= lex->sphead->name(&namelen); -#ifdef HAVE_DLOPEN - if (lex->sphead->m_type == TYPE_ENUM_FUNCTION) - { - udf_func *udf = find_udf(name, namelen); - - if (udf) - { - my_error(ER_UDF_EXISTS, MYF(0), name); - goto error; - } - } -#endif - - if (sp_process_definer(thd)) - goto error; - - WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) - if (!sp_create_routine(thd, lex->sphead->m_type, lex->sphead)) - { -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* only add privileges if really neccessary */ - - Security_context security_context; - bool restore_backup_context= false; - Security_context *backup= NULL; - LEX_USER *definer= thd->lex->definer; - /* - We're going to issue an implicit GRANT statement so we close all - open tables. We have to keep metadata locks as this ensures that - this statement is atomic against concurent FLUSH TABLES WITH READ - LOCK. Deadlocks which can arise due to fact that this implicit - statement takes metadata locks should be detected by a deadlock - detector in MDL subsystem and reported as errors. - - No need to commit/rollback statement transaction, it's not started. - - TODO: Long-term we should either ensure that implicit GRANT statement - is written into binary log as a separate statement or make both - creation of routine and implicit GRANT parts of one fully atomic - statement. - */ - DBUG_ASSERT(thd->transaction.stmt.is_empty()); - close_thread_tables(thd); - /* - Check if the definer exists on slave, - then use definer privilege to insert routine privileges to mysql.procs_priv. - - For current user of SQL thread has GLOBAL_ACL privilege, - which doesn't any check routine privileges, - so no routine privilege record will insert into mysql.procs_priv. - */ - if (thd->slave_thread && is_acl_user(definer->host.str, definer->user.str)) - { - security_context.change_security_context(thd, - &thd->lex->definer->user, - &thd->lex->definer->host, - &thd->lex->sphead->m_db, - &backup); - restore_backup_context= true; - } - - if (sp_automatic_privileges && !opt_noacl && - check_routine_access(thd, DEFAULT_CREATE_PROC_ACLS, - lex->sphead->m_db.str, name, - lex->sql_command == SQLCOM_CREATE_PROCEDURE, 1)) - { - if (sp_grant_privileges(thd, lex->sphead->m_db.str, name, - lex->sql_command == SQLCOM_CREATE_PROCEDURE)) - push_warning(thd, Sql_condition::WARN_LEVEL_WARN, - ER_PROC_AUTO_GRANT_FAIL, ER_THD(thd, ER_PROC_AUTO_GRANT_FAIL)); - thd->clear_error(); - } - - /* - Restore current user with GLOBAL_ACL privilege of SQL thread - */ - if (restore_backup_context) - { - DBUG_ASSERT(thd->slave_thread == 1); - thd->security_ctx->restore_security_context(thd, backup); - } - -#endif - } - else + if (mysql_create_routine(thd, lex)) goto error; my_ok(thd); break; /* break super switch */ } /* end case group bracket */ - case SQLCOM_CALL: - { - sp_head *sp; - /* - This will cache all SP and SF and open and lock all tables - required for execution. - */ - if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, - UINT_MAX, FALSE) || - open_and_lock_tables(thd, all_tables, TRUE, 0)) - goto error; - - if (check_routine_access(thd, EXECUTE_ACL, lex->spname->m_db.str, - lex->spname->m_name.str, TRUE, FALSE)) - goto error; - - /* - By this moment all needed SPs should be in cache so no need to look - into DB. - */ - if (!(sp= sp_find_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname, - &thd->sp_proc_cache, TRUE))) - { - my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "PROCEDURE", - lex->spname->m_qname.str); - goto error; - } - else - { - /* - Check that the stored procedure doesn't contain Dynamic SQL - and doesn't return result sets: such stored procedures can't - be called from a function or trigger. - */ - if (thd->in_sub_stmt) - { - const char *where= (thd->in_sub_stmt & SUB_STMT_TRIGGER ? - "trigger" : "function"); - if (sp->is_not_allowed_in_function(where)) - goto error; - } - - if (do_execute_sp(thd, sp)) - goto error; - } - break; - } - case SQLCOM_COMPOUND: DBUG_ASSERT(all_tables == 0); DBUG_ASSERT(thd->in_sub_stmt == 0); @@ -5813,13 +5824,9 @@ end_with_restore_list: case SQLCOM_ALTER_FUNCTION: { int sp_result; - enum stored_procedure_type type; - type= (lex->sql_command == SQLCOM_ALTER_PROCEDURE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); if (check_routine_access(thd, ALTER_PROC_ACL, lex->spname->m_db.str, - lex->spname->m_name.str, - lex->sql_command == SQLCOM_ALTER_PROCEDURE, 0)) + lex->spname->m_name.str, sph, 0)) goto error; /* @@ -5829,7 +5836,7 @@ end_with_restore_list: already puts on CREATE FUNCTION. */ /* Conditionally writes to binlog */ - sp_result= sp_update_routine(thd, type, lex->spname, &lex->sp_chistics); + sp_result= sph->sp_update_routine(thd, lex->spname, &lex->sp_chistics); switch (sp_result) { case SP_OK: @@ -5837,11 +5844,11 @@ end_with_restore_list: break; case SP_KEY_NOT_FOUND: my_error(ER_SP_DOES_NOT_EXIST, MYF(0), - SP_COM_STRING(lex), lex->spname->m_qname.str); + sph->type_str(), ErrConvDQName(lex->spname).ptr()); goto error; default: my_error(ER_SP_CANT_ALTER, MYF(0), - SP_COM_STRING(lex), lex->spname->m_qname.str); + sph->type_str(), ErrConvDQName(lex->spname).ptr()); goto error; } break; @@ -5891,19 +5898,17 @@ end_with_restore_list: #endif int sp_result; - enum stored_procedure_type type; - type= (lex->sql_command == SQLCOM_DROP_PROCEDURE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - char *db= lex->spname->m_db.str; - char *name= lex->spname->m_name.str; + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); + const char *db= lex->spname->m_db.str; + const char *name= lex->spname->m_name.str; if (check_routine_access(thd, ALTER_PROC_ACL, db, name, - lex->sql_command == SQLCOM_DROP_PROCEDURE, 0)) + Sp_handler::handler(lex->sql_command), 0)) goto error; WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) /* Conditionally writes to binlog */ - sp_result= sp_drop_routine(thd, type, lex->spname); + sp_result= sph->sp_drop_routine(thd, lex->spname); #ifndef NO_EMBEDDED_ACCESS_CHECKS /* @@ -5927,7 +5932,7 @@ end_with_restore_list: if (sp_result != SP_KEY_NOT_FOUND && sp_automatic_privileges && !opt_noacl && sp_revoke_privileges(thd, db, name, - lex->sql_command == SQLCOM_DROP_PROCEDURE)) + Sp_handler::handler(lex->sql_command))) { push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_PROC_AUTO_REVOKE_FAIL, @@ -5948,33 +5953,29 @@ end_with_restore_list: res= write_bin_log(thd, TRUE, thd->query(), thd->query_length()); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_SP_DOES_NOT_EXIST, ER_THD(thd, ER_SP_DOES_NOT_EXIST), - SP_COM_STRING(lex), lex->spname->m_qname.str); + sph->type_str(), + ErrConvDQName(lex->spname).ptr()); if (!res) my_ok(thd); break; } my_error(ER_SP_DOES_NOT_EXIST, MYF(0), - SP_COM_STRING(lex), lex->spname->m_qname.str); + sph->type_str(), ErrConvDQName(lex->spname).ptr()); goto error; default: my_error(ER_SP_DROP_FAILED, MYF(0), - SP_COM_STRING(lex), lex->spname->m_qname.str); + sph->type_str(), ErrConvDQName(lex->spname).ptr()); goto error; } break; } case SQLCOM_SHOW_CREATE_PROC: - { - WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW); - if (sp_show_create_routine(thd, TYPE_ENUM_PROCEDURE, lex->spname)) - goto error; - break; - } case SQLCOM_SHOW_CREATE_FUNC: { WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW); - if (sp_show_create_routine(thd, TYPE_ENUM_FUNCTION, lex->spname)) - goto error; + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); + if (sph->sp_show_create_routine(thd, lex->spname)) + goto error; break; } case SQLCOM_SHOW_PROC_CODE: @@ -5982,17 +5983,15 @@ end_with_restore_list: { #ifndef DBUG_OFF sp_head *sp; - stored_procedure_type type= (lex->sql_command == SQLCOM_SHOW_PROC_CODE ? - TYPE_ENUM_PROCEDURE : TYPE_ENUM_FUNCTION); - + const Sp_handler *sph= Sp_handler::handler(lex->sql_command); WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_SHOW); - if (sp_cache_routine(thd, type, lex->spname, FALSE, &sp)) + if (sph->sp_cache_routine(thd, lex->spname, false, &sp)) goto error; if (!sp || sp->show_routine_code(thd)) { /* We don't distinguish between errors for now */ my_error(ER_SP_DOES_NOT_EXIST, MYF(0), - SP_COM_STRING(lex), lex->spname->m_name.str); + sph->type_str(), lex->spname->m_name.str); goto error; } break; @@ -6017,10 +6016,10 @@ end_with_restore_list: { /* Note: SQLCOM_CREATE_VIEW also handles 'ALTER VIEW' commands - as specified through the thd->lex->create_view_mode flag. + as specified through the thd->lex->create_view->mode flag. */ WSREP_TO_ISOLATION_BEGIN(WSREP_MYSQL_DB, NULL, NULL) - res= mysql_create_view(thd, first_table, thd->lex->create_view_mode); + res= mysql_create_view(thd, first_table, thd->lex->create_view->mode); break; } case SQLCOM_DROP_VIEW: @@ -6193,12 +6192,15 @@ end_with_restore_list: case SQLCOM_REPAIR: case SQLCOM_TRUNCATE: case SQLCOM_ALTER_TABLE: - thd->query_plan_flags|= QPLAN_ADMIN; DBUG_ASSERT(first_table == all_tables && first_table != 0); /* fall through */ + case SQLCOM_ALTER_SEQUENCE: + thd->query_plan_flags|= QPLAN_ADMIN; + /* fall through */ case SQLCOM_SIGNAL: case SQLCOM_RESIGNAL: case SQLCOM_GET_DIAGNOSTICS: + case SQLCOM_CALL: DBUG_ASSERT(lex->m_sql_cmd != NULL); res= lex->m_sql_cmd->execute(thd); break; @@ -7015,6 +7017,15 @@ check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, thd->security_ctx= sctx; + if (table_ref->sequence) + { + /* We want to have either SELECT or INSERT rights to sequences depending + on how they are accessed + */ + want_access= ((table_ref->lock_type == TL_WRITE_ALLOW_WRITE) ? + INSERT_ACL : SELECT_ACL); + } + if (check_access(thd, want_access, table_ref->get_db_name(), &table_ref->grant.privilege, &table_ref->grant.m_internal, @@ -7032,8 +7043,9 @@ deny: bool -check_routine_access(THD *thd, ulong want_access,char *db, char *name, - bool is_proc, bool no_errors) +check_routine_access(THD *thd, ulong want_access, const char *db, + const char *name, + const Sp_handler *sph, bool no_errors) { TABLE_LIST tables[1]; @@ -7062,7 +7074,7 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name, 0, no_errors)) return TRUE; - return check_grant_routine(thd, want_access, tables, is_proc, no_errors); + return check_grant_routine(thd, want_access, tables, sph, no_errors); } @@ -7080,7 +7092,7 @@ check_routine_access(THD *thd, ulong want_access,char *db, char *name, */ bool check_some_routine_access(THD *thd, const char *db, const char *name, - bool is_proc) + const Sp_handler *sph) { ulong save_priv; /* @@ -7097,7 +7109,7 @@ bool check_some_routine_access(THD *thd, const char *db, const char *name, if (!check_access(thd, SHOW_PROC_ACLS, db, &save_priv, NULL, 0, 1) || (save_priv & SHOW_PROC_ACLS)) return FALSE; - return check_routine_level_acl(thd, db, name, is_proc); + return check_routine_level_acl(thd, db, name, sph); } @@ -7205,8 +7217,8 @@ bool check_fk_parent_table_access(THD *thd, TABLE_LIST parent_table; bool is_qualified_table_name; Foreign_key *fk_key= (Foreign_key *)key; - LEX_STRING db_name; - LEX_STRING table_name= { fk_key->ref_table.str, + LEX_CSTRING db_name; + LEX_CSTRING table_name= { fk_key->ref_table.str, fk_key->ref_table.length }; const ulong privileges= (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | REFERENCES_ACL); @@ -7222,12 +7234,13 @@ bool check_fk_parent_table_access(THD *thd, if (fk_key->ref_db.str) { is_qualified_table_name= true; - db_name.str= (char *) thd->memdup(fk_key->ref_db.str, - fk_key->ref_db.length+1); + if (!(db_name.str= (char *) thd->memdup(fk_key->ref_db.str, + fk_key->ref_db.length+1))) + return true; db_name.length= fk_key->ref_db.length; // Check if database name is valid or not. - if (fk_key->ref_db.str && check_db_name(&db_name)) + if (check_db_name((LEX_STRING*) &db_name)) { my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str); return true; @@ -7237,11 +7250,14 @@ bool check_fk_parent_table_access(THD *thd, { if (!thd->db) { - db_name.str= (char *) thd->memdup(create_db, strlen(create_db)+1); + DBUG_ASSERT(create_db); db_name.length= strlen(create_db); + if (!(db_name.str= (char *) thd->memdup(create_db, + db_name.length+1))) + return true; is_qualified_table_name= true; - if(create_db && check_db_name(&db_name)) + if (check_db_name((LEX_STRING*) &db_name)) { my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str); return true; @@ -7259,10 +7275,11 @@ bool check_fk_parent_table_access(THD *thd, // if lower_case_table_names is set then convert tablename to lower case. if (lower_case_table_names) { - table_name.str= (char *) thd->memdup(fk_key->ref_table.str, - fk_key->ref_table.length+1); - table_name.length= my_casedn_str(files_charset_info, table_name.str); - db_name.length = my_casedn_str(files_charset_info, db_name.str); + char *name; + table_name.str= name= (char *) thd->memdup(fk_key->ref_table.str, + fk_key->ref_table.length+1); + table_name.length= my_casedn_str(files_charset_info, name); + db_name.length= my_casedn_str(files_charset_info, (char*) db_name.str); } parent_table.init_one_table(db_name.str, db_name.length, @@ -7521,18 +7538,21 @@ mysql_init_select(LEX *lex) */ bool -mysql_new_select(LEX *lex, bool move_down) +mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *select_lex) { - SELECT_LEX *select_lex; THD *thd= lex->thd; + bool new_select= select_lex == NULL; DBUG_ENTER("mysql_new_select"); - if (!(select_lex= new (thd->mem_root) SELECT_LEX())) - DBUG_RETURN(1); - select_lex->select_number= ++thd->select_number; - select_lex->parent_lex= lex; /* Used in init_query. */ - select_lex->init_query(); - select_lex->init_select(); + if (new_select) + { + if (!(select_lex= new (thd->mem_root) SELECT_LEX())) + DBUG_RETURN(1); + select_lex->select_number= ++thd->select_number; + select_lex->parent_lex= lex; /* Used in init_query. */ + select_lex->init_query(); + select_lex->init_select(); + } lex->nest_level++; if (lex->nest_level > (int) MAX_SELECT_NESTING) { @@ -7603,7 +7623,8 @@ mysql_new_select(LEX *lex, bool move_down) unit->first_select()->context.outer_context; } - select_lex->include_global((st_select_lex_node**)&lex->all_selects_list); + if (new_select) + select_lex->include_global((st_select_lex_node**)&lex->all_selects_list); lex->current_select= select_lex; /* in subquery is SELECT query and we allow resolution of names in SELECT @@ -7627,7 +7648,7 @@ void create_select_for_variable(const char *var_name) { THD *thd; LEX *lex; - LEX_STRING tmp; + LEX_CSTRING tmp; Item *var; char buff[MAX_SYS_VAR_LENGTH*2+4+8], *end; DBUG_ENTER("create_select_for_variable"); @@ -7636,13 +7657,13 @@ void create_select_for_variable(const char *var_name) lex= thd->lex; mysql_init_select(lex); lex->sql_command= SQLCOM_SELECT; - tmp.str= (char*) var_name; + tmp.str= var_name; tmp.length=strlen(var_name); /* We set the name of Item to @@session.var_name because that then is used as the column name in the output. */ - if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_str))) + if ((var= get_system_var(thd, OPT_SESSION, tmp, null_clex_str))) { end= strxmov(buff, "@@session.", var_name, NullS); var->set_name(thd, buff, (uint)(end-buff), system_charset_info); @@ -7735,7 +7756,7 @@ static void wsrep_mysql_parse(THD *thd, char *rawbuf, uint length, (longlong) thd->thread_id, is_autocommit, thd->wsrep_retry_counter, thd->variables.wsrep_retry_autocommit, thd->query()); - my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction"); + my_message(ER_LOCK_DEADLOCK, "wsrep aborted transaction", MYF(0)); thd->reset_killed(); thd->wsrep_conflict_state= NO_CONFLICT; if (thd->wsrep_conflict_state != REPLAYING) @@ -8009,7 +8030,7 @@ bool add_to_list(THD *thd, SQL_I_List<ORDER> &list, Item *item,bool asc) TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, Table_ident *table, - LEX_STRING *alias, + LEX_CSTRING *alias, ulong table_options, thr_lock_type lock_type, enum_mdl_type mdl_type, @@ -8019,7 +8040,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, { register TABLE_LIST *ptr; TABLE_LIST *UNINIT_VAR(previous_table_ref); /* The table preceding the current one. */ - char *alias_str; + const char *alias_str; LEX *lex= thd->lex; DBUG_ENTER("add_table_to_list"); @@ -8034,7 +8055,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } if (table->is_derived_table() == FALSE && table->db.str && - check_db_name(&table->db)) + check_db_name((LEX_STRING*) &table->db)) { my_error(ER_WRONG_DB_NAME, MYF(0), table->db.str); DBUG_RETURN(0); @@ -8069,9 +8090,10 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, if (lower_case_table_names) { if (table->table.length) - table->table.length= my_casedn_str(files_charset_info, table->table.str); + table->table.length= my_casedn_str(files_charset_info, + (char*) table->table.str); if (ptr->db_length && ptr->db != any_db) - ptr->db_length= my_casedn_str(files_charset_info, ptr->db); + ptr->db_length= my_casedn_str(files_charset_info, (char*) ptr->db); } ptr->table_name=table->table.str; @@ -8081,6 +8103,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, /* TODO: remove TL_OPTION_FORCE_INDEX as it looks like it's not used */ ptr->force_index= MY_TEST(table_options & TL_OPTION_FORCE_INDEX); ptr->ignore_leaves= MY_TEST(table_options & TL_OPTION_IGNORE_LEAVES); + ptr->sequence= MY_TEST(table_options & TL_OPTION_SEQUENCE); ptr->derived= table->sel; if (!ptr->derived && is_infoschema_db(ptr->db, ptr->db_length)) { @@ -8121,8 +8144,8 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, ptr->cacheable_table= !table->is_derived_table(); ptr->index_hints= index_hints_arg; ptr->option= option ? option->str : 0; - /* check that used name is unique */ - if (lock_type != TL_IGNORE) + /* check that used name is unique. Sequences are ignored */ + if (lock_type != TL_IGNORE && !ptr->sequence) { TABLE_LIST *first_table= table_list.first; if (lex->sql_command == SQLCOM_CREATE_VIEW) @@ -8132,7 +8155,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, tables=tables->next_local) { if (!my_strcasecmp(table_alias_charset, alias_str, tables->alias) && - !strcmp(ptr->db, tables->db)) + !strcmp(ptr->db, tables->db) && ! tables->sequence) { my_error(ER_NONUNIQ_TABLE, MYF(0), alias_str); /* purecov: tested */ DBUG_RETURN(0); /* purecov: tested */ @@ -8140,7 +8163,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, } } /* Store the table reference preceding the current one. */ - if (table_list.elements > 0) + if (table_list.elements > 0 && !ptr->sequence) { /* table_list.next points to the last inserted TABLE_LIST->next_local' @@ -8165,8 +8188,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd, Notice that as a side effect here we set the next_local field of the previous table reference to 'ptr'. Here we also add one element to the list 'table_list'. + We don't store sequences into the local list to hide them from INSERT + and SELECT. */ - table_list.link_in_list(ptr, &ptr->next_local); + if (!ptr->sequence) + table_list.link_in_list(ptr, &ptr->next_local); ptr->next_name_resolution_table= NULL; #ifdef WITH_PARTITION_STORAGE_ENGINE ptr->partition_names= partition_names; @@ -8410,8 +8436,8 @@ void st_select_lex::prepare_add_window_spec(THD *thd) } bool st_select_lex::add_window_def(THD *thd, - LEX_STRING *win_name, - LEX_STRING *win_ref, + LEX_CSTRING *win_name, + LEX_CSTRING *win_ref, SQL_I_List<ORDER> win_partition_list, SQL_I_List<ORDER> win_order_list, Window_frame *win_frame) @@ -8433,7 +8459,7 @@ bool st_select_lex::add_window_def(THD *thd, } bool st_select_lex::add_window_spec(THD *thd, - LEX_STRING *win_ref, + LEX_CSTRING *win_ref, SQL_I_List<ORDER> win_partition_list, SQL_I_List<ORDER> win_order_list, Window_frame *win_frame) @@ -8534,7 +8560,7 @@ bool st_select_lex_unit::add_fake_select_lex(THD *thd_arg) fake_select_lex->nest_level_base= first_select()->nest_level_base; fake_select_lex->nest_level=first_select()->nest_level; - if (!is_union()) + if (!is_unit_op()) { /* This works only for @@ -9562,7 +9588,7 @@ void get_default_definer(THD *thd, LEX_USER *definer, bool role) if (role) { definer->user.str= const_cast<char*>(sctx->priv_role); - definer->host= empty_lex_str; + definer->host= empty_clex_str; } else { @@ -9619,7 +9645,8 @@ LEX_USER *create_default_definer(THD *thd, bool role) - On error, return 0. */ -LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) +LEX_USER *create_definer(THD *thd, LEX_CSTRING *user_name, + LEX_CSTRING *host_name) { LEX_USER *definer; @@ -9653,7 +9680,7 @@ LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name) The function is not used in existing code but can be useful later? */ -bool check_string_byte_length(LEX_STRING *str, uint err_msg, +bool check_string_byte_length(const LEX_CSTRING *str, uint err_msg, uint max_byte_length) { if (str->length <= max_byte_length) @@ -9683,7 +9710,7 @@ bool check_string_byte_length(LEX_STRING *str, uint err_msg, */ -bool check_string_char_length(LEX_STRING *str, uint err_msg, +bool check_string_char_length(const LEX_CSTRING *str, uint err_msg, uint max_char_length, CHARSET_INFO *cs, bool no_error) { @@ -9702,7 +9729,7 @@ bool check_string_char_length(LEX_STRING *str, uint err_msg, } -bool check_ident_length(LEX_STRING *ident) +bool check_ident_length(const LEX_CSTRING *ident) { if (check_string_char_length(ident, 0, NAME_CHAR_LEN, system_charset_info, 1)) { @@ -9812,7 +9839,7 @@ int error_if_data_home_dir(const char *path, const char *what) has invalid symbols */ -bool check_host_name(LEX_STRING *str) +bool check_host_name(LEX_CSTRING *str) { const char *name= str->str; const char *end= str->str + str->length; @@ -9835,6 +9862,7 @@ bool check_host_name(LEX_STRING *str) extern int MYSQLparse(THD *thd); // from sql_yacc.cc +extern int ORAparse(THD *thd); // from sql_yacc_ora.cc /** @@ -9894,7 +9922,10 @@ bool parse_sql(THD *thd, Parser_state *parser_state, /* Parse the query. */ - bool mysql_parse_status= MYSQLparse(thd) != 0; + bool mysql_parse_status= + ((thd->variables.sql_mode & MODE_ORACLE) ? + ORAparse(thd) : + MYSQLparse(thd)) != 0; /* Check that if MYSQLparse() failed either thd->is_error() is set, or an diff --git a/sql/sql_parse.h b/sql/sql_parse.h index eedb74df959..b8d7bb46e08 100644 --- a/sql/sql_parse.h +++ b/sql/sql_parse.h @@ -70,19 +70,19 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, enum enum_schema_tables schema_table_idx); void get_default_definer(THD *thd, LEX_USER *definer, bool role); LEX_USER *create_default_definer(THD *thd, bool role); -LEX_USER *create_definer(THD *thd, LEX_STRING *user_name, LEX_STRING *host_name); +LEX_USER *create_definer(THD *thd, LEX_CSTRING *user_name, LEX_CSTRING *host_name); LEX_USER *get_current_user(THD *thd, LEX_USER *user, bool lock=true); bool sp_process_definer(THD *thd); -bool check_string_byte_length(LEX_STRING *str, uint err_msg, +bool check_string_byte_length(const LEX_CSTRING *str, uint err_msg, uint max_byte_length); -bool check_string_char_length(LEX_STRING *str, uint err_msg, +bool check_string_char_length(const LEX_CSTRING *str, uint err_msg, uint max_char_length, CHARSET_INFO *cs, bool no_error); -bool check_ident_length(LEX_STRING *ident); +bool check_ident_length(const LEX_CSTRING *ident); CHARSET_INFO* merge_charset_and_collation(CHARSET_INFO *cs, CHARSET_INFO *cl); CHARSET_INFO *find_bin_collation(CHARSET_INFO *cs); -bool check_host_name(LEX_STRING *str); -bool check_identifier_name(LEX_STRING *str, uint max_char_length, +bool check_host_name(LEX_CSTRING *str); +bool check_identifier_name(LEX_CSTRING *str, uint max_char_length, uint err_code, const char *param_for_err_msg); bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length); bool sqlcom_can_generate_row_events(const THD *thd); @@ -93,7 +93,7 @@ void mysql_init_select(LEX *lex); void mysql_parse(THD *thd, char *rawbuf, uint length, Parser_state *parser_state, bool is_com_multi, bool is_next_command); -bool mysql_new_select(LEX *lex, bool move_down); +bool mysql_new_select(LEX *lex, bool move_down, SELECT_LEX *sel); void create_select_for_variable(const char *var_name); void create_table_set_open_action_and_adjust_tables(LEX *lex); void mysql_init_multi_delete(LEX *lex); @@ -136,12 +136,12 @@ extern const LEX_STRING command_name[]; extern uint server_command_flags[]; /* Inline functions */ -inline bool check_identifier_name(LEX_STRING *str, uint err_code) +inline bool check_identifier_name(LEX_CSTRING *str, uint err_code) { return check_identifier_name(str, NAME_CHAR_LEN, err_code, ""); } -inline bool check_identifier_name(LEX_STRING *str) +inline bool check_identifier_name(LEX_CSTRING *str) { return check_identifier_name(str, NAME_CHAR_LEN, 0, ""); } @@ -150,10 +150,12 @@ inline bool check_identifier_name(LEX_STRING *str) bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables); bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, bool no_errors); -bool check_routine_access(THD *thd,ulong want_access,char *db,char *name, - bool is_proc, bool no_errors); +bool check_routine_access(THD *thd,ulong want_access,const char *db, + const char *name, + const Sp_handler *sph, bool no_errors); bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); -bool check_some_routine_access(THD *thd, const char *db, const char *name, bool is_proc); +bool check_some_routine_access(THD *thd, const char *db, const char *name, + const Sp_handler *sph); bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, bool any_combination_of_privileges_will_do, uint number, @@ -164,8 +166,9 @@ inline bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables inline bool check_single_table_access(THD *thd, ulong privilege, TABLE_LIST *tables, bool no_errors) { return false; } -inline bool check_routine_access(THD *thd,ulong want_access,char *db, - char *name, bool is_proc, bool no_errors) +inline bool check_routine_access(THD *thd,ulong want_access, const char *db, + const char *name, + const Sp_handler *sph, bool no_errors) { return false; } inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) { @@ -173,7 +176,8 @@ inline bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table) return false; } inline bool check_some_routine_access(THD *thd, const char *db, - const char *name, bool is_proc) + const char *name, + const Sp_handler *sph) { return false; } inline bool check_table_access(THD *thd, ulong requirements,TABLE_LIST *tables, diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 6e4da0fbbf2..c9e78964e2a 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -177,15 +177,15 @@ Item* convert_charset_partition_constant(Item *item, CHARSET_INFO *cs) @retval false String not found */ -static bool is_name_in_list(char *name, List<char> list_names) +static bool is_name_in_list(const char *name, List<const char> list_names) { - List_iterator<char> names_it(list_names); + List_iterator<const char> names_it(list_names); uint num_names= list_names.elements; uint i= 0; do { - char *list_name= names_it++; + const char *list_name= names_it++; if (!(my_strcasecmp(system_charset_info, name, list_name))) return TRUE; } while (++i < num_names); @@ -261,7 +261,7 @@ bool partition_default_handling(THD *thd, TABLE *table, partition_info *part_inf > 0 Error code */ -int get_parts_for_update(const uchar *old_data, uchar *new_data, +int get_parts_for_update(const uchar *old_data, const uchar *new_data, const uchar *rec0, partition_info *part_info, uint32 *old_part_id, uint32 *new_part_id, longlong *new_func_value) @@ -463,8 +463,8 @@ static bool set_up_field_array(THD *thd, TABLE *table, { if (!is_sub_part && part_info->column_list) { - List_iterator<char> it(part_info->part_field_list); - char *field_name; + List_iterator<const char> it(part_info->part_field_list); + const char *field_name; DBUG_ASSERT(num_fields == part_info->part_field_list.elements); inx= 0; @@ -473,7 +473,7 @@ static bool set_up_field_array(THD *thd, TABLE *table, field_name= it++; if (!my_strcasecmp(system_charset_info, field_name, - field->field_name)) + field->field_name.str)) break; } while (++inx < num_fields); if (inx == num_fields) @@ -758,14 +758,14 @@ static void clear_field_flag(TABLE *table) */ -static bool handle_list_of_fields(THD *thd, List_iterator<char> it, +static bool handle_list_of_fields(THD *thd, List_iterator<const char> it, TABLE *table, partition_info *part_info, bool is_sub_part) { Field *field; bool result; - char *field_name; + const char *field_name; bool is_list_empty= TRUE; DBUG_ENTER("handle_list_of_fields"); @@ -1590,7 +1590,7 @@ bool fix_partition_func(THD *thd, TABLE *table, set_linear_hash_mask(part_info, part_info->num_subparts); if (part_info->list_of_subpart_fields) { - List_iterator<char> it(part_info->subpart_field_list); + List_iterator<const char> it(part_info->subpart_field_list); if (unlikely(handle_list_of_fields(thd, it, table, part_info, TRUE))) goto end; } @@ -1617,7 +1617,7 @@ bool fix_partition_func(THD *thd, TABLE *table, set_linear_hash_mask(part_info, part_info->num_parts); if (part_info->list_of_part_fields) { - List_iterator<char> it(part_info->part_field_list); + List_iterator<const char> it(part_info->part_field_list); if (unlikely(handle_list_of_fields(thd, it, table, part_info, FALSE))) goto end; } @@ -1639,7 +1639,7 @@ bool fix_partition_func(THD *thd, TABLE *table, const char *error_str; if (part_info->column_list) { - List_iterator<char> it(part_info->part_field_list); + List_iterator<const char> it(part_info->part_field_list); if (unlikely(handle_list_of_fields(thd, it, table, part_info, FALSE))) goto end; } @@ -1736,11 +1736,11 @@ end: ALTER TABLE commands. Finally it is used for SHOW CREATE TABLES. */ -static int add_part_field_list(THD *thd, String *str, List<char> field_list) +static int add_part_field_list(THD *thd, String *str, List<const char> field_list) { int err= 0; const char *field_name; - List_iterator<char> part_it(field_list); + List_iterator<const char> part_it(field_list); err+= str->append('('); while ((field_name= part_it++)) @@ -1971,7 +1971,7 @@ error: NULL No field found */ -static Create_field* get_sql_field(char *field_name, +static Create_field* get_sql_field(const char *field_name, Alter_info *alter_info) { List_iterator<Create_field> it(alter_info->create_list); @@ -1981,7 +1981,7 @@ static Create_field* get_sql_field(char *field_name, while ((sql_field= it++)) { if (!(my_strcasecmp(system_charset_info, - sql_field->field_name, + sql_field->field_name.str, field_name))) { DBUG_RETURN(sql_field); @@ -1998,7 +1998,7 @@ static int add_column_list_values(String *str, partition_info *part_info, { int err= 0; uint i; - List_iterator<char> it(part_info->part_field_list); + List_iterator<const char> it(part_info->part_field_list); uint num_elements= part_info->part_field_list.elements; bool use_parenthesis= (part_info->part_type == LIST_PARTITION && part_info->num_columns > 1U); @@ -2008,7 +2008,7 @@ static int add_column_list_values(String *str, partition_info *part_info, for (i= 0; i < num_elements; i++) { part_column_list_val *col_val= &list_value->col_val_array[i]; - char *field_name= it++; + const char *field_name= it++; if (col_val->max_value) err+= str->append(STRING_WITH_LEN("MAXVALUE")); else if (col_val->null_value) @@ -2040,8 +2040,8 @@ static int add_column_list_values(String *str, partition_info *part_info, my_error(ER_FIELD_NOT_FOUND_PART_ERROR, MYF(0)); return 1; } - if (check_part_field(sql_field->sql_type, - sql_field->field_name, + if (check_part_field(sql_field->real_field_type(), + sql_field->field_name.str, &result_type, &need_cs_check)) return 1; @@ -2055,7 +2055,7 @@ static int add_column_list_values(String *str, partition_info *part_info, Field *field= part_info->part_field_array[i]; result_type= field->result_type(); if (check_part_field(field->real_type(), - field->field_name, + field->field_name.str, &result_type, &need_cs_check)) return 1; @@ -6540,7 +6540,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, Alter_info *alter_info, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, - char *db, + const char *db, const char *table_name) { /* Set-up struct used to write frm files */ diff --git a/sql/sql_partition.h b/sql/sql_partition.h index 2d0ec788332..b0dede7487e 100644 --- a/sql/sql_partition.h +++ b/sql/sql_partition.h @@ -85,7 +85,7 @@ bool check_reorganise_list(partition_info *new_part_info, partition_info *old_part_info, List<char> list_part_names); handler *get_ha_partition(partition_info *part_info); -int get_parts_for_update(const uchar *old_data, uchar *new_data, +int get_parts_for_update(const uchar *old_data, const uchar *new_data, const uchar *rec0, partition_info *part_info, uint32 *old_part_id, uint32 *new_part_id, longlong *func_value); @@ -257,7 +257,7 @@ uint fast_alter_partition_table(THD *thd, TABLE *table, Alter_info *alter_info, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, - char *db, + const char *db, const char *table_name); bool set_part_state(Alter_info *alter_info, partition_info *tab_part_info, enum partition_state part_state); diff --git a/sql/sql_partition_admin.cc b/sql/sql_partition_admin.cc index 41555306da7..61b026e5450 100644 --- a/sql/sql_partition_admin.cc +++ b/sql/sql_partition_admin.cc @@ -379,16 +379,14 @@ static bool exchange_name_with_ddl_log(THD *thd, /* call rename table from table to tmp-name */ DBUG_EXECUTE_IF("exchange_partition_fail_3", my_error(ER_ERROR_ON_RENAME, MYF(0), - name, tmp_name, 0, "n/a"); + name, tmp_name, 0); error_set= TRUE; goto err_rename;); DBUG_EXECUTE_IF("exchange_partition_abort_3", DBUG_SUICIDE();); if (file->ha_rename_table(name, tmp_name)) { - char errbuf[MYSYS_STRERROR_SIZE]; - my_strerror(errbuf, sizeof(errbuf), my_errno); my_error(ER_ERROR_ON_RENAME, MYF(0), name, tmp_name, - my_errno, errbuf); + my_errno); error_set= TRUE; goto err_rename; } @@ -400,16 +398,13 @@ static bool exchange_name_with_ddl_log(THD *thd, /* call rename table from partition to table */ DBUG_EXECUTE_IF("exchange_partition_fail_5", my_error(ER_ERROR_ON_RENAME, MYF(0), - from_name, name, 0, "n/a"); + from_name, name, 0); error_set= TRUE; goto err_rename;); DBUG_EXECUTE_IF("exchange_partition_abort_5", DBUG_SUICIDE();); if (file->ha_rename_table(from_name, name)) { - char errbuf[MYSYS_STRERROR_SIZE]; - my_strerror(errbuf, sizeof(errbuf), my_errno); - my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, name, - my_errno, errbuf); + my_error(ER_ERROR_ON_RENAME, MYF(0), from_name, name, my_errno); error_set= TRUE; goto err_rename; } @@ -421,16 +416,13 @@ static bool exchange_name_with_ddl_log(THD *thd, /* call rename table from tmp-nam to partition */ DBUG_EXECUTE_IF("exchange_partition_fail_7", my_error(ER_ERROR_ON_RENAME, MYF(0), - tmp_name, from_name, 0, "n/a"); + tmp_name, from_name, 0); error_set= TRUE; goto err_rename;); DBUG_EXECUTE_IF("exchange_partition_abort_7", DBUG_SUICIDE();); if (file->ha_rename_table(tmp_name, from_name)) { - char errbuf[MYSYS_STRERROR_SIZE]; - my_strerror(errbuf, sizeof(errbuf), my_errno); - my_error(ER_ERROR_ON_RENAME, MYF(0), tmp_name, from_name, - my_errno, errbuf); + my_error(ER_ERROR_ON_RENAME, MYF(0), tmp_name, from_name, my_errno); error_set= TRUE; goto err_rename; } @@ -499,7 +491,7 @@ bool Sql_cmd_alter_table_exchange_partition:: TABLE_LIST *swap_table_list; handlerton *table_hton; partition_element *part_elem; - char *partition_name; + const char *partition_name; char temp_name[FN_REFLEN+1]; char part_file_name[2*FN_REFLEN+1]; char swap_file_name[FN_REFLEN+1]; @@ -572,7 +564,7 @@ bool Sql_cmd_alter_table_exchange_partition:: swap_table_list->table_name, "", 0); /* create a unique temp name #sqlx-nnnn_nnnn, x for eXchange */ - my_snprintf(temp_name, sizeof(temp_name), "%sx-%lx_%lx", + my_snprintf(temp_name, sizeof(temp_name), "%sx-%lx_%llx", tmp_file_prefix, current_pid, thd->thread_id); if (lower_case_table_names) my_casedn_str(files_charset_info, temp_name); @@ -802,11 +794,11 @@ bool Sql_cmd_alter_table_truncate_partition::execute(THD *thd) Prune all, but named partitions, to avoid excessive calls to external_lock(). */ - List_iterator<char> partition_names_it(alter_info->partition_names); + List_iterator<const char> partition_names_it(alter_info->partition_names); uint num_names= alter_info->partition_names.elements; for (i= 0; i < num_names; i++) { - char *partition_name= partition_names_it++; + const char *partition_name= partition_names_it++; String *str_partition_name= new (thd->mem_root) String(partition_name, system_charset_info); if (!str_partition_name) diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 6fe01519380..4d25de0f299 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -296,10 +296,10 @@ public: sys_var_pluginvar *cast_pluginvar() { return this; } uchar* real_value_ptr(THD *thd, enum_var_type type); TYPELIB* plugin_var_typelib(void); - uchar* do_value_ptr(THD *thd, enum_var_type type, const LEX_STRING *base); - uchar* session_value_ptr(THD *thd, const LEX_STRING *base) + uchar* do_value_ptr(THD *thd, enum_var_type type, const LEX_CSTRING *base); + uchar* session_value_ptr(THD *thd, const LEX_CSTRING *base) { return do_value_ptr(thd, OPT_SESSION, base); } - uchar* global_value_ptr(THD *thd, const LEX_STRING *base) + uchar* global_value_ptr(THD *thd, const LEX_CSTRING *base) { return do_value_ptr(thd, OPT_GLOBAL, base); } uchar *default_value_ptr(THD *thd) { return do_value_ptr(thd, OPT_DEFAULT, 0); } @@ -370,7 +370,7 @@ bool check_valid_path(const char *path, size_t len) return prefix < len; } -static void fix_dl_name(MEM_ROOT *root, LEX_STRING *dl) +static void fix_dl_name(MEM_ROOT *root, LEX_CSTRING *dl) { const size_t so_ext_len= sizeof(SO_EXT) - 1; if (my_strcasecmp(&my_charset_latin1, dl->str + dl->length - so_ext_len, @@ -450,7 +450,7 @@ static int item_val_real(struct st_mysql_value *value, double *buf) #ifdef HAVE_DLOPEN -static struct st_plugin_dl *plugin_dl_find(const LEX_STRING *dl) +static struct st_plugin_dl *plugin_dl_find(const LEX_CSTRING *dl) { uint i; struct st_plugin_dl *tmp; @@ -506,7 +506,7 @@ static void free_plugin_mem(struct st_plugin_dl *p) if (p->handle) dlclose(p->handle); #endif - my_free(p->dl.str); + my_free(const_cast<char*>(p->dl.str)); if (p->allocated) my_free(p->plugins); } @@ -726,7 +726,7 @@ static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl, } #endif /* HAVE_DLOPEN */ -static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) +static st_plugin_dl *plugin_dl_add(const LEX_CSTRING *dl, int report) { #ifdef HAVE_DLOPEN char dlpath[FN_REFLEN]; @@ -745,7 +745,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) plugin directory are used (to make this even remotely secure). */ if (check_valid_path(dl->str, dl->length) || - check_string_char_length((LEX_STRING *) dl, 0, NAME_CHAR_LEN, + check_string_char_length((LEX_CSTRING *) dl, 0, NAME_CHAR_LEN, system_charset_info, 1) || plugin_dir_len + dl->length + 1 >= FN_REFLEN) { @@ -838,10 +838,12 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) static_cast<int>(plugin_dl.dl.length)); goto ret; } - plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length, - files_charset_info, dl->str, dl->length, system_charset_info, - &dummy_errors); - plugin_dl.dl.str[plugin_dl.dl.length]= 0; + plugin_dl.dl.length= copy_and_convert((char*) plugin_dl.dl.str, + plugin_dl.dl.length, + files_charset_info, dl->str, + dl->length, system_charset_info, + &dummy_errors); + ((char*) plugin_dl.dl.str)[plugin_dl.dl.length]= 0; /* Add this dll to array */ if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl))) { @@ -884,7 +886,8 @@ static void plugin_dl_del(struct st_plugin_dl *plugin_dl) } -static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int type) +static struct st_plugin_int *plugin_find_internal(const LEX_CSTRING *name, + int type) { uint i; DBUG_ENTER("plugin_find_internal"); @@ -911,7 +914,7 @@ static struct st_plugin_int *plugin_find_internal(const LEX_STRING *name, int ty } -static SHOW_COMP_OPTION plugin_status(const LEX_STRING *name, int type) +static SHOW_COMP_OPTION plugin_status(const LEX_CSTRING *name, int type) { SHOW_COMP_OPTION rc= SHOW_OPTION_NO; struct st_plugin_int *plugin; @@ -928,7 +931,7 @@ static SHOW_COMP_OPTION plugin_status(const LEX_STRING *name, int type) } -bool plugin_is_ready(const LEX_STRING *name, int type) +bool plugin_is_ready(const LEX_CSTRING *name, int type) { bool rc= FALSE; if (plugin_status(name, type) == SHOW_OPTION_YES) @@ -939,7 +942,7 @@ bool plugin_is_ready(const LEX_STRING *name, int type) SHOW_COMP_OPTION plugin_status(const char *name, size_t len, int type) { - LEX_STRING plugin_name= { (char *) name, len }; + LEX_CSTRING plugin_name= { name, len }; return plugin_status(&plugin_name, type); } @@ -1023,7 +1026,7 @@ plugin_ref plugin_lock(THD *thd, plugin_ref ptr) } -plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, int type) +plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type) { LEX *lex= thd ? thd->lex : 0; plugin_ref rc= NULL; @@ -1066,7 +1069,7 @@ static st_plugin_int *plugin_insert_or_reuse(struct st_plugin_int *plugin) Requires that a write-lock is held on LOCK_system_variables_hash */ static bool plugin_add(MEM_ROOT *tmp_root, - const LEX_STRING *name, LEX_STRING *dl, int report) + const LEX_CSTRING *name, LEX_CSTRING *dl, int report) { struct st_plugin_int tmp, *maybe_dupe; struct st_maria_plugin *plugin; @@ -1533,7 +1536,7 @@ int plugin_init(int *argc, char **argv, int flags) MEM_ROOT tmp_root; bool reaped_mandatory_plugin= false; bool mandatory= true; - LEX_STRING MyISAM= { C_STRING_WITH_LEN("MyISAM") }; + LEX_CSTRING MyISAM= { STRING_WITH_LEN("MyISAM") }; DBUG_ENTER("plugin_init"); if (initialized) @@ -1662,10 +1665,11 @@ int plugin_init(int *argc, char **argv, int flags) char path[FN_REFLEN + 1]; build_table_filename(path, sizeof(path) - 1, "mysql", "plugin", reg_ext, 0); char engine_name_buf[NAME_CHAR_LEN + 1]; - LEX_STRING maybe_myisam= { engine_name_buf, 0 }; - frm_type_enum frm_type= dd_frm_type(NULL, path, &maybe_myisam); + LEX_CSTRING maybe_myisam= { engine_name_buf, 0 }; + bool is_sequence; + Table_type frm_type= dd_frm_type(NULL, path, &maybe_myisam, &is_sequence); /* if mysql.plugin table is MyISAM - load it right away */ - if (frm_type == FRMTYPE_TABLE && !strcasecmp(maybe_myisam.str, "MyISAM")) + if (frm_type == TABLE_TYPE_NORMAL && !strcasecmp(maybe_myisam.str, "MyISAM")) { plugin_load(&tmp_root); flags|= PLUGIN_INIT_SKIP_PLUGIN_TABLE; @@ -1819,8 +1823,8 @@ static void plugin_load(MEM_ROOT *tmp_root) get_field(tmp_root, table->field[0], &str_name); get_field(tmp_root, table->field[1], &str_dl); - LEX_STRING name= {(char *)str_name.ptr(), str_name.length()}; - LEX_STRING dl= {(char *)str_dl.ptr(), str_dl.length()}; + LEX_CSTRING name= {str_name.ptr(), str_name.length()}; + LEX_CSTRING dl= {str_dl.ptr(), str_dl.length()}; /* there're no other threads running yet, so we don't need a mutex. @@ -1883,14 +1887,16 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, const char *list) mysql_mutex_lock(&LOCK_plugin); free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); name.str= 0; // load everything - if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG)) + if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl, + REPORT_TO_LOG)) goto error; } else { free_root(tmp_root, MYF(MY_MARK_BLOCKS_FREE)); mysql_mutex_lock(&LOCK_plugin); - if (plugin_add(tmp_root, &name, &dl, REPORT_TO_LOG)) + if (plugin_add(tmp_root, (LEX_CSTRING*) &name, (LEX_CSTRING*) &dl, + REPORT_TO_LOG)) goto error; } mysql_mutex_unlock(&LOCK_plugin); @@ -2059,7 +2065,7 @@ void plugin_shutdown(void) That is, initialize it, and update mysql.plugin table */ -static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name, +static bool finalize_install(THD *thd, TABLE *table, const LEX_CSTRING *name, int *argc, char **argv) { struct st_plugin_int *tmp= plugin_find_internal(name, MYSQL_ANY_PLUGIN); @@ -2113,12 +2119,12 @@ static bool finalize_install(THD *thd, TABLE *table, const LEX_STRING *name, return 0; } -bool mysql_install_plugin(THD *thd, const LEX_STRING *name, - const LEX_STRING *dl_arg) +bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, + const LEX_CSTRING *dl_arg) { TABLE_LIST tables; TABLE *table; - LEX_STRING dl= *dl_arg; + LEX_CSTRING dl= *dl_arg; bool error; int argc=orig_argc; char **argv=orig_argv; @@ -2178,7 +2184,7 @@ bool mysql_install_plugin(THD *thd, const LEX_STRING *name, struct st_maria_plugin *plugin; for (plugin= plugin_dl->plugins; plugin->info; plugin++) { - LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) }; + LEX_CSTRING str= { plugin->name, strlen(plugin->name) }; error|= finalize_install(thd, table, &str, &argc, argv); } } @@ -2196,7 +2202,7 @@ err: } -static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name) +static bool do_uninstall(THD *thd, TABLE *table, const LEX_CSTRING *name) { struct st_plugin_int *plugin; mysql_mutex_assert_owner(&LOCK_plugin); @@ -2252,12 +2258,12 @@ static bool do_uninstall(THD *thd, TABLE *table, const LEX_STRING *name) } -bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name, - const LEX_STRING *dl_arg) +bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, + const LEX_CSTRING *dl_arg) { TABLE *table; TABLE_LIST tables; - LEX_STRING dl= *dl_arg; + LEX_CSTRING dl= *dl_arg; bool error= false; DBUG_ENTER("mysql_uninstall_plugin"); @@ -2309,7 +2315,7 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name, for (struct st_maria_plugin *plugin= plugin_dl->plugins; plugin->info; plugin++) { - LEX_STRING str= { const_cast<char*>(plugin->name), strlen(plugin->name) }; + LEX_CSTRING str= { plugin->name, strlen(plugin->name) }; error|= do_uninstall(thd, table, &str); } } @@ -2425,7 +2431,7 @@ static bool plugin_dl_foreach_internal(THD *thd, st_plugin_dl *plugin_dl, return 0; } -bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl, +bool plugin_dl_foreach(THD *thd, const LEX_CSTRING *dl, plugin_foreach_func *func, void *arg) { bool err= 0; @@ -3394,7 +3400,7 @@ TYPELIB* sys_var_pluginvar::plugin_var_typelib(void) uchar* sys_var_pluginvar::do_value_ptr(THD *thd, enum_var_type type, - const LEX_STRING *base) + const LEX_CSTRING *base) { uchar* result; diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 7b89246a9f9..469ae5c88a3 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -90,7 +90,7 @@ struct st_ptr_backup { struct st_plugin_dl { - LEX_STRING dl; + LEX_CSTRING dl; void *handle; struct st_maria_plugin *plugins; st_ptr_backup *ptr_backup; @@ -105,7 +105,7 @@ struct st_plugin_dl struct st_plugin_int { - LEX_STRING name; + LEX_CSTRING name; struct st_maria_plugin *plugin; struct st_plugin_dl *plugin_dl; st_ptr_backup *ptr_backup; @@ -163,18 +163,18 @@ extern const char *plugin_maturity_names[]; extern int plugin_init(int *argc, char **argv, int init_flags); extern void plugin_shutdown(void); void add_plugin_options(DYNAMIC_ARRAY *options, MEM_ROOT *mem_root); -extern bool plugin_is_ready(const LEX_STRING *name, int type); +extern bool plugin_is_ready(const LEX_CSTRING *name, int type); #define my_plugin_lock_by_name(A,B,C) plugin_lock_by_name(A,B,C) #define my_plugin_lock(A,B) plugin_lock(A,B) extern plugin_ref plugin_lock(THD *thd, plugin_ref ptr); -extern plugin_ref plugin_lock_by_name(THD *thd, const LEX_STRING *name, +extern plugin_ref plugin_lock_by_name(THD *thd, const LEX_CSTRING *name, int type); extern void plugin_unlock(THD *thd, plugin_ref plugin); extern void plugin_unlock_list(THD *thd, plugin_ref *list, uint count); -extern bool mysql_install_plugin(THD *thd, const LEX_STRING *name, - const LEX_STRING *dl); -extern bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name, - const LEX_STRING *dl); +extern bool mysql_install_plugin(THD *thd, const LEX_CSTRING *name, + const LEX_CSTRING *dl); +extern bool mysql_uninstall_plugin(THD *thd, const LEX_CSTRING *name, + const LEX_CSTRING *dl); extern bool plugin_register_builtin(struct st_mysql_plugin *plugin); extern void plugin_thdvar_init(THD *thd); extern void plugin_thdvar_cleanup(THD *thd); @@ -192,7 +192,7 @@ extern bool plugin_foreach_with_mask(THD *thd, plugin_foreach_func *func, int type, uint state_mask, void *arg); extern void sync_dynamic_session_variables(THD* thd, bool global_lock); -extern bool plugin_dl_foreach(THD *thd, const LEX_STRING *dl, +extern bool plugin_dl_foreach(THD *thd, const LEX_CSTRING *dl, plugin_foreach_func *func, void *arg); sys_var *find_sys_var_ex(THD *thd, const char *str, size_t length, diff --git a/sql/sql_plugin_compat.h b/sql/sql_plugin_compat.h index 5c7bb620575..e0b184c9b2f 100644 --- a/sql/sql_plugin_compat.h +++ b/sql/sql_plugin_compat.h @@ -25,7 +25,7 @@ #define MIN_AUTHENTICATION_INTERFACE_VERSION 0x0100 struct MYSQL_SERVER_AUTH_INFO_0x0100 { - char *user_name; + const char *user_name; unsigned int user_name_length; const char *auth_string; unsigned long auth_string_length; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 6724346a490..4df92c71006 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -203,7 +203,7 @@ public: void setup_set_params(); virtual Query_arena::Type type() const; virtual void cleanup_stmt(); - bool set_name(LEX_STRING *name); + bool set_name(LEX_CSTRING *name); inline void close_cursor() { delete cursor; cursor= 0; } inline bool is_in_use() { return flags & (uint) IS_IN_USE; } inline bool is_sql_prepare() const { return flags & (uint) IS_SQL_PREPARE; } @@ -819,20 +819,6 @@ static void setup_one_conversion_function(THD *thd, Item_param *param, #ifndef EMBEDDED_LIBRARY /** - Check whether this parameter data type is compatible with long data. - Used to detect whether a long data stream has been supplied to a - incompatible data type. -*/ -inline bool is_param_long_data_type(Item_param *param) -{ - enum_field_types field_type= param->field_type(); - return (((field_type >= MYSQL_TYPE_TINY_BLOB) && - (field_type <= MYSQL_TYPE_STRING)) || - field_type == MYSQL_TYPE_VARCHAR); -} - - -/** Routines to assign parameters from data supplied by the client. Update the parameter markers by reading data from the packet and @@ -909,7 +895,7 @@ static bool insert_params_with_log(Prepared_statement *stmt, uchar *null_array, type (the types are supplied at execute). Check that the supplied type of placeholder can accept a data stream. */ - else if (! is_param_long_data_type(param)) + else if (!param->type_handler()->is_param_long_data_type()) DBUG_RETURN(1); if (acc.append(param)) @@ -957,7 +943,7 @@ static bool insert_params(Prepared_statement *stmt, uchar *null_array, type (the types are supplied at execute). Check that the supplied type of placeholder can accept a data stream. */ - else if (! is_param_long_data_type(param)) + else if (!param->type_handler()->is_param_long_data_type()) DBUG_RETURN(1); if (param->convert_str_value(stmt->thd)) DBUG_RETURN(1); /* out of memory */ @@ -1510,6 +1496,7 @@ static bool mysql_test_delete(Prepared_statement *stmt, uint table_count= 0; THD *thd= stmt->thd; LEX *lex= stmt->lex; + bool delete_while_scanning; DBUG_ENTER("mysql_test_delete"); if (delete_precheck(thd, table_list) || @@ -1538,7 +1525,8 @@ static bool mysql_test_delete(Prepared_statement *stmt, DBUG_RETURN(mysql_prepare_delete(thd, table_list, lex->select_lex.with_wild, lex->select_lex.item_list, - &lex->select_lex.where)); + &lex->select_lex.where, + &delete_while_scanning)); error: DBUG_RETURN(TRUE); } @@ -1963,8 +1951,7 @@ static int mysql_test_show_grants(Prepared_statement *stmt) THD *thd= stmt->thd; List<Item> fields; - mysql_show_grants_get_fields(thd, &fields, "Grants for"); - + mysql_show_grants_get_fields(thd, &fields, STRING_WITH_LEN("Grants for")); DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); } #endif /*NO_EMBEDDED_ACCESS_CHECKS*/ @@ -2053,13 +2040,14 @@ static int mysql_test_show_binlogs(Prepared_statement *stmt) TRUE error, error message is set in THD */ -static int mysql_test_show_create_routine(Prepared_statement *stmt, int type) +static int mysql_test_show_create_routine(Prepared_statement *stmt, + const Sp_handler *sph) { DBUG_ENTER("mysql_test_show_binlogs"); THD *thd= stmt->thd; List<Item> fields; - sp_head::show_create_routine_get_fields(thd, type, &fields); + sp_head::show_create_routine_get_fields(thd, sph, &fields); DBUG_RETURN(send_stmt_metadata(thd, stmt, &fields)); } @@ -2087,7 +2075,7 @@ static bool mysql_test_create_view(Prepared_statement *stmt) TABLE_LIST *view= lex->unlink_first_table(&link_to_local); TABLE_LIST *tables= lex->query_tables; - if (create_view_precheck(thd, tables, view, lex->create_view_mode)) + if (create_view_precheck(thd, tables, view, lex->create_view->mode)) goto err; /* @@ -2287,7 +2275,7 @@ static int mysql_test_handler_read(Prepared_statement *stmt, { if (!lex->result && !(lex->result= new (stmt->mem_root) select_send(thd))) { - my_error(ER_OUTOFMEMORY, MYF(0), sizeof(select_send)); + my_error(ER_OUTOFMEMORY, MYF(0), (int) sizeof(select_send)); DBUG_RETURN(1); } if (send_prep_stmt(stmt, ha_table->fields.elements) || @@ -2399,6 +2387,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) } break; case SQLCOM_CREATE_TABLE: + case SQLCOM_CREATE_SEQUENCE: res= mysql_test_create_table(stmt); break; case SQLCOM_SHOW_CREATE: @@ -2448,21 +2437,21 @@ static bool check_prepared_statement(Prepared_statement *stmt) break; #endif /* EMBEDDED_LIBRARY */ case SQLCOM_SHOW_CREATE_PROC: - if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_PROCEDURE)) == 2) + if ((res= mysql_test_show_create_routine(stmt, &sp_handler_procedure)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); } break; case SQLCOM_SHOW_CREATE_FUNC: - if ((res= mysql_test_show_create_routine(stmt, TYPE_ENUM_FUNCTION)) == 2) + if ((res= mysql_test_show_create_routine(stmt, &sp_handler_function)) == 2) { /* Statement and field info has already been sent */ DBUG_RETURN(FALSE); } break; case SQLCOM_CREATE_VIEW: - if (lex->create_view_mode == VIEW_ALTER) + if (lex->create_view->mode == VIEW_ALTER) { my_message(ER_UNSUPPORTED_PS, ER_THD(thd, ER_UNSUPPORTED_PS), MYF(0)); goto error; @@ -2500,8 +2489,10 @@ static bool check_prepared_statement(Prepared_statement *stmt) */ case SQLCOM_SHOW_EXPLAIN: case SQLCOM_DROP_TABLE: + case SQLCOM_DROP_SEQUENCE: case SQLCOM_RENAME_TABLE: case SQLCOM_ALTER_TABLE: + case SQLCOM_ALTER_SEQUENCE: case SQLCOM_COMMIT: case SQLCOM_CREATE_INDEX: case SQLCOM_DROP_INDEX: @@ -2783,7 +2774,7 @@ bool LEX::get_dynamic_sql_string(LEX_CSTRING *dst, String *buffer) void mysql_sql_stmt_prepare(THD *thd) { LEX *lex= thd->lex; - LEX_STRING *name= &lex->prepared_stmt_name; + LEX_CSTRING *name= &lex->prepared_stmt_name; Prepared_statement *stmt; LEX_CSTRING query; DBUG_ENTER("mysql_sql_stmt_prepare"); @@ -3217,7 +3208,7 @@ void mysql_sql_stmt_execute(THD *thd) { LEX *lex= thd->lex; Prepared_statement *stmt; - LEX_STRING *name= &lex->prepared_stmt_name; + LEX_CSTRING *name= &lex->prepared_stmt_name; /* Query text for binary, general or slow log, if any of them is open */ String expanded_query; DBUG_ENTER("mysql_sql_stmt_execute"); @@ -3438,7 +3429,7 @@ void mysqld_stmt_close(THD *thd, char *packet) void mysql_sql_stmt_close(THD *thd) { Prepared_statement* stmt; - LEX_STRING *name= &thd->lex->prepared_stmt_name; + LEX_CSTRING *name= &thd->lex->prepared_stmt_name; DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", (int) name->length, name->str)); @@ -3801,7 +3792,7 @@ void Prepared_statement::cleanup_stmt() } -bool Prepared_statement::set_name(LEX_STRING *name_arg) +bool Prepared_statement::set_name(LEX_CSTRING *name_arg) { name.length= name_arg->length; name.str= (char*) memdup_root(mem_root, name_arg->str, name_arg->length); @@ -4459,7 +4450,7 @@ Prepared_statement::reprepare() char saved_cur_db_name_buf[SAFE_NAME_LEN+1]; LEX_STRING saved_cur_db_name= { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) }; - LEX_STRING stmt_db_name= { db, db_length }; + LEX_CSTRING stmt_db_name= { db, db_length }; bool cur_db_changed; bool error; @@ -4482,7 +4473,7 @@ Prepared_statement::reprepare() thd->variables.sql_mode= save_sql_mode; if (cur_db_changed) - mysql_change_db(thd, &saved_cur_db_name, TRUE); + mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE); if (! error) { @@ -4580,7 +4571,7 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy) /* Don't swap flags: the copy has IS_SQL_PREPARE always set. */ /* swap_variables(uint, flags, copy->flags); */ /* Swap names, the old name is allocated in the wrong memory root */ - swap_variables(LEX_STRING, name, copy->name); + swap_variables(LEX_CSTRING, name, copy->name); /* Ditto */ swap_variables(char *, db, copy->db); swap_variables(size_t, db_length, copy->db_length); @@ -4625,7 +4616,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) { saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) }; bool cur_db_changed; - LEX_STRING stmt_db_name= { db, db_length }; + LEX_CSTRING stmt_db_name= { db, db_length }; status_var_increment(thd->status_var.com_stmt_execute); @@ -4753,7 +4744,7 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) */ if (cur_db_changed) - mysql_change_db(thd, &saved_cur_db_name, TRUE); + mysql_change_db(thd, (LEX_CSTRING*) &saved_cur_db_name, TRUE); /* Assert that if an error, no cursor is open */ DBUG_ASSERT(! (error && cursor)); @@ -4828,8 +4819,8 @@ bool Prepared_statement::execute_immediate(const char *query, uint query_len) { DBUG_ENTER("Prepared_statement::execute_immediate"); String expanded_query; - static LEX_STRING execute_immediate_stmt_name= - {(char*) STRING_WITH_LEN("(immediate)") }; + static LEX_CSTRING execute_immediate_stmt_name= + {STRING_WITH_LEN("(immediate)") }; set_sql_prepare(); name= execute_immediate_stmt_name; // for DBUG_PRINT etc diff --git a/sql/sql_priv.h b/sql/sql_priv.h index 6c9df6a9606..db75dc2198b 100644 --- a/sql/sql_priv.h +++ b/sql/sql_priv.h @@ -227,6 +227,7 @@ #define OPTIMIZER_SWITCH_EXISTS_TO_IN (1ULL << 28) #define OPTIMIZER_SWITCH_ORDERBY_EQ_PROP (1ULL << 29) #define OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED (1ULL << 30) +#define OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED (1ULL << 31) #define OPTIMIZER_SWITCH_DEFAULT (OPTIMIZER_SWITCH_INDEX_MERGE | \ OPTIMIZER_SWITCH_INDEX_MERGE_UNION | \ @@ -252,7 +253,9 @@ OPTIMIZER_SWITCH_LOOSE_SCAN | \ OPTIMIZER_SWITCH_EXISTS_TO_IN | \ OPTIMIZER_SWITCH_ORDERBY_EQ_PROP | \ - OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED) + OPTIMIZER_SWITCH_COND_PUSHDOWN_FOR_DERIVED | \ + OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED) + /* Replication uses 8 bytes to store SQL_MODE in the binary log. The day you use strictly more than 64 bits by adding one more define above, you should diff --git a/sql/sql_profile.cc b/sql/sql_profile.cc index f3b62991c5c..7206162792a 100644 --- a/sql/sql_profile.cc +++ b/sql/sql_profile.cc @@ -120,8 +120,10 @@ int make_profile_table_for_show(THD *thd, ST_SCHEMA_TABLE *schema_table) continue; field_info= &schema_table->fields_info[i]; + LEX_CSTRING field_name= {field_info->field_name, + strlen(field_info->field_name) }; Item_field *field= new (thd->mem_root) Item_field(thd, context, - NullS, NullS, field_info->field_name); + NullS, NullS, &field_name); if (field) { field->set_name(thd, field_info->old_name, diff --git a/sql/sql_reload.cc b/sql/sql_reload.cc index 73b996e738e..c01ad90f5d2 100644 --- a/sql/sql_reload.cc +++ b/sql/sql_reload.cc @@ -166,7 +166,7 @@ bool reload_acl_and_cache(THD *thd, unsigned long long options, if (options & REFRESH_RELAY_LOG) { #ifdef HAVE_REPLICATION - LEX_STRING connection_name; + LEX_CSTRING connection_name; Master_info *mi; if (thd) connection_name= thd->lex->relay_log_connection_name; diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc index 1588644f0e1..2cba634e17c 100644 --- a/sql/sql_rename.cc +++ b/sql/sql_rename.cc @@ -33,8 +33,8 @@ static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list, bool skip_error); -static bool do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, - char *new_table_name, char *new_table_alias, +static bool do_rename(THD *thd, TABLE_LIST *ren_table, const char *new_db, + const char *new_table_name, const char *new_table_alias, bool skip_error); static TABLE_LIST *reverse_table_list(TABLE_LIST *table_list); @@ -50,7 +50,7 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list, bool silent) bool binlog_error= 0; TABLE_LIST *ren_table= 0; int to_table; - char *rename_log_table[2]= {NULL, NULL}; + const char *rename_log_table[2]= {NULL, NULL}; DBUG_ENTER("mysql_rename_tables"); /* @@ -254,8 +254,9 @@ do_rename_temporary(THD *thd, TABLE_LIST *ren_table, TABLE_LIST *new_table, */ static bool -do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, - char *new_table_alias, bool skip_error) +do_rename(THD *thd, TABLE_LIST *ren_table, const char *new_db, + const char *new_table_name, const char *new_table_alias, + bool skip_error) { int rc= 1; handlerton *hton; @@ -291,11 +292,11 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name, if (!(rc= mysql_rename_table(hton, ren_table->db, old_alias, new_db, new_alias, 0))) { - LEX_STRING db_name= { ren_table->db, ren_table->db_length }; - LEX_STRING table_name= { ren_table->table_name, + LEX_CSTRING db_name= { ren_table->db, ren_table->db_length }; + LEX_CSTRING table_name= { ren_table->table_name, ren_table->table_name_length }; - LEX_STRING new_table= { (char *) new_alias, strlen(new_alias) }; - LEX_STRING new_db_name= { (char*)new_db, strlen(new_db)}; + LEX_CSTRING new_table= { new_alias, strlen(new_alias) }; + LEX_CSTRING new_db_name= { new_db, strlen(new_db)}; (void) rename_table_in_stat_tables(thd, &db_name, &table_name, &new_db_name, &new_table); if ((rc= Table_triggers_list::change_table_name(thd, ren_table->db, diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc index e3a082b680d..1f802b7de83 100644 --- a/sql/sql_repl.cc +++ b/sql/sql_repl.cc @@ -865,7 +865,6 @@ get_binlog_list(MEM_ROOT *memroot) !(e->name= strmake_root(memroot, fname, length))) { mysql_bin_log.unlock_index(); - my_error(ER_OUTOFMEMORY, MYF(0), length + 1 + sizeof(*e)); DBUG_RETURN(NULL); } e->next= current_list; @@ -3045,7 +3044,7 @@ int start_slave(THD* thd , Master_info* mi, bool net_report) { /* connection was deleted while we waited for lock_slave_threads */ mi->unlock_slave_threads(); - my_error(WARN_NO_MASTER_INFO, mi->connection_name.length, + my_error(WARN_NO_MASTER_INFO, MYF(0), (int) mi->connection_name.length, mi->connection_name.str); DBUG_RETURN(-1); } @@ -3303,7 +3302,7 @@ int reset_slave(THD *thd, Master_info* mi) { /* connection was deleted while we waited for lock_slave_threads */ mi->unlock_slave_threads(); - my_error(WARN_NO_MASTER_INFO, mi->connection_name.length, + my_error(WARN_NO_MASTER_INFO, MYF(0), (int) mi->connection_name.length, mi->connection_name.str); DBUG_RETURN(-1); } @@ -3446,7 +3445,7 @@ static bool get_string_parameter(char *to, const char *from, size_t length, uint from_numchars= cs->cset->numchars(cs, from, from + from_length); if (from_numchars > length / cs->mbmaxlen) { - my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name, length / cs->mbmaxlen); + my_error(ER_WRONG_STRING_LENGTH, MYF(0), from, name, (int) (length / cs->mbmaxlen)); return 1; } memcpy(to, from, from_length+1); @@ -3515,7 +3514,7 @@ bool change_master(THD* thd, Master_info* mi, bool *master_info_added) { /* connection was deleted while we waited for lock_slave_threads */ mi->unlock_slave_threads(); - my_error(WARN_NO_MASTER_INFO, mi->connection_name.length, + my_error(WARN_NO_MASTER_INFO, MYF(0), (int) mi->connection_name.length, mi->connection_name.str); DBUG_RETURN(TRUE); } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index 0ce7ff958db..a4caa4077fa 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -223,6 +223,9 @@ static bool test_if_cheaper_ordering(const JOIN_TAB *tab, ha_rows *new_select_limit, uint *new_used_key_parts= NULL, uint *saved_best_key_parts= NULL); +static int test_if_order_by_key(JOIN *join, + ORDER *order, TABLE *table, uint idx, + uint *used_key_parts= NULL); static bool test_if_skip_sort_order(JOIN_TAB *tab,ORDER *order, ha_rows select_limit, bool no_changes, const key_map *map); @@ -321,8 +324,8 @@ void dbug_serve_apcs(THD *thd, int n_calls) bool dbug_user_var_equals_int(THD *thd, const char *name, int value) { user_var_entry *var; - LEX_STRING varname= {(char*)name, strlen(name)}; - if ((var= get_variable(&thd->user_vars, varname, FALSE))) + LEX_CSTRING varname= { name, strlen(name)}; + if ((var= get_variable(&thd->user_vars, &varname, FALSE))) { bool null_value; longlong var_value= var->val_int(&null_value); @@ -346,7 +349,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, DBUG_ENTER("handle_select"); MYSQL_SELECT_START(thd->query()); - if (select_lex->master_unit()->is_union() || + if (select_lex->master_unit()->is_unit_op() || select_lex->master_unit()->fake_select_lex) res= mysql_union(thd, lex, result, &lex->unit, setup_tables_done_option); else @@ -377,7 +380,7 @@ bool handle_select(THD *thd, LEX *lex, select_result *result, res|= thd->is_error(); if (unlikely(res)) result->abort_result_set(); - if (thd->killed == ABORT_QUERY) + if (thd->killed == ABORT_QUERY && !thd->no_errors) { /* If LIMIT ROWS EXAMINED interrupted query execution, issue a warning, @@ -514,9 +517,9 @@ fix_inner_refs(THD *thd, List<Item> &all_fields, SELECT_LEX *select, new_ref= direct_ref ? new (thd->mem_root) Item_direct_ref(thd, ref->context, item_ref, ref->table_name, - ref->field_name, ref->alias_name_used) : + &ref->field_name, ref->alias_name_used) : new (thd->mem_root) Item_ref(thd, ref->context, item_ref, ref->table_name, - ref->field_name, ref->alias_name_used); + &ref->field_name, ref->alias_name_used); if (!new_ref) return TRUE; ref->outer_ref= new_ref; @@ -705,7 +708,7 @@ JOIN::prepare(TABLE_LIST *tables_init, select_lex= select_lex_arg; select_lex->join= this; join_list= &select_lex->top_join_list; - union_part= unit_arg->is_union(); + union_part= unit_arg->is_unit_op(); if (select_lex->handle_derived(thd->lex, DT_PREPARE)) DBUG_RETURN(1); @@ -1075,42 +1078,55 @@ err: DBUG_RETURN(res); /* purecov: inspected */ } -int JOIN::optimize() +void JOIN::build_explain() { - // to prevent double initialization on EXPLAIN - if (optimization_state != JOIN::NOT_OPTIMIZED) - return FALSE; - optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS; - - int res= optimize_inner(); - if (!res && have_query_plan != QEP_DELETED) - { - create_explain_query_if_not_exists(thd->lex, thd->mem_root); - have_query_plan= QEP_AVAILABLE; - save_explain_data(thd->lex->explain, false /* can overwrite */, - need_tmp, - !skip_sort_order && !no_order && (order || group_list), - select_distinct); - uint select_nr= select_lex->select_number; - JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt(); - for (uint i= 0; i < aggr_tables; i++, curr_tab++) + create_explain_query_if_not_exists(thd->lex, thd->mem_root); + have_query_plan= QEP_AVAILABLE; + save_explain_data(thd->lex->explain, false /* can overwrite */, + need_tmp, + !skip_sort_order && !no_order && (order || group_list), + select_distinct); + uint select_nr= select_lex->select_number; + JOIN_TAB *curr_tab= join_tab + exec_join_tab_cnt(); + for (uint i= 0; i < aggr_tables; i++, curr_tab++) + { + if (select_nr == INT_MAX) { - if (select_nr == INT_MAX) - { - /* this is a fake_select_lex of a union */ - select_nr= select_lex->master_unit()->first_select()->select_number; - curr_tab->tracker= thd->lex->explain->get_union(select_nr)-> - get_tmptable_read_tracker(); - } - else - { - curr_tab->tracker= thd->lex->explain->get_select(select_nr)-> - get_using_temporary_read_tracker(); - } + /* this is a fake_select_lex of a union */ + select_nr= select_lex->master_unit()->first_select()->select_number; + curr_tab->tracker= thd->lex->explain->get_union(select_nr)-> + get_tmptable_read_tracker(); + } + else + { + curr_tab->tracker= thd->lex->explain->get_select(select_nr)-> + get_using_temporary_read_tracker(); } - } - optimization_state= JOIN::OPTIMIZATION_DONE; +} + +int JOIN::optimize() +{ + int res= 0; + join_optimization_state init_state= optimization_state; + if (optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE) + res= optimize_stage2(); + else + { + // to prevent double initialization on EXPLAIN + if (optimization_state != JOIN::NOT_OPTIMIZED) + return FALSE; + optimization_state= JOIN::OPTIMIZATION_IN_PROGRESS; + is_for_splittable_grouping_derived= false; + res= optimize_inner(); + } + if (!with_two_phase_optimization || + init_state == JOIN::OPTIMIZATION_PHASE_1_DONE) + { + if (!res && have_query_plan != QEP_DELETED) + build_explain(); + optimization_state= JOIN::OPTIMIZATION_DONE; + } return res; } @@ -1160,10 +1176,8 @@ int JOIN::init_join_caches() int JOIN::optimize_inner() { - ulonglong select_opts_for_readinfo; - uint no_jbuf_after; - JOIN_TAB *tab; DBUG_ENTER("JOIN::optimize"); + subq_exit_fl= false; do_send_rows = (unit->select_limit_cnt) ? 1 : 0; DEBUG_SYNC(thd, "before_join_optimize"); @@ -1325,6 +1339,11 @@ JOIN::optimize_inner() */ if (tbl->is_materialized_derived()) { + JOIN *join= tbl->get_unit()->first_select()->join; + if (join && + join->optimization_state == JOIN::OPTIMIZATION_PHASE_1_DONE && + join->with_two_phase_optimization) + continue; /* Do not push conditions from where into materialized inner tables of outer joins: this is not valid. @@ -1384,6 +1403,7 @@ JOIN::optimize_inner() "Impossible HAVING" : "Impossible WHERE"; table_count= top_join_tab_count= 0; error= 0; + subq_exit_fl= true; goto setup_subq_exit; } } @@ -1438,6 +1458,7 @@ JOIN::optimize_inner() zero_result_cause= "No matching min/max row"; table_count= top_join_tab_count= 0; error=0; + subq_exit_fl= true; goto setup_subq_exit; } if (res > 1) @@ -1480,6 +1501,7 @@ JOIN::optimize_inner() { DBUG_PRINT("info",("No tables")); error= 0; + subq_exit_fl= true; goto setup_subq_exit; } error= -1; // Error is sent to client @@ -1513,6 +1535,39 @@ JOIN::optimize_inner() DBUG_RETURN(1); } +setup_subq_exit: + with_two_phase_optimization= check_two_phase_optimization(thd); + if (with_two_phase_optimization) + optimization_state= JOIN::OPTIMIZATION_PHASE_1_DONE; + else + { + if (optimize_stage2()) + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + +int JOIN::optimize_stage2() +{ + ulonglong select_opts_for_readinfo; + uint no_jbuf_after; + JOIN_TAB *tab; + DBUG_ENTER("JOIN::optimize_stage2"); + + if (subq_exit_fl) + goto setup_subq_exit; + + if (select_lex->handle_derived(thd->lex, DT_OPTIMIZE)) + DBUG_RETURN(1); + + if (thd->check_killed()) + DBUG_RETURN(1); + + /* Generate an execution plan from the found optimal join order. */ + if (get_best_combination()) + DBUG_RETURN(1); + if (optimizer_flag(thd, OPTIMIZER_SWITCH_DERIVED_WITH_KEYS)) drop_unused_derived_keys(); @@ -1555,7 +1610,14 @@ JOIN::optimize_inner() } if (const_tables && !thd->locked_tables_mode && !(select_options & SELECT_NO_UNLOCK)) - mysql_unlock_some_tables(thd, table, const_tables); + { + /* + Unlock all tables, except sequences, as accessing these may still + require table updates + */ + mysql_unlock_some_tables(thd, table, const_tables, + GET_LOCK_SKIP_SEQUENCES); + } if (!conds && outer_join) { /* Handle the case where we have an OUTER JOIN without a WHERE */ @@ -4454,10 +4516,12 @@ make_join_statistics(JOIN *join, List<TABLE_LIST> &tables_list, if (join->choose_subquery_plan(all_table_map & ~join->const_table_map)) goto error; + if (join->improve_chosen_plan(join->thd)) + goto error; + DEBUG_SYNC(join->thd, "inside_make_join_statistics"); - /* Generate an execution plan from the found optimal join order. */ - DBUG_RETURN(join->thd->check_killed() || join->get_best_combination()); + DBUG_RETURN(0); error: /* @@ -5039,7 +5103,7 @@ Item_func_trig_cond::add_key_fields(JOIN *join, KEY_FIELD **key_fields, if (!join->group_list && !join->order && join->unit->item && join->unit->item->substype() == Item_subselect::IN_SUBS && - !join->unit->is_union()) + !join->unit->is_unit_op()) { KEY_FIELD *save= *key_fields; args[0]->add_key_fields(join, key_fields, and_level, usable_tables, @@ -8785,6 +8849,268 @@ JOIN_TAB *next_depth_first_tab(JOIN* join, JOIN_TAB* tab) return tab; } +static +bool key_can_be_used_to_split_by_fields(KEY *key_info, uint used_key_parts, + List<Field> &fields) +{ + if (used_key_parts < fields.elements) + return false; + List_iterator_fast<Field> li(fields); + Field *fld; + KEY_PART_INFO *start= key_info->key_part; + KEY_PART_INFO *end= start + fields.elements; + while ((fld= li++)) + { + KEY_PART_INFO *key_part; + for (key_part= start; key_part < end; key_part++) + { + if (key_part->fieldnr == fld->field_index + 1) + break; + } + if (key_part == end) + return false; + } + return true; +} + +bool JOIN::check_for_splittable_grouping_derived(THD *thd) +{ + partition_list= 0; + st_select_lex_unit *unit= select_lex->master_unit(); + TABLE_LIST *derived= unit->derived; + if (!optimizer_flag(thd, OPTIMIZER_SWITCH_SPLIT_GROUPING_DERIVED)) + return false; + if (!(derived && derived->is_materialized_derived())) + return false; + if (unit->first_select()->next_select()) + return false; + if (derived->prohibit_cond_pushdown) + return false; + if (derived->is_recursive_with_table()) + return false; + if (group_list) + { + if (!select_lex->have_window_funcs()) + partition_list= group_list; + } + else if (select_lex->have_window_funcs() && + select_lex->window_specs.elements == 1) + { + partition_list= + select_lex->window_specs.head()->partition_list->first; + } + if (!partition_list) + return false; + + ORDER *ord; + TABLE *table= 0; + key_map ref_keys; + uint group_fields= 0; + ref_keys.set_all(); + for (ord= partition_list; ord; ord= ord->next, group_fields++) + { + Item *ord_item= *ord->item; + if (ord_item->real_item()->type() != Item::FIELD_ITEM) + return false; + Field *ord_field= ((Item_field *) (ord_item->real_item()))->field; + if (!table) + table= ord_field->table; + else if (table != ord_field->table) + return false; + ref_keys.intersect(ord_field->part_of_key); + } + if (ref_keys.is_clear_all()) + return false; + + uint i; + List<Field> grouping_fields; + List<Field> splitting_fields; + List_iterator<Item> li(fields_list); + for (ord= partition_list; ord; ord= ord->next) + { + Item *item; + i= 0; + while ((item= li++)) + { + if ((*ord->item)->eq(item, 0)) + break; + i++; + } + if (!item) + return false; + if (splitting_fields.push_back(derived->table->field[i], thd->mem_root)) + return false; + Item_field *ord_field= (Item_field *)(item->real_item()); + if (grouping_fields.push_back(ord_field->field, thd->mem_root)) + return false; + li.rewind(); + } + + for (i= 0; i < table->s->keys; i++) + { + if (!(ref_keys.is_set(i))) + continue; + KEY *key_info= table->key_info + i; + if (key_can_be_used_to_split_by_fields(key_info, + table->actual_n_key_parts(key_info), + grouping_fields)) + break; + } + if (i == table->s->keys) + return false; + + derived->table->splitting_fields= splitting_fields; + is_for_splittable_grouping_derived= true; + return true; +} + + +bool JOIN::check_two_phase_optimization(THD *thd) +{ + if (!check_for_splittable_grouping_derived(thd)) + return false; + return true; +} + + +Item *JOIN_TAB::get_splitting_cond_for_grouping_derived(THD *thd) +{ + /* this is a stub */ + TABLE_LIST *derived= table->pos_in_table_list; + st_select_lex *sel= derived->get_unit()->first_select(); + Item *cond= 0; + table_map used_tables= OUTER_REF_TABLE_BIT; + POSITION *pos= join->best_positions; + for (; pos->table != this; pos++) + { + used_tables|= pos->table->table->map; + } + + if (!pos->key) + return 0; + + KEY *key_info= table->key_info + pos->key->key; + if (!key_can_be_used_to_split_by_fields(key_info, + key_info->user_defined_key_parts, + table->splitting_fields)) + return 0; + + create_ref_for_key(join, this, pos->key, + false, used_tables); + List<Item> cond_list; + KEY_PART_INFO *start= key_info->key_part; + KEY_PART_INFO *end= start + table->splitting_fields.elements; + List_iterator_fast<Field> li(table->splitting_fields); + Field *fld= li++; + for (ORDER *ord= sel->join->partition_list; ord; + ord= ord->next, fld= li++) + { + Item *left_item= (*ord->item)->build_clone(thd, thd->mem_root); + uint i= 0; + for (KEY_PART_INFO *key_part= start; key_part < end; key_part++, i++) + { + if (key_part->fieldnr == fld->field_index + 1) + break; + } + Item *right_item= ref.items[i]->build_clone(thd, thd->mem_root); + Item_func_eq *eq_item= 0; + right_item= right_item->build_clone(thd, thd->mem_root); + if (left_item && right_item) + { + right_item->walk(&Item::set_fields_as_dependent_processor, + false, join->select_lex); + right_item->update_used_tables(); + eq_item= new (thd->mem_root) Item_func_eq(thd, left_item, right_item); + } + if (!eq_item || cond_list.push_back(eq_item, thd->mem_root)) + return 0; + } + switch (cond_list.elements) { + case 0: break; + case 1: cond= cond_list.head(); break; + default: cond= new (thd->mem_root) Item_cond_and(thd, cond_list); + } + if (cond) + cond->fix_fields(thd,0); + return cond; +} + +bool JOIN::inject_cond_into_where(Item *injected_cond) +{ + Item *where_item= injected_cond; + List<Item> *and_args= NULL; + if (conds && conds->type() == Item::COND_ITEM && + ((Item_cond*) conds)->functype() == Item_func::COND_AND_FUNC) + { + and_args= ((Item_cond*) conds)->argument_list(); + if (cond_equal) + and_args->disjoin((List<Item> *) &cond_equal->current_level); + } + + where_item= and_items(thd, conds, where_item); + if (!where_item->fixed && where_item->fix_fields(thd, 0)) + return true; + thd->change_item_tree(&select_lex->where, where_item); + select_lex->where->top_level_item(); + conds= select_lex->where; + + if (and_args && cond_equal) + { + and_args= ((Item_cond*) conds)->argument_list(); + List_iterator<Item_equal> li(cond_equal->current_level); + Item_equal *elem; + while ((elem= li++)) + { + and_args->push_back(elem, thd->mem_root); + } + } + + return false; + +} + +bool JOIN::push_splitting_cond_into_derived(THD *thd, Item *cond) +{ + enum_reopt_result reopt_result= REOPT_NONE; + table_map all_table_map= 0; + for (JOIN_TAB *tab= join_tab; + tab < join_tab + top_join_tab_count; tab++) + all_table_map|= tab->table->map; + reopt_result= reoptimize(cond, all_table_map & ~const_table_map, NULL); + if (reopt_result == REOPT_ERROR) + return true; + if (inject_cond_into_where(cond)) + return true; + if (cond->used_tables() & OUTER_REF_TABLE_BIT) + { + select_lex->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; + st_select_lex_unit *unit= select_lex->master_unit(); + unit->uncacheable|= UNCACHEABLE_DEPENDENT_INJECTED; + } + return false; +} + +bool JOIN::improve_chosen_plan(THD *thd) +{ + for (JOIN_TAB *tab= join_tab + const_tables; + tab < join_tab + table_count; tab++) + { + TABLE_LIST *tbl= tab->table->pos_in_table_list; + if (tbl->is_materialized_derived()) + { + st_select_lex *sel= tbl->get_unit()->first_select(); + JOIN *derived_join= sel->join; + if (derived_join && derived_join->is_for_splittable_grouping_derived) + { + Item *cond= tab->get_splitting_cond_for_grouping_derived(thd); + if (cond && derived_join->push_splitting_cond_into_derived(thd, cond)) + return true; + } + } + } + return false; +} + static Item * const null_ptr= NULL; @@ -11853,12 +12179,14 @@ bool JOIN_TAB::preread_init() /* Materialize derived table/view. */ if ((!derived->get_unit()->executed || - derived->is_recursive_with_table()) && + derived->is_recursive_with_table() || + derived->get_unit()->uncacheable) && mysql_handle_single_derived(join->thd->lex, derived, DT_CREATE | DT_FILL)) return TRUE; - preread_init_done= TRUE; + if (!(derived->get_unit()->uncacheable & UNCACHEABLE_DEPENDENT)) + preread_init_done= TRUE; if (select && select->quick) select->quick->replace_handler(table->file); @@ -12919,10 +13247,15 @@ static bool check_simple_equality(THD *thd, const Item::Context &ctx, else { /* None of the fields was found in multiple equalities */ - Item_equal *item_equal= new (thd->mem_root) Item_equal(thd, - orig_left_item, - orig_right_item, - FALSE); + Type_handler_hybrid_field_type + tmp(orig_left_item->type_handler_for_comparison()); + if (tmp.aggregate_for_comparison(orig_right_item-> + type_handler_for_comparison())) + return false; + Item_equal *item_equal= + new (thd->mem_root) Item_equal(thd, tmp.type_handler(), + orig_left_item, orig_right_item, + false); item_equal->set_context_field((Item_field*)left_item); cond_equal->current_level.push_back(item_equal, thd->mem_root); } @@ -13007,8 +13340,14 @@ static bool check_simple_equality(THD *thd, const Item::Context &ctx, } else { - item_equal= new (thd->mem_root) Item_equal(thd, const_item2, - orig_field_item, TRUE); + Type_handler_hybrid_field_type + tmp(orig_left_item->type_handler_for_comparison()); + if (tmp.aggregate_for_comparison(orig_right_item-> + type_handler_for_comparison())) + return false; + item_equal= new (thd->mem_root) Item_equal(thd, tmp.type_handler(), + const_item2, + orig_field_item, true); item_equal->set_context_field(field_item); cond_equal->current_level.push_back(item_equal, thd->mem_root); } @@ -13058,6 +13397,14 @@ static bool check_row_equality(THD *thd, const Arg_comparator *comparators, if (left_item->type() == Item::ROW_ITEM && right_item->type() == Item::ROW_ITEM) { + /* + Item_splocal for ROW SP variables return Item::ROW_ITEM. + Here we know that left_item and right_item are not Item_splocal, + because ROW SP variables with nested ROWs are not supported yet. + It's safe to cast left_item and right_item to Item_row. + */ + DBUG_ASSERT(!left_item->get_item_splocal()); + DBUG_ASSERT(!right_item->get_item_splocal()); is_converted= check_row_equality(thd, comparators[i].subcomparators(), (Item_row *) left_item, @@ -13069,7 +13416,7 @@ static bool check_row_equality(THD *thd, const Arg_comparator *comparators, const Arg_comparator *tmp= &comparators[i]; is_converted= check_simple_equality(thd, Item::Context(Item::ANY_SUBST, - tmp->compare_type(), + tmp->compare_type_handler(), tmp->compare_collation()), left_item, right_item, cond_equal); @@ -13128,6 +13475,15 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal, if (left_item->type() == Item::ROW_ITEM && right_item->type() == Item::ROW_ITEM) { + /* + Item_splocal::type() for ROW variables returns Item::ROW_ITEM. + Distinguish ROW-type Item_splocal from Item_row. + Example query: + SELECT 1 FROM DUAL WHERE row_sp_variable=ROW(100,200); + */ + if (left_item->get_item_splocal() || + right_item->get_item_splocal()) + return false; return check_row_equality(thd, cmp.subcomparators(), (Item_row *) left_item, @@ -13136,7 +13492,7 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal, } return check_simple_equality(thd, Context(ANY_SUBST, - compare_type(), + compare_type_handler(), compare_collation()), left_item, right_item, cond_equal); } @@ -14207,71 +14563,11 @@ can_change_cond_ref_to_const(Item_bool_func2 *target, Item_bool_func2 *source, Item *source_expr, Item *source_const) { - if (!target_expr->eq(source_expr,0) || - target_value == source_const || - target->compare_type() != source->compare_type()) - return false; - if (target->compare_type() == STRING_RESULT) - { - /* - In this example: - SET NAMES utf8 COLLATE utf8_german2_ci; - DROP TABLE IF EXISTS t1; - CREATE TABLE t1 (a CHAR(10) CHARACTER SET utf8); - INSERT INTO t1 VALUES ('o-umlaut'),('oe'); - SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci AND a='oe'; - - the query should return only the row with 'oe'. - It should not return 'o-umlaut', because 'o-umlaut' does not match - the right part of the condition: a='oe' - ('o-umlaut' is not equal to 'oe' in utf8_general_ci, - which is the collation of the field "a"). - - If we change the right part from: - ... AND a='oe' - to - ... AND 'oe' COLLATE utf8_german2_ci='oe' - it will be evalulated to TRUE and removed from the condition, - so the overall query will be simplified to: - - SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci; - - which will erroneously start to return both 'oe' and 'o-umlaut'. - So changing "expr" to "const" is not possible if the effective - collations of "target" and "source" are not exactly the same. - - Note, the code before the fix for MDEV-7152 only checked that - collations of "source_const" and "target_value" are the same. - This was not enough, as the bug report demonstrated. - */ - return - target->compare_collation() == source->compare_collation() && - target_value->collation.collation == source_const->collation.collation; - } - if (target->compare_type() == TIME_RESULT) - { - if (target_value->cmp_type() != TIME_RESULT) - { - /* - Can't rewrite: - WHERE COALESCE(time_column)='00:00:00' - AND COALESCE(time_column)=DATE'2015-09-11' - to - WHERE DATE'2015-09-11'='00:00:00' - AND COALESCE(time_column)=DATE'2015-09-11' - because the left part will erroneously try to parse '00:00:00' - as DATE, not as TIME. - - TODO: It could still be rewritten to: - WHERE DATE'2015-09-11'=TIME'00:00:00' - AND COALESCE(time_column)=DATE'2015-09-11' - i.e. we need to replace both target_expr and target_value - at the same time. This is not supported yet. - */ - return false; - } - } - return true; // Non-string comparison + return target_expr->eq(source_expr,0) && + target_value != source_const && + target->compare_type_handler()-> + can_change_cond_ref_to_const(target, target_expr, target_value, + source, source_expr, source_const); } @@ -15959,7 +16255,7 @@ const_expression_in_where(COND *cond, Item *comp_item, Field *comp_field, */ Field *create_tmp_field_from_field(THD *thd, Field *org_field, - const char *name, TABLE *table, + LEX_CSTRING *name, TABLE *table, Item_field *item) { Field *new_field; @@ -15973,7 +16269,7 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field, if (item) item->result_field= new_field; else - new_field->field_name= name; + new_field->field_name= *name; new_field->flags|= (org_field->flags & NO_DEFAULT_VALUE_FLAG); if (org_field->maybe_null() || (item && item->maybe_null)) new_field->flags&= ~NOT_NULL_FLAG; // Because of outer join @@ -15992,45 +16288,33 @@ Field *create_tmp_field_from_field(THD *thd, Field *org_field, } -Field *Item::create_tmp_field(bool group, TABLE *table, uint convert_int_length) +Field *Item::create_tmp_field_int(TABLE *table, uint convert_int_length) +{ + const Type_handler *h= &type_handler_long; + if (max_char_length() > convert_int_length) + h= &type_handler_longlong; + return h->make_and_init_table_field(&name, Record_addr(maybe_null), + *this, table); +} + + +Field *Item_sum::create_tmp_field(bool group, TABLE *table) { Field *UNINIT_VAR(new_field); MEM_ROOT *mem_root= table->in_use->mem_root; switch (cmp_type()) { case REAL_RESULT: + { new_field= new (mem_root) - Field_double(max_length, maybe_null, name, decimals, TRUE); + Field_double(max_char_length(), maybe_null, &name, decimals, TRUE); break; + } case INT_RESULT: - /* - Select an integer type with the minimal fit precision. - convert_int_length is sign inclusive, don't consider the sign. - */ - if (max_char_length() > convert_int_length) - new_field= new (mem_root) - Field_longlong(max_char_length(), maybe_null, name, unsigned_flag); - else - new_field= new (mem_root) - Field_long(max_char_length(), maybe_null, name, unsigned_flag); - break; case TIME_RESULT: - new_field= tmp_table_field_from_field_type(table, true, false); - break; - case STRING_RESULT: - DBUG_ASSERT(collation.collation); - /* - GEOMETRY fields have STRING_RESULT result type. - To preserve type they needed to be handled separately. - */ - if (field_type() == MYSQL_TYPE_GEOMETRY) - new_field= tmp_table_field_from_field_type(table, true, false); - else - new_field= make_string_field(table); - new_field->set_derivation(collation.derivation, collation.repertoire); - break; case DECIMAL_RESULT: - new_field= Field_new_decimal::create_from_item(mem_root, this); + case STRING_RESULT: + new_field= tmp_table_field_from_field_type(table); break; case ROW_RESULT: // This case should never be choosen @@ -16044,6 +16328,22 @@ Field *Item::create_tmp_field(bool group, TABLE *table, uint convert_int_length) } +static void create_tmp_field_from_item_finalize(THD *thd, + Field *new_field, + Item *item, + Item ***copy_func, + bool modify_item) +{ + if (copy_func && + (item->is_result_field() || + (item->real_item()->is_result_field()))) + *((*copy_func)++) = item; // Save for copy_funcs + if (modify_item) + item->set_result_field(new_field); + if (item->type() == Item::NULL_ITEM) + new_field->is_created_from_null_item= TRUE; +} + /** Create field for temporary table using type of given item. @@ -16074,16 +16374,9 @@ static Field *create_tmp_field_from_item(THD *thd, Item *item, TABLE *table, { Field *UNINIT_VAR(new_field); DBUG_ASSERT(thd == table->in_use); - new_field= item->Item::create_tmp_field(false, table); - - if (copy_func && - (item->is_result_field() || - (item->real_item()->is_result_field()))) - *((*copy_func)++) = item; // Save for copy_funcs - if (modify_item) - item->set_result_field(new_field); - if (item->type() == Item::NULL_ITEM) - new_field->is_created_from_null_item= TRUE; + if ((new_field= item->create_tmp_field(false, table))) + create_tmp_field_from_item_finalize(thd, new_field, item, + copy_func, modify_item); return new_field; } @@ -16107,15 +16400,16 @@ Field *Item::create_field_for_schema(THD *thd, TABLE *table) { Field *field; if (max_length > MAX_FIELD_VARCHARLENGTH) - field= new Field_blob(max_length, maybe_null, name, collation.collation); + field= new Field_blob(max_length, maybe_null, &name, + collation.collation); else - field= new Field_varstring(max_length, maybe_null, name, + field= new Field_varstring(max_length, maybe_null, &name, table->s, collation.collation); if (field) field->init(table); return field; } - return tmp_table_field_from_field_type(table, false, false); + return tmp_table_field_from_field_type(table); } @@ -16157,6 +16451,8 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, Item::Type orig_type= type; Item *orig_item= 0; + DBUG_ASSERT(thd == table->in_use); + if (type != Item::FIELD_ITEM && item->real_item()->type() == Item::FIELD_ITEM) { @@ -16166,6 +16462,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, } switch (type) { + case Item::TYPE_HOLDER: case Item::SUM_FUNC_ITEM: { result= item->create_tmp_field(group, table); @@ -16214,19 +16511,25 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, else if (table_cant_handle_bit_fields && field->field->type() == MYSQL_TYPE_BIT) { + const Type_handler *handler= item->type_handler_long_or_longlong(); *from_field= field->field; - result= create_tmp_field_from_item(thd, item, table, copy_func, - modify_item); + if ((result= + handler->make_and_init_table_field(&item->name, + Record_addr(item->maybe_null), + *item, table))) + create_tmp_field_from_item_finalize(thd, result, item, + copy_func, modify_item); if (result && modify_item) field->result_field= result; } else + { + LEX_CSTRING *tmp= orig_item ? &orig_item->name : &item->name; result= create_tmp_field_from_field(thd, (*from_field= field->field), - orig_item ? orig_item->name : - item->name, - table, + tmp, table, modify_item ? field : NULL); + } if (orig_type == Item::REF_ITEM && orig_modify) ((Item_ref*)orig_item)->set_result_field(result); /* @@ -16254,11 +16557,10 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, { *((*copy_func)++)= item; } - Field *result_field= create_tmp_field_from_field(thd, sp_result_field, - item_func_sp->name, + &item_func_sp->name, table, NULL); @@ -16295,11 +16597,6 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type, return create_tmp_field_from_item(thd, item, table, (make_copy_field ? 0 : copy_func), modify_item); - case Item::TYPE_HOLDER: - result= ((Item_type_holder *)item)->make_field_by_type(table); - result->set_derivation(item->collation.derivation, - item->collation.repertoire); - return result; default: // Dosen't have to be stored return 0; } @@ -16615,6 +16912,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, distinct, false); if (!new_field) goto err; // Should be OOM + DBUG_ASSERT(!new_field->field_name.str || strlen(new_field->field_name.str) == new_field->field_name.length); tmp_from_field++; reclength+=new_field->pack_length(); if (new_field->flags & BLOB_FLAG) @@ -16690,6 +16988,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, goto err; // Got OOM continue; // Some kind of const item } + DBUG_ASSERT(!new_field->field_name.str || strlen(new_field->field_name.str) == new_field->field_name.length); if (type == Item::SUM_FUNC_ITEM) { Item_sum *agg_item= (Item_sum *) item; @@ -17133,7 +17432,7 @@ create_tmp_table(THD *thd, TMP_TABLE_PARAM *param, List<Item> &fields, (uchar*) 0, (uint) 0, Field::NONE, - NullS, &my_charset_bin); + &null_clex_str, &my_charset_bin); if (!key_part_info->field) goto err; key_part_info->field->init(table); @@ -17255,18 +17554,18 @@ bool Virtual_tmp_table::init(uint field_count) }; -bool Virtual_tmp_table::add(List<Column_definition> &field_list) +bool Virtual_tmp_table::add(List<Spvar_definition> &field_list) { /* Create all fields and calculate the total length of record */ - Column_definition *cdef; /* column definition */ - List_iterator_fast<Column_definition> it(field_list); + Spvar_definition *cdef; /* column definition */ + List_iterator_fast<Spvar_definition> it(field_list); for ( ; (cdef= it++); ) { Field *tmp; if (!(tmp= cdef->make_field(s, in_use->mem_root, 0, (uchar*) (f_maybe_null(cdef->pack_flag) ? "" : 0), f_maybe_null(cdef->pack_flag) ? 1 : 0, - cdef->field_name))) + &cdef->field_name))) return true; add(tmp); } @@ -20703,7 +21002,7 @@ part_of_refkey(TABLE *table,Field *field) static int test_if_order_by_key(JOIN *join, ORDER *order, TABLE *table, uint idx, - uint *used_key_parts= NULL) + uint *used_key_parts) { KEY_PART_INFO *key_part,*key_part_end; key_part=table->key_info[idx].key_part; @@ -22249,7 +22548,7 @@ find_order_in_list(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_NON_UNIQ_ERROR, ER_THD(thd, ER_NON_UNIQ_ERROR), - ((Item_ident*) order_item)->field_name, + ((Item_ident*) order_item)->field_name.str, thd->where); } } @@ -23289,14 +23588,15 @@ change_to_use_tmp_fields(THD *thd, Ref_ptr_array ref_pointer_array, ifield->db_name= iref->db_name; } #ifndef DBUG_OFF - if (!item_field->name) + if (!item_field->name.str) { char buff[256]; String str(buff,sizeof(buff),&my_charset_bin); str.length(0); str.extra_allocation(1024); item->print(&str, QT_ORDINARY); - item_field->name= thd->strmake(str.ptr(),str.length()); + item_field->name.str= thd->strmake(str.ptr(), str.length()); + item_field->name.length= str.length(); } #endif } @@ -23652,8 +23952,9 @@ static bool change_group_ref(THD *thd, Item_func *expr, ORDER *group_list, if (item->eq(*group_tmp->item,0)) { Item *new_item; - if (!(new_item= new (thd->mem_root) Item_ref(thd, context, group_tmp->item, 0, - item->name))) + if (!(new_item= new (thd->mem_root) Item_ref(thd, context, + group_tmp->item, 0, + &item->name))) return 1; // fatal_error is set thd->change_item_tree(arg, new_item); arg_changed= TRUE; @@ -24689,6 +24990,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, explain->select_id= join->select_lex->select_number; explain->select_type= join->select_lex->type; + explain->linkage= select_lex->linkage; explain->using_temporary= need_tmp; explain->using_filesort= need_order_arg; /* Setting explain->message means that all other members are invalid */ @@ -24707,6 +25009,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, explain->select_id= select_lex->select_number; explain->select_type= select_lex->type; + explain->linkage= select_lex->linkage; explain->using_temporary= need_tmp; explain->using_filesort= need_order_arg; explain->message= "Storage engine handles GROUP BY"; @@ -24726,6 +25029,7 @@ int JOIN::save_explain_data_intern(Explain_query *output, join->select_lex->set_explain_type(true); xpl_sel->select_id= join->select_lex->select_number; xpl_sel->select_type= join->select_lex->type; + xpl_sel->linkage= select_lex->linkage; if (select_lex->master_unit()->derived) xpl_sel->connection_type= Explain_node::EXPLAIN_NODE_DERIVED; @@ -24901,12 +25205,12 @@ bool mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result) sl->options|= SELECT_DESCRIBE; } - if (unit->is_union()) + if (unit->is_unit_op()) { if (unit->union_needs_tmp_table() && unit->fake_select_lex) { unit->fake_select_lex->select_number= FAKE_SELECT_LEX_ID; // just for initialization - unit->fake_select_lex->type= "UNION RESULT"; + unit->fake_select_lex->type= unit_operation_text[unit->common_op()]; unit->fake_select_lex->options|= SELECT_DESCRIBE; } if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE))) @@ -25285,6 +25589,17 @@ void st_select_lex::print(THD *thd, String *str, enum_query_type query_type) { DBUG_ASSERT(thd); + if ((query_type & QT_SHOW_SELECT_NUMBER) && + thd->lex->all_selects_list && + thd->lex->all_selects_list->link_next && + select_number != UINT_MAX && + select_number != INT_MAX) + { + str->append("/* select#"); + str->append_ulonglong(select_number); + str->append(" */ "); + } + str->append(STRING_WITH_LEN("select ")); if (join && join->cleaned) diff --git a/sql/sql_select.h b/sql/sql_select.h index 886a66c3ef4..9f91733ae9c 100644 --- a/sql/sql_select.h +++ b/sql/sql_select.h @@ -614,6 +614,7 @@ typedef struct st_join_table { bool use_order() const; ///< Use ordering provided by chosen index? bool sort_table(); bool remove_duplicates(); + Item *get_splitting_cond_for_grouping_derived(THD *thd); } JOIN_TAB; @@ -1274,6 +1275,8 @@ public: and should be taken from the appropriate JOIN_TAB */ bool filesort_found_rows; + + bool subq_exit_fl; ROLLUP rollup; ///< Used with rollup @@ -1380,7 +1383,8 @@ public: enum join_optimization_state { NOT_OPTIMIZED=0, OPTIMIZATION_IN_PROGRESS=1, - OPTIMIZATION_DONE=2}; + OPTIMIZATION_PHASE_1_DONE=2, + OPTIMIZATION_DONE=3}; // state of JOIN optimization enum join_optimization_state optimization_state; bool initialized; ///< flag to avoid double init_execution calls @@ -1405,6 +1409,15 @@ public: bool set_group_rpa; /** Exec time only: TRUE <=> current group has been sent */ bool group_sent; + /** + TRUE if the query contains an aggregate function but has no GROUP + BY clause. + */ + bool implicit_grouping; + + bool is_for_splittable_grouping_derived; + bool with_two_phase_optimization; + ORDER *partition_list; JOIN_TAB *sort_and_group_aggr_tab; @@ -1510,6 +1523,8 @@ public: bool prepare_stage2(); int optimize(); int optimize_inner(); + int optimize_stage2(); + void build_explain(); int reinit(); int init_execution(); void exec(); @@ -1656,6 +1671,11 @@ public: bool need_order, bool distinct, const char *message); JOIN_TAB *first_breadth_first_tab() { return join_tab; } + bool check_two_phase_optimization(THD *thd); + bool check_for_splittable_grouping_derived(THD *thd); + bool inject_cond_into_where(Item *injected_cond); + bool push_splitting_cond_into_derived(THD *thd, Item *cond); + bool improve_chosen_plan(THD *thd); private: /** Create a temporary table to be used for processing DISTINCT/ORDER @@ -1686,13 +1706,9 @@ private: */ void optimize_distinct(); - /** - TRUE if the query contains an aggregate function but has no GROUP - BY clause. - */ - bool implicit_grouping; void cleanup_item_list(List<Item> &items) const; bool make_aggr_tables_info(); + }; enum enum_with_bush_roots { WITH_BUSH_ROOTS, WITHOUT_BUSH_ROOTS}; @@ -1724,7 +1740,7 @@ void copy_fields(TMP_TABLE_PARAM *param); bool copy_funcs(Item **func_ptr, const THD *thd); uint find_shortest_key(TABLE *table, const key_map *usable_keys); Field* create_tmp_field_from_field(THD *thd, Field* org_field, - const char *name, TABLE *table, + LEX_CSTRING *name, TABLE *table, Item_field *item); bool is_indexed_agg_distinct(JOIN *join, List<Item_field> *out_args); @@ -2042,6 +2058,8 @@ public: bzero(this, sizeof(*this)); temp_pool_slot= MY_BIT_NONE; in_use= thd; + copy_blobs= true; + alias.set("", 0, &my_charset_bin); } ~Virtual_tmp_table() @@ -2085,11 +2103,11 @@ public: } /** - Add fields from a Column_definition list + Add fields from a Spvar_definition list @returns false - on success. @returns true - on error. */ - bool add(List<Column_definition> &field_list); + bool add(List<Spvar_definition> &field_list); /** Open a virtual table for read/write: @@ -2127,7 +2145,7 @@ public: */ inline TABLE * -create_virtual_tmp_table(THD *thd, List<Column_definition> &field_list) +create_virtual_tmp_table(THD *thd, List<Spvar_definition> &field_list) { Virtual_tmp_table *table; if (!(table= new(thd) Virtual_tmp_table(thd))) diff --git a/sql/sql_sequence.cc b/sql/sql_sequence.cc new file mode 100644 index 00000000000..eb9dfe012f7 --- /dev/null +++ b/sql/sql_sequence.cc @@ -0,0 +1,937 @@ +/* + Copyright (c) 2017, MariaDB Corporation, Alibaba Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "sql_class.h" +#include "sql_list.h" +#include "sql_sequence.h" +#include "ha_sequence.h" +#include "sql_base.h" +#include "sql_table.h" // write_bin_log +#include "transaction.h" +#include "lock.h" +#include "sql_acl.h" + +struct Field_definition +{ + const char *field_name; + uint length; + const Type_handler *type_handler; + LEX_CSTRING comment; + ulong flags; +}; + +/* + Structure for all SEQUENCE tables + + Note that the first field is named "next_val" to all us to have + NEXTVAL a reserved word that will on access be changed to + NEXTVAL(sequence_table). For this to work, the table can't have + a column named NEXTVAL. +*/ + +#define FL (NOT_NULL_FLAG | NO_DEFAULT_VALUE_FLAG) + +static Field_definition sequence_structure[]= +{ + {"next_not_cached_value", 21, &type_handler_longlong, + {STRING_WITH_LEN("")}, FL}, + {"minimum_value", 21, &type_handler_longlong, STRING_WITH_LEN(""), FL}, + {"maximum_value", 21, &type_handler_longlong, STRING_WITH_LEN(""), FL}, + {"start_value", 21, &type_handler_longlong, {STRING_WITH_LEN("start value when sequences is created or value if RESTART is used")}, FL}, + {"increment", 21, &type_handler_longlong, + {C_STRING_WITH_LEN("increment value")}, FL}, + {"cache_size", 21, &type_handler_longlong, STRING_WITH_LEN(""), + FL | UNSIGNED_FLAG}, + {"cycle_option", 1, &type_handler_tiny, {STRING_WITH_LEN("0 if no cycles are allowed, 1 if the sequence should begin a new cycle when maximum_value is passed")}, + FL | UNSIGNED_FLAG }, + {"cycle_count", 21, &type_handler_longlong, + {STRING_WITH_LEN("How many cycles have been done")}, FL}, + {NULL, 0, &type_handler_longlong, {STRING_WITH_LEN("")}, 0} +}; + +#undef FL + + +#define MAX_AUTO_INCREMENT_VALUE 65535 + +/* + Check whether sequence values are valid. + Sets default values for fields that are not used, according to Oracle spec. + + RETURN VALUES + false valid + true invalid +*/ + +bool sequence_definition::check_and_adjust(bool set_reserved_until) +{ + longlong max_increment; + DBUG_ENTER("sequence_definition::check"); + + if (!(real_increment= increment)) + real_increment= global_system_variables.auto_increment_increment; + + /* + If min_value is not set, set it to LONGLONG_MIN or 1, depending on + increment + */ + if (!(used_fields & seq_field_used_min_value)) + min_value= real_increment < 0 ? LONGLONG_MIN+1 : 1; + + /* + If min_value is not set, set it to LONGLONG_MAX or -1, depending on + real_increment + */ + if (!(used_fields & seq_field_used_max_value)) + max_value= real_increment < 0 ? -1 : LONGLONG_MAX-1; + + if (!(used_fields & seq_field_used_start)) + { + /* Use min_value or max_value for start depending on real_increment */ + start= real_increment < 0 ? max_value : min_value; + } + + if (set_reserved_until) + reserved_until= start; + + adjust_values(reserved_until); + + /* To ensure that cache * real_increment will never overflow */ + max_increment= (real_increment ? + labs(real_increment) : + MAX_AUTO_INCREMENT_VALUE); + + if (max_value >= start && + max_value > min_value && + start >= min_value && + max_value != LONGLONG_MAX && + min_value != LONGLONG_MIN && + cache < (LONGLONG_MAX - max_increment) / max_increment && + ((real_increment > 0 && reserved_until >= min_value) || + (real_increment < 0 && reserved_until <= max_value))) + DBUG_RETURN(FALSE); + + DBUG_RETURN(TRUE); // Error +} + + +/* + Read sequence values from a table +*/ + +void sequence_definition::read_fields(TABLE *table) +{ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->read_set); + reserved_until= table->field[0]->val_int(); + min_value= table->field[1]->val_int(); + max_value= table->field[2]->val_int(); + start= table->field[3]->val_int(); + increment= table->field[4]->val_int(); + cache= table->field[5]->val_int(); + cycle= table->field[6]->val_int(); + round= table->field[7]->val_int(); + dbug_tmp_restore_column_map(table->read_set, old_map); + used_fields= ~(uint) 0; + print_dbug(); +} + + +/* + Store sequence into a table row +*/ + +void sequence_definition::store_fields(TABLE *table) +{ + my_bitmap_map *old_map= dbug_tmp_use_all_columns(table, table->write_set); + + /* zero possible delete markers & null bits */ + memcpy(table->record[0], table->s->default_values, table->s->null_bytes); + table->field[0]->store(reserved_until, 0); + table->field[1]->store(min_value, 0); + table->field[2]->store(max_value, 0); + table->field[3]->store(start, 0); + table->field[4]->store(increment, 0); + table->field[5]->store(cache, 0); + table->field[6]->store((longlong) cycle != 0, 0); + table->field[7]->store((longlong) round, 1); + + dbug_tmp_restore_column_map(table->write_set, old_map); + print_dbug(); +} + + +/* + Check the sequence fields through seq_fields when createing a sequence. + + RETURN VALUES + false Success + true Failure +*/ + +bool check_sequence_fields(LEX *lex, List<Create_field> *fields) +{ + Create_field *field; + List_iterator_fast<Create_field> it(*fields); + uint field_count; + uint field_no; + const char *reason; + DBUG_ENTER("check_sequence_fields"); + + field_count= fields->elements; + if (field_count != array_elements(sequence_structure)-1) + { + reason= "Wrong number of columns"; + goto err; + } + if (lex->alter_info.key_list.elements > 0) + { + reason= "Sequence tables cannot have any keys"; + goto err; + } + + for (field_no= 0; (field= it++); field_no++) + { + Field_definition *field_def= &sequence_structure[field_no]; + if (my_strcasecmp(system_charset_info, field_def->field_name, + field->field_name.str) || + field->flags != field_def->flags || + field->type_handler() != field_def->type_handler) + { + reason= field->field_name.str; + goto err; + } + } + DBUG_RETURN(FALSE); + +err: + my_error(ER_SEQUENCE_INVALID_TABLE_STRUCTURE, MYF(0), + lex->select_lex.table_list.first->db, + lex->select_lex.table_list.first->table_name, reason); + DBUG_RETURN(TRUE); +} + + +/* + Create the fields for a SEQUENCE TABLE + + RETURN VALUES + false Success + true Failure (out of memory) +*/ + +bool prepare_sequence_fields(THD *thd, List<Create_field> *fields) +{ + Field_definition *field_info; + DBUG_ENTER("prepare_sequence_fields"); + + for (field_info= sequence_structure; field_info->field_name ; field_info++) + { + Create_field *new_field; + LEX_CSTRING field_name= {field_info->field_name, + strlen(field_info->field_name)}; + + if (unlikely(!(new_field= new Create_field()))) + DBUG_RETURN(TRUE); /* purify inspected */ + + new_field->field_name= field_name; + new_field->set_handler(field_info->type_handler); + new_field->length= field_info->length; + new_field->char_length= field_info->length; + new_field->comment= field_info->comment; + new_field->flags= field_info->flags; + if (unlikely(fields->push_back(new_field))) + DBUG_RETURN(TRUE); /* purify inspected */ + } + DBUG_RETURN(FALSE); +} + +/* + Initialize the sequence table record as part of CREATE SEQUENCE + + Store one row with sequence information. + + RETURN VALUES + false Success + true Failure. Error reported. + + NOTES + This function is called as part of CREATE SEQUENCE. When called + there are now active transactions and no open tables. + There is also a MDL lock on the table. +*/ + +bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *table_list) +{ + int error; + TABLE *table; + TABLE_LIST::enum_open_strategy save_open_strategy; + Reprepare_observer *save_reprepare_observer; + sequence_definition *seq= lex->create_info.seq_create_info; + bool temporary_table= table_list->table != 0; + DBUG_ENTER("sequence_insert"); + + /* If not temporary table */ + if (!temporary_table) + { + /* Table was locked as part of create table. Free it but keep MDL locks */ + close_thread_tables(thd); + table_list->lock_type= TL_WRITE_DEFAULT; + table_list->updating= 1; + /* + The FOR CREATE flag is needed to ensure that ha_open() doesn't try to + read the not yet existing row in the sequence table + */ + thd->open_options|= HA_OPEN_FOR_CREATE; + save_open_strategy= table_list->open_strategy; + /* + We have to reset the reprepare observer to be able to open the + table under prepared statements. + */ + save_reprepare_observer= thd->m_reprepare_observer; + thd->m_reprepare_observer= 0; + table_list->open_strategy= TABLE_LIST::OPEN_IF_EXISTS; + table_list->open_type= OT_BASE_ONLY; + error= open_and_lock_tables(thd, table_list, FALSE, + MYSQL_LOCK_IGNORE_TIMEOUT | + MYSQL_OPEN_HAS_MDL_LOCK); + table_list->open_strategy= save_open_strategy; + thd->open_options&= ~HA_OPEN_FOR_CREATE; + thd->m_reprepare_observer= save_reprepare_observer; + if (error) + DBUG_RETURN(TRUE); /* purify inspected */ + } + + table= table_list->table; + + /* + seq is 0 if sequence was created with CREATE TABLE instead of + CREATE SEQUENCE + */ + if (!seq) + { + if (!(seq= new (thd->mem_root) sequence_definition)) + DBUG_RETURN(TRUE); // EOM + } + + seq->reserved_until= seq->start; + error= seq->write_initial_sequence(table); + + trans_commit_stmt(thd); + trans_commit_implicit(thd); + if (!temporary_table) + close_thread_tables(thd); + thd->mdl_context.release_transactional_locks(); + DBUG_RETURN(error); +} + + +/* Create a SQUENCE object */ + +SEQUENCE::SEQUENCE() :all_values_used(0), initialized(SEQ_UNINTIALIZED) +{ + mysql_rwlock_init(key_LOCK_SEQUENCE, &mutex); +} + +SEQUENCE::~SEQUENCE() +{ + mysql_rwlock_destroy(&mutex); +} + +/* + The following functions is to ensure that we when reserve new values + trough sequence object sequence we have only one writer at at time. + A sequence table can have many readers (trough normal SELECT's). + + We mark that we have a write lock in the table object so that + ha_sequence::ha_write() can check if we have a lock. If already locked, then + ha_write() knows that we are running a sequence operation. If not, then + ha_write() knows that it's an INSERT. +*/ + +void SEQUENCE::write_lock(TABLE *table) +{ + DBUG_ASSERT(((ha_sequence*) table->file)->is_locked() == 0); + mysql_rwlock_wrlock(&mutex); + ((ha_sequence*) table->file)->write_lock(); +} +void SEQUENCE::write_unlock(TABLE *table) +{ + ((ha_sequence*) table->file)->unlock(); + mysql_rwlock_unlock(&mutex); +} +void SEQUENCE::read_lock(TABLE *table) +{ + if (!((ha_sequence*) table->file)->is_locked()) + mysql_rwlock_rdlock(&mutex); +} +void SEQUENCE::read_unlock(TABLE *table) +{ + if (!((ha_sequence*) table->file)->is_locked()) + mysql_rwlock_unlock(&mutex); +} + +/** + Read values from the sequence tables to table_share->sequence. + This is called from ha_open() when the table is not yet locked +*/ + +int SEQUENCE::read_initial_values(TABLE *table) +{ + int error= 0; + enum thr_lock_type save_lock_type; + MDL_request mdl_request; // Empty constructor! + DBUG_ENTER("SEQUENCE::read_initial_values"); + + if (likely(initialized != SEQ_UNINTIALIZED)) + DBUG_RETURN(0); + write_lock(table); + if (likely(initialized == SEQ_UNINTIALIZED)) + { + MYSQL_LOCK *lock; + bool mdl_lock_used= 0; + THD *thd= table->in_use; + bool has_active_transaction= !thd->transaction.stmt.is_empty(); + /* + There is already a mdl_ticket for this table. However, for list_fields + the MDL lock is of type MDL_SHARED_HIGH_PRIO which is not usable + for doing a able lock. Get a proper read lock to solve this. + */ + if (table->mdl_ticket == 0) + { + MDL_request_list mdl_requests; + mdl_lock_used= 1; + /* + This happens if first request is SHOW CREATE TABLE or LIST FIELDS + where we don't have a mdl lock on the table + */ + + mdl_request.init(MDL_key::TABLE, + table->s->db.str, + table->s->table_name.str, + MDL_SHARED_READ, MDL_EXPLICIT); + mdl_requests.push_front(&mdl_request); + if (thd->mdl_context.acquire_locks(&mdl_requests, + thd->variables.lock_wait_timeout)) + DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT); + } + save_lock_type= table->reginfo.lock_type; + table->reginfo.lock_type= TL_READ; + if (!(lock= mysql_lock_tables(thd, &table, 1, + MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY))) + { + if (mdl_lock_used) + thd->mdl_context.release_lock(mdl_request.ticket); + DBUG_RETURN(HA_ERR_LOCK_WAIT_TIMEOUT); + } + DBUG_ASSERT(table->reginfo.lock_type == TL_READ); + if (!(error= read_stored_values(table))) + initialized= SEQ_READY_TO_USE; + mysql_unlock_tables(thd, lock, 0); + if (mdl_lock_used) + thd->mdl_context.release_lock(mdl_request.ticket); + + /* Reset value to default */ + table->reginfo.lock_type= save_lock_type; + /* + Doing mysql_lock_tables() may have started a read only transaction. + If that happend, it's better that we commit it now, as a lot of + code assumes that there is no active stmt transaction directly after + open_tables() + */ + if (!has_active_transaction && !thd->transaction.stmt.is_empty()) + trans_commit_stmt(thd); + } + write_unlock(table); + DBUG_RETURN(error); +} + + +/* + Do the actiual reading of data from sequence table and + update values in the sequence object. + + Called once from when table is opened +*/ + +int SEQUENCE::read_stored_values(TABLE *table) +{ + int error; + my_bitmap_map *save_read_set; + DBUG_ENTER("SEQUENCE::read_stored_values"); + + save_read_set= tmp_use_all_columns(table, table->read_set); + error= table->file->ha_read_first_row(table->record[0], MAX_KEY); + tmp_restore_column_map(table->read_set, save_read_set); + + if (error) + { + table->file->print_error(error, MYF(0)); + DBUG_RETURN(error); + } + read_fields(table); + adjust_values(reserved_until); + + all_values_used= 0; + DBUG_RETURN(0); +} + + +/* + Adjust values after reading a the stored state +*/ + +void sequence_definition::adjust_values(longlong next_value) +{ + next_free_value= next_value; + if (!(real_increment= increment)) + { + longlong offset= 0; + longlong off, to_add; + /* Use auto_increment_increment and auto_increment_offset */ + + if ((real_increment= global_system_variables.auto_increment_increment) + != 1) + offset= global_system_variables.auto_increment_offset; + + /* + Ensure that next_free_value has the right offset, so that we + can generate a serie by just adding real_increment. + */ + off= next_free_value % real_increment; + if (off < 0) + off+= real_increment; + to_add= (real_increment + offset - off) % real_increment; + + /* + Check if add will make next_free_value bigger than max_value, + taken into account that next_free_value or max_value addition + may overflow + */ + if (next_free_value > max_value - to_add || + next_free_value + to_add > max_value) + next_free_value= max_value+1; + else + { + next_free_value+= to_add; + DBUG_ASSERT(next_free_value % real_increment == offset && + next_free_value >= reserved_until); + } + } +} + + +/** + Write initial sequence information for CREATE and ALTER to sequence table +*/ + +int sequence_definition::write_initial_sequence(TABLE *table) +{ + int error; + THD *thd= table->in_use; + MY_BITMAP *save_write_set; + + store_fields(table); + /* Store the sequence values in table share */ + table->s->sequence->copy(this); + /* + Sequence values will be replicated as a statement + like 'create sequence'. So disable binary log temporarily + */ + tmp_disable_binlog(thd); + save_write_set= table->write_set; + table->write_set= &table->s->all_set; + table->s->sequence->initialized= SEQUENCE::SEQ_IN_PREPARE; + error= table->file->ha_write_row(table->record[0]); + table->s->sequence->initialized= SEQUENCE::SEQ_UNINTIALIZED; + reenable_binlog(thd); + table->write_set= save_write_set; + if (error) + table->file->print_error(error, MYF(0)); + else + { + /* + Sequence structure is up to date and table has one row, + sequence is now usable + */ + table->s->sequence->initialized= SEQUENCE::SEQ_READY_TO_USE; + } + return error; +} + + +/** + Store current sequence values into the sequence table +*/ + +int sequence_definition::write(TABLE *table, bool all_fields) +{ + int error; + MY_BITMAP *save_rpl_write_set, *save_write_set, *save_read_set; + DBUG_ASSERT(((ha_sequence*) table->file)->is_locked()); + + save_rpl_write_set= table->rpl_write_set; + if (likely(!all_fields)) + { + /* Only write next_value and round to binary log */ + table->rpl_write_set= &table->def_rpl_write_set; + bitmap_clear_all(table->rpl_write_set); + bitmap_set_bit(table->rpl_write_set, NEXT_FIELD_NO); + bitmap_set_bit(table->rpl_write_set, ROUND_FIELD_NO); + } + else + table->rpl_write_set= &table->s->all_set; + + /* + The following is needed to fix comparison of rows in + ha_update_first_row() for InnoDB + */ + memcpy(table->record[1],table->s->default_values, table->s->reclength); + + /* Update table */ + save_write_set= table->write_set; + save_read_set= table->read_set; + table->read_set= table->write_set= &table->s->all_set; + table->file->column_bitmaps_signal(); + store_fields(table); + if ((error= table->file->ha_write_row(table->record[0]))) + table->file->print_error(error, MYF(0)); + table->rpl_write_set= save_rpl_write_set; + table->read_set= save_read_set; + table->write_set= save_write_set; + table->file->column_bitmaps_signal(); + return error; +} + + +/** + Get next value for sequence + + @param in table Sequence table + @param in second_round + 1 if recursive call (out of values once) + @param out error Set this to <> 0 in case of error + push_warning_printf(WARN_LEVEL_WARN) has been called + + + @retval 0 Next number or error. Check error variable + # Next sequence number + + NOTES: + Return next_free_value and increment next_free_value to next allowed + value or reserved_value if out of range + if next_free_value >= reserved_value reserve a new range by writing + a record to the sequence table. + + The state of the variables: + next_free_value contains next value to use. It may be + bigger than max_value or less than min_value if end of sequence. + reserved_until contains the last value written to the file. All + values up to this one can be used. + If next_free_value >= reserved_until we have to reserve new + values from the sequence. +*/ + +longlong SEQUENCE::next_value(TABLE *table, bool second_round, int *error) +{ + longlong res_value, org_reserved_until, add_to; + bool out_of_values; + DBUG_ENTER("SEQUENCE::next_value"); + + *error= 0; + if (!second_round) + write_lock(table); + + res_value= next_free_value; + next_free_value= increment_value(next_free_value); + + if ((real_increment > 0 && res_value < reserved_until) || + (real_increment < 0 && res_value > reserved_until)) + { + write_unlock(table); + DBUG_RETURN(res_value); + } + + if (all_values_used) + goto err; + + org_reserved_until= reserved_until; + + /* + Out of cached values, reserve 'cache' new ones + The cache value is checked on insert so the following can't + overflow + */ + add_to= cache ? real_increment * cache : real_increment; + out_of_values= 0; + + if (real_increment > 0) + { + if (reserved_until + add_to > max_value || + reserved_until > max_value - add_to) + { + reserved_until= max_value + 1; + out_of_values= res_value >= reserved_until; + } + else + reserved_until+= add_to; + } + else + { + if (reserved_until + add_to < min_value || + reserved_until < min_value - add_to) + { + reserved_until= min_value - 1; + out_of_values= res_value <= reserved_until; + } + else + reserved_until+= add_to; + } + if (out_of_values) + { + if (!cycle || second_round) + goto err; + round++; + reserved_until= real_increment >0 ? min_value : max_value; + adjust_values(reserved_until); // Fix next_free_value + /* + We have to do everything again to ensure that the given range was + not empty, which could happen if increment == 0 + */ + DBUG_RETURN(next_value(table, 1, error)); + } + + if ((*error= write(table, 0))) + { + reserved_until= org_reserved_until; + next_free_value= res_value; + } + + write_unlock(table); + DBUG_RETURN(res_value); + +err: + write_unlock(table); + my_error(ER_SEQUENCE_RUN_OUT, MYF(0), table->s->db.str, + table->s->table_name.str); + *error= ER_SEQUENCE_RUN_OUT; + all_values_used= 1; + DBUG_RETURN(0); +} + + +/* + The following functions is to detect if a table has been dropped + and re-created since last call to PREVIOUS VALUE. + + This is needed as we don't delete dropped sequences from THD->sequence + for DROP TABLE. +*/ + +bool SEQUENCE_LAST_VALUE::check_version(TABLE *table) +{ + DBUG_ASSERT(table->s->tabledef_version.length == MY_UUID_SIZE); + return memcmp(table->s->tabledef_version.str, table_version, + MY_UUID_SIZE) != 0; +} + +void SEQUENCE_LAST_VALUE::set_version(TABLE *table) +{ + memcpy(table_version, table->s->tabledef_version.str, MY_UUID_SIZE); +} + +/** + Set the next value for sequence + + @param in table Sequence table + @param in next_val Next free value + @param in next_round Round for 'next_value' (in cace of cycles) + @param in is_used 1 if next_val is already used + + @retval 0 ok, value adjusted + 1 value was less than current value or + error when storing value + + @comment + A new value is set only if "nextval,next_round" is less than + "next_free_value,round". This is needed as in replication + setvalue() calls may come out to the slave out-of-order. + Storing only the highest value ensures that sequence object will always + contain the highest used value when the slave is promoted to a master. +*/ + +bool SEQUENCE::set_value(TABLE *table, longlong next_val, ulonglong next_round, + bool is_used) +{ + bool error= 1; + bool needs_to_be_stored= 0; + longlong org_reserved_until= reserved_until; + longlong org_next_free_value= next_free_value; + ulonglong org_round= round; + DBUG_ENTER("SEQUENCE::set_value"); + + write_lock(table); + if (is_used) + next_val= increment_value(next_val); + + if (round > next_round) + goto end; + if (round == next_round) + { + if (real_increment > 0 ? + next_val < next_free_value : + next_val > next_free_value) + goto end; + if (next_val == next_free_value) + { + error= 0; + goto end; + } + } + else if (cycle == 0) + goto end; // round < next_round && no cycles + else + needs_to_be_stored= 1; + + round= next_round; + adjust_values(next_val); + if ((real_increment > 0 ? + next_free_value > reserved_until : + next_free_value < reserved_until) || + needs_to_be_stored) + { + reserved_until= next_free_value; + if (write(table, 0)) + { + reserved_until= org_reserved_until; + next_free_value= org_next_free_value; + round= org_round; + goto end; + } + } + error= 0; + +end: + write_unlock(table); + DBUG_RETURN(error); +} + + +bool Sql_cmd_alter_sequence::execute(THD *thd) +{ + int error= 0; + int trapped_errors= 0; + LEX *lex= thd->lex; + TABLE_LIST *first_table= lex->query_tables; + TABLE *table; + sequence_definition *new_seq= lex->create_info.seq_create_info; + SEQUENCE *seq; + No_such_table_error_handler no_such_table_handler; + DBUG_ENTER("Sql_cmd_alter_sequence::execute"); + + if (check_access(thd, ALTER_ACL, first_table->db, + &first_table->grant.privilege, + &first_table->grant.m_internal, + 0, 0)) + DBUG_RETURN(TRUE); /* purecov: inspected */ + + if (check_grant(thd, ALTER_ACL, first_table, FALSE, UINT_MAX, FALSE)) + DBUG_RETURN(TRUE); /* purecov: inspected */ + + if (lex->check_exists) + thd->push_internal_handler(&no_such_table_handler); + error= open_and_lock_tables(thd, first_table, FALSE, 0); + if (lex->check_exists) + { + trapped_errors= no_such_table_handler.safely_trapped_errors(); + thd->pop_internal_handler(); + } + if (error) + { + if (trapped_errors) + { + StringBuffer<FN_REFLEN> tbl_name; + tbl_name.append(first_table->db); + tbl_name.append('.'); + tbl_name.append(first_table->table_name); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_UNKNOWN_SEQUENCES, + ER_THD(thd, ER_UNKNOWN_SEQUENCES), + tbl_name.c_ptr_safe()); + my_ok(thd); + DBUG_RETURN(FALSE); + } + DBUG_RETURN(TRUE); + } + + table= first_table->table; + seq= table->s->sequence; + new_seq->reserved_until= seq->reserved_until; + + /* Copy from old sequence those fields that the user didn't specified */ + if (!(new_seq->used_fields & seq_field_used_increment)) + new_seq->increment= seq->increment; + if (!(new_seq->used_fields & seq_field_used_min_value)) + new_seq->min_value= seq->min_value; + if (!(new_seq->used_fields & seq_field_used_max_value)) + new_seq->max_value= seq->max_value; + if (!(new_seq->used_fields & seq_field_used_start)) + new_seq->start= seq->start; + if (!(new_seq->used_fields & seq_field_used_cache)) + new_seq->cache= seq->cache; + if (!(new_seq->used_fields & seq_field_used_cycle)) + new_seq->cycle= seq->cycle; + + /* If we should restart from a new value */ + if (new_seq->used_fields & seq_field_used_restart) + { + if (!(new_seq->used_fields & seq_field_used_restart_value)) + new_seq->restart= new_seq->start; + new_seq->reserved_until= new_seq->restart; + } + + /* Let check_and_adjust think all fields are used */ + new_seq->used_fields= ~0; + if (new_seq->check_and_adjust(0)) + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), + first_table->db, + first_table->table_name); + error= 1; + goto end; + } + + table->s->sequence->write_lock(table); + if (!(error= new_seq->write(table, 1))) + { + /* Store the sequence values in table share */ + table->s->sequence->copy(new_seq); + } + else + table->file->print_error(error, MYF(0)); + table->s->sequence->write_unlock(table); + if (trans_commit_stmt(thd)) + error= 1; + if (trans_commit_implicit(thd)) + error= 1; + if (!error) + error= write_bin_log(thd, 1, thd->query(), thd->query_length()); + if (!error) + my_ok(thd); + +end: + DBUG_RETURN(error); +} diff --git a/sql/sql_sequence.h b/sql/sql_sequence.h new file mode 100644 index 00000000000..b61e4ffe40d --- /dev/null +++ b/sql/sql_sequence.h @@ -0,0 +1,167 @@ +/* Copyright (c) 2017, MariaDB corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef SQL_SEQUENCE_INCLUDED +#define SQL_SEQUENCE_INCLUDED + +#define seq_field_used_min_value 1 +#define seq_field_used_max_value 2 +#define seq_field_used_start 4 +#define seq_field_used_increment 8 +#define seq_field_used_cache 16 +#define seq_field_used_cycle 32 +#define seq_field_used_restart 64 +#define seq_field_used_restart_value 128 + +/* Field position in sequence table for some fields we refer to directly */ +#define NEXT_FIELD_NO 0 +#define MIN_VALUE_FIELD_NO 1 +#define ROUND_FIELD_NO 7 + +/** + sequence_definition is used when defining a sequence as part of create +*/ + +class sequence_definition :public Sql_alloc +{ +public: + sequence_definition(): + min_value(1), max_value(LONGLONG_MAX-1), start(1), increment(1), + cache(1000), round(0), restart(0), cycle(0), used_fields(0) + {} + longlong reserved_until; + longlong min_value; + longlong max_value; + longlong start; + longlong increment; + longlong cache; + ulonglong round; + longlong restart; // alter sequence restart value + bool cycle; + uint used_fields; // Which fields where used in CREATE + + bool check_and_adjust(bool set_reserved_until); + void store_fields(TABLE *table); + void read_fields(TABLE *table); + int write_initial_sequence(TABLE *table); + int write(TABLE *table, bool all_fields); + /* This must be called after sequence data has been updated */ + void adjust_values(longlong next_value); + inline void print_dbug() + { + DBUG_PRINT("sequence", ("reserved: %lld start: %lld increment: %lld min_value: %lld max_value: %lld cache: %lld round: %lld", + reserved_until, start, increment, min_value, + max_value, cache, round)); + } +protected: + /* + The following values are the values from sequence_definition + merged with global auto_increment_offset and auto_increment_increment + */ + longlong real_increment; + longlong next_free_value; +}; + +/** + SEQUENCE is in charge of managing the sequence values. + It's also responsible to generate new values and updating the sequence + table (engine=SQL_SEQUENCE) trough it's specialized handler interface. + + If increment is 0 then the sequence will be be using + auto_increment_increment and auto_increment_offset variables, just like + AUTO_INCREMENT is using. +*/ + +class SEQUENCE :public sequence_definition +{ +public: + enum seq_init { SEQ_UNINTIALIZED, SEQ_IN_PREPARE, SEQ_IN_ALTER, + SEQ_READY_TO_USE }; + SEQUENCE(); + ~SEQUENCE(); + int read_initial_values(TABLE *table); + int read_stored_values(TABLE *table); + void write_lock(TABLE *table); + void write_unlock(TABLE *table); + void read_lock(TABLE *table); + void read_unlock(TABLE *table); + void copy(sequence_definition *seq) + { + sequence_definition::operator= (*seq); + adjust_values(reserved_until); + all_values_used= 0; + } + longlong next_value(TABLE *table, bool second_round, int *error); + bool set_value(TABLE *table, longlong next_value, ulonglong round_arg, + bool is_used); + longlong increment_value(longlong value) + { + if (real_increment > 0) + { + if (value + real_increment > max_value || + value > max_value - real_increment) + value= max_value + 1; + else + value+= real_increment; + } + else + { + if (value + real_increment < min_value || + value < min_value - real_increment) + value= min_value - 1; + else + value+= real_increment; + } + return value; + } + + bool all_values_used; + seq_init initialized; + +private: + mysql_rwlock_t mutex; +}; + + +/** + Class to cache last value of NEXT VALUE from the sequence +*/ + +class SEQUENCE_LAST_VALUE +{ +public: + SEQUENCE_LAST_VALUE(uchar *key_arg, uint length_arg) + :key(key_arg), length(length_arg) + {} + ~SEQUENCE_LAST_VALUE() + { my_free((void*) key); } + /* Returns 1 if table hasn't been dropped or re-created */ + bool check_version(TABLE *table); + void set_version(TABLE *table); + + const uchar *key; + uint length; + bool null_value; + longlong value; + uchar table_version[MY_UUID_SIZE]; +}; + + +class Create_field; +extern bool prepare_sequence_fields(THD *thd, List<Create_field> *fields); +extern bool check_sequence_fields(LEX *lex, List<Create_field> *fields); +extern bool sequence_insert(THD *thd, LEX *lex, TABLE_LIST *table_list); +#endif /* SQL_SEQUENCE_INCLUDED */ diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index ca721b9e366..5e826dff2e0 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -64,7 +64,7 @@ static int insert_server_record_into_cache(FOREIGN_SERVER *server); static FOREIGN_SERVER * prepare_server_struct_for_insert(LEX_SERVER_OPTIONS *server_options); /* drop functions */ -static int delete_server_record(TABLE *table, LEX_STRING *name); +static int delete_server_record(TABLE *table, LEX_CSTRING *name); static int delete_server_record_in_cache(LEX_SERVER_OPTIONS *server_options); /* update functions */ @@ -939,7 +939,7 @@ end: */ static int -delete_server_record(TABLE *table, LEX_STRING *name) +delete_server_record(TABLE *table, LEX_CSTRING *name) { int error; DBUG_ENTER("delete_server_record"); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 97fa231a13a..4dcc519029c 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -58,10 +58,11 @@ #include "lock.h" // MYSQL_OPEN_IGNORE_FLUSH #include "debug_sync.h" #include "keycaches.h" - +#include "ha_sequence.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" #endif + enum enum_i_s_events_fields { ISE_EVENT_CATALOG= 0, @@ -93,17 +94,17 @@ enum enum_i_s_events_fields #define USERNAME_WITH_HOST_CHAR_LENGTH (USERNAME_CHAR_LENGTH + HOSTNAME_LENGTH + 2) -static const LEX_STRING trg_action_time_type_names[]= +static const LEX_CSTRING trg_action_time_type_names[]= { - { C_STRING_WITH_LEN("BEFORE") }, - { C_STRING_WITH_LEN("AFTER") } + { STRING_WITH_LEN("BEFORE") }, + { STRING_WITH_LEN("AFTER") } }; -static const LEX_STRING trg_event_type_names[]= +static const LEX_CSTRING trg_event_type_names[]= { - { C_STRING_WITH_LEN("INSERT") }, - { C_STRING_WITH_LEN("UPDATE") }, - { C_STRING_WITH_LEN("DELETE") } + { STRING_WITH_LEN("INSERT") }, + { STRING_WITH_LEN("UPDATE") }, + { STRING_WITH_LEN("DELETE") } }; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -130,8 +131,10 @@ static void get_cs_converted_string_value(THD *thd, #endif static int show_create_view(THD *thd, TABLE_LIST *table, String *buff); +static int show_create_sequence(THD *thd, TABLE_LIST *table_list, + String *packet); -static const LEX_STRING *view_algorithm(TABLE_LIST *table); +static const LEX_CSTRING *view_algorithm(TABLE_LIST *table); bool get_lookup_field_values(THD *, COND *, TABLE_LIST *, LOOKUP_FIELD_VALUES *); @@ -145,13 +148,13 @@ static int make_version_string(char *buf, int buf_length, uint version) } -static const LEX_STRING maturity_name[]={ - { C_STRING_WITH_LEN("Unknown") }, - { C_STRING_WITH_LEN("Experimental") }, - { C_STRING_WITH_LEN("Alpha") }, - { C_STRING_WITH_LEN("Beta") }, - { C_STRING_WITH_LEN("Gamma") }, - { C_STRING_WITH_LEN("Stable") }}; +static const LEX_CSTRING maturity_name[]={ + { STRING_WITH_LEN("Unknown") }, + { STRING_WITH_LEN("Experimental") }, + { STRING_WITH_LEN("Alpha") }, + { STRING_WITH_LEN("Beta") }, + { STRING_WITH_LEN("Gamma") }, + { STRING_WITH_LEN("Stable") }}; static my_bool show_plugins(THD *thd, plugin_ref plugin, @@ -311,7 +314,7 @@ int fill_all_plugins(THD *thd, TABLE_LIST *tables, COND *cond) for (uint i=0; i < (uint) dirp->number_of_files; i++) { FILEINFO *file= dirp->dir_entry+i; - LEX_STRING dl= { file->name, strlen(file->name) }; + LEX_CSTRING dl= { file->name, strlen(file->name) }; const char *dlend= dl.str + dl.length; const size_t so_ext_len= sizeof(SO_EXT) - 1; @@ -384,8 +387,8 @@ exit: static int get_geometry_column_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, - LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { CHARSET_INFO *cs= system_charset_info; TABLE *show_table; @@ -439,7 +442,8 @@ static int get_geometry_column_record(THD *thd, TABLE_LIST *tables, /*G_TABLE_NAME*/ table->field[6]->store(table_name->str, table_name->length, cs); /*G_GEOMETRY_COLUMN*/ - table->field[7]->store(field->field_name, strlen(field->field_name), cs); + table->field[7]->store(field->field_name.str, field->field_name.length, + cs); /*STORAGE_TYPE*/ table->field[8]->store(1LL, TRUE); /*Always 1 (binary implementation)*/ /*GEOMETRY_TYPE*/ @@ -664,7 +668,7 @@ static bool skip_ignored_dir_check= TRUE; bool ignore_db_dirs_init() { - return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_STRING *), + return my_init_dynamic_array(&ignore_db_dirs_array, sizeof(LEX_CSTRING *), 0, 0, MYF(0)); } @@ -684,7 +688,7 @@ static uchar * db_dirs_hash_get_key(const uchar *data, size_t *len_ret, my_bool __attribute__((unused))) { - LEX_STRING *e= (LEX_STRING *) data; + LEX_CSTRING *e= (LEX_CSTRING *) data; *len_ret= e->length; return (uchar *) e->str; @@ -705,7 +709,7 @@ db_dirs_hash_get_key(const uchar *data, size_t *len_ret, bool push_ignored_db_dir(char *path) { - LEX_STRING *new_elt; + LEX_CSTRING *new_elt; char *new_elt_buffer; size_t path_len= strlen(path); @@ -714,7 +718,7 @@ push_ignored_db_dir(char *path) // No need to normalize, it's only a directory name, not a path. if (!my_multi_malloc(0, - &new_elt, sizeof(LEX_STRING), + &new_elt, sizeof(LEX_CSTRING), &new_elt_buffer, path_len + 1, NullS)) return true; @@ -736,8 +740,8 @@ push_ignored_db_dir(char *path) void ignore_db_dirs_reset() { - LEX_STRING **elt; - while (NULL!= (elt= (LEX_STRING **) pop_dynamic(&ignore_db_dirs_array))) + LEX_CSTRING **elt; + while (NULL!= (elt= (LEX_CSTRING **) pop_dynamic(&ignore_db_dirs_array))) if (elt && *elt) my_free(*elt); } @@ -787,8 +791,7 @@ ignore_db_dirs_process_additions() ulong i; size_t len; char *ptr; - LEX_STRING *dir; - + LEX_CSTRING *dir; skip_ignored_dir_check= TRUE; @@ -940,8 +943,8 @@ enum find_files_result { static find_files_result -find_files(THD *thd, Dynamic_array<LEX_STRING*> *files, LEX_STRING *db, - const char *path, const LEX_STRING *wild) +find_files(THD *thd, Dynamic_array<LEX_CSTRING*> *files, LEX_CSTRING *db, + const char *path, const LEX_CSTRING *wild) { MY_DIR *dirp; Discovered_table_list tl(thd, files, wild); @@ -1170,12 +1173,19 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, } /* TODO: add environment variables show when it become possible */ - if (thd->lex->only_view && !table_list->view) + if (thd->lex->table_type == TABLE_TYPE_VIEW && !table_list->view) { my_error(ER_WRONG_OBJECT, MYF(0), table_list->db, table_list->table_name, "VIEW"); goto exit; } + else if (thd->lex->table_type == TABLE_TYPE_SEQUENCE && + table_list->table->s->table_type != TABLE_TYPE_SEQUENCE) + { + my_error(ER_NOT_SEQUENCE, MYF(0), + table_list->db, table_list->table_name); + goto exit; + } buffer->length(0); @@ -1184,6 +1194,8 @@ mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, if ((table_list->view ? show_create_view(thd, table_list, buffer) : + thd->lex->table_type == TABLE_TYPE_SEQUENCE ? + show_create_sequence(thd, table_list, buffer) : show_create_table(thd, table_list, buffer, NULL, WITHOUT_DB_NAME))) goto exit; @@ -1317,8 +1329,8 @@ void mysqld_show_create_db_get_fields(THD *thd, List<Item> *field_list) } -bool mysqld_show_create_db(THD *thd, LEX_STRING *dbname, - LEX_STRING *orig_dbname, +bool mysqld_show_create_db(THD *thd, LEX_CSTRING *dbname, + LEX_CSTRING *orig_dbname, const DDL_options_st &options) { char buff[2048]; @@ -1431,7 +1443,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild) for (ptr=table->field ; (field= *ptr); ptr++) { if (!wild || !wild[0] || - !wild_case_compare(system_charset_info, field->field_name,wild)) + !wild_case_compare(system_charset_info, field->field_name.str,wild)) { if (table_list->view) field_list.push_back(new (mem_root) @@ -1767,6 +1779,179 @@ static void append_create_options(THD *thd, String *packet, packet->append(STRING_WITH_LEN(" */")); } +/** + Add table options to end of CREATE statement + + @param schema_table 1 if schema table + @param sequence 1 if sequence. If sequence, we flush out options + not relevant for sequences. +*/ + +static void add_table_options(THD *thd, TABLE *table, + Table_specification_st *create_info_arg, + bool schema_table, bool sequence, + String *packet) +{ + sql_mode_t sql_mode= thd->variables.sql_mode; + TABLE_SHARE *share= table->s; + handlerton *hton; + HA_CREATE_INFO create_info; + bool check_options= (!(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && + !create_info_arg); + +#ifdef WITH_PARTITION_STORAGE_ENGINE + if (table->part_info) + hton= table->part_info->default_engine_type; + else +#endif + hton= table->file->ht; + + bzero((char*) &create_info, sizeof(create_info)); + /* Allow update_create_info to update row type, page checksums and options */ + create_info.row_type= share->row_type; + create_info.page_checksum= share->page_checksum; + create_info.options= share->db_create_options; + table->file->update_create_info(&create_info); + + /* + IF check_create_info + THEN add ENGINE only if it was used when creating the table + */ + if (!create_info_arg || + (create_info_arg->used_fields & HA_CREATE_USED_ENGINE)) + { + LEX_CSTRING *engine_name= table->file->engine_name(); + + if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) + packet->append(STRING_WITH_LEN(" TYPE=")); + else + packet->append(STRING_WITH_LEN(" ENGINE=")); + + packet->append(engine_name->str, engine_name->length); + } + + if (sequence) + goto end_options; + + /* + Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column, + and NEXT_ID > 1 (the default). We must not print the clause + for engines that do not support this as it would break the + import of dumps, but as of this writing, the test for whether + AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=... + is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT)) + Because of that, we do not explicitly test for the feature, + but may extrapolate its existence from that of an AUTO_INCREMENT column. + */ + + if (create_info.auto_increment_value > 1) + { + packet->append(STRING_WITH_LEN(" AUTO_INCREMENT=")); + packet->append_ulonglong(create_info.auto_increment_value); + } + + if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) && + share->table_type != TABLE_TYPE_SEQUENCE) + { + /* + IF check_create_info + THEN add DEFAULT CHARSET only if it was used when creating the table + */ + if (!create_info_arg || + (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET)) + { + packet->append(STRING_WITH_LEN(" DEFAULT CHARSET=")); + packet->append(share->table_charset->csname); + if (!(share->table_charset->state & MY_CS_PRIMARY)) + { + packet->append(STRING_WITH_LEN(" COLLATE=")); + packet->append(table->s->table_charset->name); + } + } + } + + if (share->min_rows) + { + packet->append(STRING_WITH_LEN(" MIN_ROWS=")); + packet->append_ulonglong(share->min_rows); + } + + if (share->max_rows && !schema_table && !sequence) + { + packet->append(STRING_WITH_LEN(" MAX_ROWS=")); + packet->append_ulonglong(share->max_rows); + } + + if (share->avg_row_length) + { + packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH=")); + packet->append_ulonglong(share->avg_row_length); + } + + if (create_info.options & HA_OPTION_PACK_KEYS) + packet->append(STRING_WITH_LEN(" PACK_KEYS=1")); + if (create_info.options & HA_OPTION_NO_PACK_KEYS) + packet->append(STRING_WITH_LEN(" PACK_KEYS=0")); + if (share->db_create_options & HA_OPTION_STATS_PERSISTENT) + packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1")); + if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT) + packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0")); + if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON) + packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1")); + else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF) + packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0")); + if (share->stats_sample_pages != 0) + { + packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES=")); + packet->append_ulonglong(share->stats_sample_pages); + } + + /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ + if (create_info.options & HA_OPTION_CHECKSUM) + packet->append(STRING_WITH_LEN(" CHECKSUM=1")); + if (create_info.page_checksum != HA_CHOICE_UNDEF) + { + packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM=")); + packet->append(ha_choice_values[create_info.page_checksum], 1); + } + if (create_info.options & HA_OPTION_DELAY_KEY_WRITE) + packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1")); + if (create_info.row_type != ROW_TYPE_DEFAULT) + { + packet->append(STRING_WITH_LEN(" ROW_FORMAT=")); + packet->append(ha_row_type[(uint) create_info.row_type]); + } + if (share->transactional != HA_CHOICE_UNDEF) + { + packet->append(STRING_WITH_LEN(" TRANSACTIONAL=")); + packet->append(ha_choice_values[(uint) share->transactional], 1); + } + if (share->table_type == TABLE_TYPE_SEQUENCE) + packet->append(STRING_WITH_LEN(" SEQUENCE=1")); + if (table->s->key_block_size) + { + packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE=")); + packet->append_ulonglong(table->s->key_block_size); + } + table->file->append_create_info(packet); + +end_options: + if (share->comment.length) + { + packet->append(STRING_WITH_LEN(" COMMENT=")); + append_unescaped(packet, share->comment.str, share->comment.length); + } + if (share->connect_string.length) + { + packet->append(STRING_WITH_LEN(" CONNECTION=")); + append_unescaped(packet, share->connect_string.str, share->connect_string.length); + } + append_create_options(thd, packet, share->option_list, check_options, + hton->table_options); + append_directory(thd, packet, "DATA", create_info.data_file_name); + append_directory(thd, packet, "INDEX", create_info.index_file_name); +} + /* Build a CREATE TABLE statement for a table. @@ -1796,7 +1981,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, enum_with_db_name with_db_name) { List<Item> field_list; - char tmp[MAX_FIELD_WIDTH], *for_str, buff[128], def_value_buf[MAX_FIELD_WIDTH]; + char tmp[MAX_FIELD_WIDTH], *for_str, def_value_buf[MAX_FIELD_WIDTH]; const char *alias; String type; String def_value; @@ -1804,9 +1989,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, uint primary_key; KEY *key_info; TABLE *table= table_list->table; - handler *file= table->file; TABLE_SHARE *share= table->s; - HA_CREATE_INFO create_info; sql_mode_t sql_mode= thd->variables.sql_mode; bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | MODE_MSSQL | MODE_DB2 | @@ -1817,8 +2000,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, !foreign_db_mode; bool check_options= !(sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) && !create_info_arg; - handlerton *hton; my_bitmap_map *old_map; + handlerton *hton; int error= 0; DBUG_ENTER("show_create_table"); DBUG_PRINT("enter",("table: %s", table->s->table_name.str)); @@ -1828,7 +2011,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, hton= table->part_info->default_engine_type; else #endif - hton= file->ht; + hton= table->file->ht; restore_record(table, s->default_values); // Get empty record @@ -1864,7 +2047,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, */ if (with_db_name == WITH_DB_NAME) { - const LEX_STRING *const db= + const LEX_CSTRING *const db= table_list->schema_table ? &INFORMATION_SCHEMA_NAME : &table->s->db; if (!thd->db || strcmp(db->str, thd->db)) { @@ -1890,7 +2073,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(",\n")); packet->append(STRING_WITH_LEN(" ")); - append_identifier(thd,packet,field->field_name, strlen(field->field_name)); + append_identifier(thd,packet,field->field_name.str, + field->field_name.length); packet->append(' '); type.set(tmp, sizeof(tmp), system_charset_info); @@ -1977,12 +2161,6 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, } key_info= table->key_info; - bzero((char*) &create_info, sizeof(create_info)); - /* Allow update_create_info to update row type, page checksums and options */ - create_info.row_type= share->row_type; - create_info.page_checksum= share->page_checksum; - create_info.options= share->db_create_options; - file->update_create_info(&create_info); primary_key= share->primary_key; for (uint i=0 ; i < share->keys ; i++,key_info++) @@ -2020,8 +2198,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(','); if (key_part->field) - append_identifier(thd,packet,key_part->field->field_name, - strlen(key_part->field->field_name)); + append_identifier(thd,packet, key_part->field->field_name.str, + key_part->field->field_name.length); if (key_part->field && (key_part->length != table->field[key_part->fieldnr-1]->key_length() && @@ -2035,7 +2213,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, store_key_options(thd, packet, table, key_info); if (key_info->parser) { - LEX_STRING *parser_name= plugin_name(key_info->parser); + LEX_CSTRING *parser_name= plugin_name(key_info->parser); packet->append(STRING_WITH_LEN(" /*!50100 WITH PARSER ")); append_identifier(thd, packet, parser_name->str, parser_name->length); packet->append(STRING_WITH_LEN(" */ ")); @@ -2049,10 +2227,10 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, to the CREATE TABLE statement */ - if ((for_str= file->get_foreign_key_create_info())) + if ((for_str= table->file->get_foreign_key_create_info())) { packet->append(for_str, strlen(for_str)); - file->free_foreign_key_create_info(for_str); + table->file->free_foreign_key_create_info(for_str); } /* Add table level check constraints */ @@ -2079,146 +2257,9 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN("\n)")); if (show_table_options) - { - /* - IF check_create_info - THEN add ENGINE only if it was used when creating the table - */ - if (!create_info_arg || - (create_info_arg->used_fields & HA_CREATE_USED_ENGINE)) - { - if (sql_mode & (MODE_MYSQL323 | MODE_MYSQL40)) - packet->append(STRING_WITH_LEN(" TYPE=")); - else - packet->append(STRING_WITH_LEN(" ENGINE=")); - packet->append(hton_name(hton)); - } - - /* - Add AUTO_INCREMENT=... if there is an AUTO_INCREMENT column, - and NEXT_ID > 1 (the default). We must not print the clause - for engines that do not support this as it would break the - import of dumps, but as of this writing, the test for whether - AUTO_INCREMENT columns are allowed and wether AUTO_INCREMENT=... - is supported is identical, !(file->table_flags() & HA_NO_AUTO_INCREMENT)) - Because of that, we do not explicitly test for the feature, - but may extrapolate its existence from that of an AUTO_INCREMENT column. - */ - - if (create_info.auto_increment_value > 1) - { - char *end; - packet->append(STRING_WITH_LEN(" AUTO_INCREMENT=")); - end= longlong10_to_str(create_info.auto_increment_value, buff,10); - packet->append(buff, (uint) (end - buff)); - } - - if (share->table_charset && !(sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))) - { - /* - IF check_create_info - THEN add DEFAULT CHARSET only if it was used when creating the table - */ - if (!create_info_arg || - (create_info_arg->used_fields & HA_CREATE_USED_DEFAULT_CHARSET)) - { - packet->append(STRING_WITH_LEN(" DEFAULT CHARSET=")); - packet->append(share->table_charset->csname); - if (!(share->table_charset->state & MY_CS_PRIMARY)) - { - packet->append(STRING_WITH_LEN(" COLLATE=")); - packet->append(table->s->table_charset->name); - } - } - } - - if (share->min_rows) - { - char *end; - packet->append(STRING_WITH_LEN(" MIN_ROWS=")); - end= longlong10_to_str(share->min_rows, buff, 10); - packet->append(buff, (uint) (end- buff)); - } + add_table_options(thd, table, create_info_arg, + table_list->schema_table != 0, 0, packet); - if (share->max_rows && !table_list->schema_table) - { - char *end; - packet->append(STRING_WITH_LEN(" MAX_ROWS=")); - end= longlong10_to_str(share->max_rows, buff, 10); - packet->append(buff, (uint) (end - buff)); - } - - if (share->avg_row_length) - { - char *end; - packet->append(STRING_WITH_LEN(" AVG_ROW_LENGTH=")); - end= longlong10_to_str(share->avg_row_length, buff,10); - packet->append(buff, (uint) (end - buff)); - } - - if (create_info.options & HA_OPTION_PACK_KEYS) - packet->append(STRING_WITH_LEN(" PACK_KEYS=1")); - if (create_info.options & HA_OPTION_NO_PACK_KEYS) - packet->append(STRING_WITH_LEN(" PACK_KEYS=0")); - if (share->db_create_options & HA_OPTION_STATS_PERSISTENT) - packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=1")); - if (share->db_create_options & HA_OPTION_NO_STATS_PERSISTENT) - packet->append(STRING_WITH_LEN(" STATS_PERSISTENT=0")); - if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_ON) - packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=1")); - else if (share->stats_auto_recalc == HA_STATS_AUTO_RECALC_OFF) - packet->append(STRING_WITH_LEN(" STATS_AUTO_RECALC=0")); - if (share->stats_sample_pages != 0) - { - char *end; - packet->append(STRING_WITH_LEN(" STATS_SAMPLE_PAGES=")); - end= longlong10_to_str(share->stats_sample_pages, buff, 10); - packet->append(buff, (uint) (end - buff)); - } - - /* We use CHECKSUM, instead of TABLE_CHECKSUM, for backward compability */ - if (create_info.options & HA_OPTION_CHECKSUM) - packet->append(STRING_WITH_LEN(" CHECKSUM=1")); - if (create_info.page_checksum != HA_CHOICE_UNDEF) - { - packet->append(STRING_WITH_LEN(" PAGE_CHECKSUM=")); - packet->append(ha_choice_values[create_info.page_checksum], 1); - } - if (create_info.options & HA_OPTION_DELAY_KEY_WRITE) - packet->append(STRING_WITH_LEN(" DELAY_KEY_WRITE=1")); - if (create_info.row_type != ROW_TYPE_DEFAULT) - { - packet->append(STRING_WITH_LEN(" ROW_FORMAT=")); - packet->append(ha_row_type[(uint) create_info.row_type]); - } - if (share->transactional != HA_CHOICE_UNDEF) - { - packet->append(STRING_WITH_LEN(" TRANSACTIONAL=")); - packet->append(ha_choice_values[(uint) share->transactional], 1); - } - if (table->s->key_block_size) - { - char *end; - packet->append(STRING_WITH_LEN(" KEY_BLOCK_SIZE=")); - end= longlong10_to_str(table->s->key_block_size, buff, 10); - packet->append(buff, (uint) (end - buff)); - } - table->file->append_create_info(packet); - if (share->comment.length) - { - packet->append(STRING_WITH_LEN(" COMMENT=")); - append_unescaped(packet, share->comment.str, share->comment.length); - } - if (share->connect_string.length) - { - packet->append(STRING_WITH_LEN(" CONNECTION=")); - append_unescaped(packet, share->connect_string.str, share->connect_string.length); - } - append_create_options(thd, packet, share->option_list, check_options, - hton->table_options); - append_directory(thd, packet, "DATA", create_info.data_file_name); - append_directory(thd, packet, "INDEX", create_info.index_file_name); - } #ifdef WITH_PARTITION_STORAGE_ENGINE { if (table->part_info && @@ -2315,11 +2356,11 @@ void view_store_options(THD *thd, TABLE_LIST *table, String *buff) Returns ALGORITHM clause of a view */ -static const LEX_STRING *view_algorithm(TABLE_LIST *table) +static const LEX_CSTRING *view_algorithm(TABLE_LIST *table) { - static const LEX_STRING undefined= { C_STRING_WITH_LEN("UNDEFINED") }; - static const LEX_STRING merge= { C_STRING_WITH_LEN("MERGE") }; - static const LEX_STRING temptable= { C_STRING_WITH_LEN("TEMPTABLE") }; + static const LEX_CSTRING undefined= { STRING_WITH_LEN("UNDEFINED") }; + static const LEX_CSTRING merge= { STRING_WITH_LEN("MERGE") }; + static const LEX_CSTRING temptable= { STRING_WITH_LEN("TEMPTABLE") }; switch (table->algorithm) { case VIEW_ALGORITHM_TMPTABLE: return &temptable; @@ -2343,8 +2384,8 @@ static const LEX_STRING *view_algorithm(TABLE_LIST *table) definer_host [in] host name part of definer */ -void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user, - const LEX_STRING *definer_host) +void append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user, + const LEX_CSTRING *definer_host) { buffer->append(STRING_WITH_LEN("DEFINER=")); append_identifier(thd, buffer, definer_user->str, definer_user->length); @@ -2424,6 +2465,55 @@ static int show_create_view(THD *thd, TABLE_LIST *table, String *buff) } +static int show_create_sequence(THD *thd, TABLE_LIST *table_list, + String *packet) +{ + TABLE *table= table_list->table; + SEQUENCE *seq= table->s->sequence; + LEX_CSTRING alias; + sql_mode_t sql_mode= thd->variables.sql_mode; + bool foreign_db_mode= sql_mode & (MODE_POSTGRESQL | MODE_ORACLE | + MODE_MSSQL | MODE_DB2 | + MODE_MAXDB | MODE_ANSI); + bool show_table_options= !(sql_mode & MODE_NO_TABLE_OPTIONS) && + !foreign_db_mode; + + if (lower_case_table_names == 2) + { + alias.str= table->alias.c_ptr(); + alias.length= table->alias.length(); + } + else + alias= table->s->table_name; + + packet->append(STRING_WITH_LEN("CREATE SEQUENCE ")); + append_identifier(thd, packet, alias.str, alias.length); + packet->append(STRING_WITH_LEN(" start with ")); + packet->append_longlong(seq->start); + packet->append(STRING_WITH_LEN(" minvalue ")); + packet->append_longlong(seq->min_value); + packet->append(STRING_WITH_LEN(" maxvalue ")); + packet->append_longlong(seq->max_value); + packet->append(STRING_WITH_LEN(" increment by ")); + packet->append_longlong(seq->increment); + if (seq->cache) + { + packet->append(STRING_WITH_LEN(" cache ")); + packet->append_longlong(seq->cache); + } + else + packet->append(STRING_WITH_LEN(" nocache")); + if (seq->cycle) + packet->append(STRING_WITH_LEN(" cycle")); + else + packet->append(STRING_WITH_LEN(" nocycle")); + + if (show_table_options) + add_table_options(thd, table, 0, 0, 1, packet); + return 0; +} + + /**************************************************************************** Return info about all processes returns for each thread: thread id, user, host, db, command, info @@ -2728,8 +2818,9 @@ int select_result_text_buffer::append_row(List<Item> &items, bool send_names) if (send_names) { - data_ptr= item->name; - data_len= strlen(item->name); + DBUG_ASSERT(strlen(item->name.str) == item->name.length); + data_ptr= item->name.str; + data_len= item->name.length; } else { @@ -2885,7 +2976,7 @@ int fill_show_explain(THD *thd, TABLE_LIST *table, COND *cond) } else { - my_error(ER_NO_SUCH_THREAD, MYF(0), thread_id); + my_error(ER_NO_SUCH_THREAD, MYF(0), (ulong) thread_id); DBUG_RETURN(1); } } @@ -3251,7 +3342,7 @@ const char* get_one_variable(THD *thd, { sys_var *var= (sys_var *) value; show_type= var->show_type(); - value= var->value_ptr(thd, value_type, &null_lex_str); + value= var->value_ptr(thd, value_type, &null_clex_str); *charset= var->charset(thd); } @@ -3555,10 +3646,10 @@ bool schema_table_store_record(THD *thd, TABLE *table) static int make_table_list(THD *thd, SELECT_LEX *sel, - LEX_STRING *db_name, LEX_STRING *table_name) + LEX_CSTRING *db_name, LEX_CSTRING *table_name) { Table_ident *table_ident; - table_ident= new Table_ident(thd, *db_name, *table_name, 1); + table_ident= new Table_ident(thd, db_name, table_name, 1); if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, MDL_SHARED_READ)) return 1; return 0; @@ -3629,8 +3720,8 @@ bool get_lookup_value(THD *thd, Item_func *item_func, /* Lookup value is database name */ if (!cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1), - (uchar *) item_field->field_name, - strlen(item_field->field_name))) + (uchar *) item_field->field_name.str, + item_field->field_name.length)) { thd->make_lex_string(&lookup_field_vals->db_value, tmp_str->ptr(), tmp_str->length()); @@ -3638,8 +3729,8 @@ bool get_lookup_value(THD *thd, Item_func *item_func, /* Lookup value is table name */ else if (!cs->coll->strnncollsp(cs, (uchar *) field_name2, strlen(field_name2), - (uchar *) item_field->field_name, - strlen(item_field->field_name))) + (uchar *) item_field->field_name.str, + item_field->field_name.length)) { thd->make_lex_string(&lookup_field_vals->table_value, tmp_str->ptr(), tmp_str->length()); @@ -3724,11 +3815,11 @@ bool uses_only_table_name_fields(Item *item, TABLE_LIST *table) field_info[schema_table->idx_field2].field_name : ""; if (table->table != item_field->field->table || (cs->coll->strnncollsp(cs, (uchar *) field_name1, strlen(field_name1), - (uchar *) item_field->field_name, - strlen(item_field->field_name)) && + (uchar *) item_field->field_name.str, + item_field->field_name.length) && cs->coll->strnncollsp(cs, (uchar *) field_name2, strlen(field_name2), - (uchar *) item_field->field_name, - strlen(item_field->field_name)))) + (uchar *) item_field->field_name.str, + item_field->field_name.length))) return 0; } else if (item->type() == Item::REF_ITEM) @@ -3870,10 +3961,12 @@ bool get_lookup_field_values(THD *thd, COND *cond, TABLE_LIST *tables, are allocating a new memory buffer for these strings. */ if (lookup_field_values->db_value.str && lookup_field_values->db_value.str[0]) - my_casedn_str(system_charset_info, lookup_field_values->db_value.str); + my_casedn_str(system_charset_info, + (char*) lookup_field_values->db_value.str); if (lookup_field_values->table_value.str && lookup_field_values->table_value.str[0]) - my_casedn_str(system_charset_info, lookup_field_values->table_value.str); + my_casedn_str(system_charset_info, + (char*) lookup_field_values->table_value.str); } return rc; @@ -3902,8 +3995,8 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table) non-zero error */ -int make_db_list(THD *thd, Dynamic_array<LEX_STRING*> *files, - LOOKUP_FIELD_VALUES *lookup_field_vals) +static int make_db_list(THD *thd, Dynamic_array<LEX_CSTRING*> *files, + LOOKUP_FIELD_VALUES *lookup_field_vals) { if (lookup_field_vals->wild_db_value) { @@ -3960,13 +4053,13 @@ int make_db_list(THD *thd, Dynamic_array<LEX_STRING*> *files, */ if (files->append_val(&INFORMATION_SCHEMA_NAME)) return 1; - return find_files(thd, files, 0, mysql_data_home, &null_lex_str); + return find_files(thd, files, 0, mysql_data_home, &null_clex_str); } struct st_add_schema_table { - Dynamic_array<LEX_STRING*> *files; + Dynamic_array<LEX_CSTRING*> *files; const char *wild; }; @@ -3974,9 +4067,9 @@ struct st_add_schema_table static my_bool add_schema_table(THD *thd, plugin_ref plugin, void* p_data) { - LEX_STRING *file_name= 0; + LEX_CSTRING *file_name= 0; st_add_schema_table *data= (st_add_schema_table *)p_data; - Dynamic_array<LEX_STRING*> *file_list= data->files; + Dynamic_array<LEX_CSTRING*> *file_list= data->files; const char *wild= data->wild; ST_SCHEMA_TABLE *schema_table= plugin_data(plugin, ST_SCHEMA_TABLE *); DBUG_ENTER("add_schema_table"); @@ -3996,18 +4089,18 @@ static my_bool add_schema_table(THD *thd, plugin_ref plugin, DBUG_RETURN(0); } - if ((file_name= thd->make_lex_string(schema_table->table_name, - strlen(schema_table->table_name))) && + if ((file_name= thd->make_clex_string(schema_table->table_name, + strlen(schema_table->table_name))) && !file_list->append(file_name)) DBUG_RETURN(0); DBUG_RETURN(1); } -int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files, +int schema_tables_add(THD *thd, Dynamic_array<LEX_CSTRING*> *files, const char *wild) { - LEX_STRING *file_name= 0; + LEX_CSTRING *file_name; ST_SCHEMA_TABLE *tmp_schema_table= schema_tables; st_add_schema_table add_data; DBUG_ENTER("schema_tables_add"); @@ -4029,8 +4122,8 @@ int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files, continue; } if ((file_name= - thd->make_lex_string(tmp_schema_table->table_name, - strlen(tmp_schema_table->table_name))) && + thd->make_clex_string(tmp_schema_table->table_name, + strlen(tmp_schema_table->table_name))) && !files->append(file_name)) continue; DBUG_RETURN(1); @@ -4065,9 +4158,9 @@ int schema_tables_add(THD *thd, Dynamic_array<LEX_STRING*> *files, */ static int -make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names, +make_table_name_list(THD *thd, Dynamic_array<LEX_CSTRING*> *table_names, LEX *lex, LOOKUP_FIELD_VALUES *lookup_field_vals, - LEX_STRING *db_name) + LEX_CSTRING *db_name) { char path[FN_REFLEN + 1]; build_table_filename(path, sizeof(path) - 1, db_name->str, "", "", 0); @@ -4084,13 +4177,13 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names, } if (db_name == &INFORMATION_SCHEMA_NAME) { - LEX_STRING *name; + LEX_CSTRING *name; ST_SCHEMA_TABLE *schema_table= find_schema_table(thd, lookup_field_vals->table_value.str); if (schema_table && !schema_table->hidden) { - if (!(name= thd->make_lex_string(schema_table->table_name, - strlen(schema_table->table_name))) || + if (!(name= thd->make_clex_string(schema_table->table_name, + strlen(schema_table->table_name))) || table_names->append(name)) return 1; } @@ -4135,16 +4228,17 @@ make_table_name_list(THD *thd, Dynamic_array<LEX_STRING*> *table_names, static void get_table_engine_for_i_s(THD *thd, char *buf, TABLE_LIST *tl, - LEX_STRING *db, LEX_STRING *table) + LEX_CSTRING *db, LEX_CSTRING *table) { - LEX_STRING engine_name= { buf, 0 }; + LEX_CSTRING engine_name= { buf, 0 }; if (thd->get_stmt_da()->sql_errno() == ER_UNKNOWN_STORAGE_ENGINE) { char path[FN_REFLEN]; build_table_filename(path, sizeof(path) - 1, db->str, table->str, reg_ext, 0); - if (dd_frm_type(thd, path, &engine_name) == FRMTYPE_TABLE) + bool is_sequence; + if (dd_frm_type(thd, path, &engine_name, &is_sequence) == TABLE_TYPE_NORMAL) tl->option= engine_name.str; } } @@ -4174,8 +4268,8 @@ static void get_table_engine_for_i_s(THD *thd, char *buf, TABLE_LIST *tl, static bool fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys, TABLE *table, ST_SCHEMA_TABLE *schema_table, - LEX_STRING *orig_db_name, - LEX_STRING *orig_table_name, + LEX_CSTRING *orig_db_name, + LEX_CSTRING *orig_table_name, Open_tables_backup *open_tables_state_backup, bool can_deadlock) { @@ -4183,7 +4277,7 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys, Query_arena::STMT_CONVENTIONAL_EXECUTION), backup_arena, *old_arena; LEX *old_lex= thd->lex, temp_lex, *lex; - LEX_STRING db_name, table_name; + LEX_CSTRING db_name, table_name; TABLE_LIST *table_list; bool result= true; DBUG_ENTER("fill_schema_table_by_open"); @@ -4208,6 +4302,7 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys, /* Prepare temporary LEX. */ thd->lex= lex= &temp_lex; lex_start(thd); + lex->sql_command= old_lex->sql_command; /* Disable constant subquery evaluation as we won't be locking tables. */ lex->context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW; @@ -4260,26 +4355,8 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys, table_list->i_s_requested_object= schema_table->i_s_requested_object; } - /* - Let us set fake sql_command so views won't try to merge - themselves into main statement. If we don't do this, - SELECT * from information_schema.xxxx will cause problems. - SQLCOM_SHOW_FIELDS is used because it satisfies - 'only_view_structure()'. - */ - lex->sql_command= SQLCOM_SHOW_FIELDS; - result= (thd->open_temporary_tables(table_list) || - open_normal_and_derived_tables(thd, table_list, - (MYSQL_OPEN_IGNORE_FLUSH | - MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL | - (can_deadlock ? - MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)), - DT_INIT | DT_PREPARE | DT_CREATE)); - /* - Restore old value of sql_command back as it is being looked at in - process_table() function. - */ - lex->sql_command= old_lex->sql_command; + DBUG_ASSERT(thd->lex == lex); + result= open_tables_only_view_structure(thd, table_list, can_deadlock); DEBUG_SYNC(thd, "after_open_table_ignore_flush"); @@ -4296,7 +4373,8 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys, */ if (!is_show_fields_or_keys && result && (thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE || - thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT)) + thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT || + thd->get_stmt_da()->sql_errno() == ER_NOT_SEQUENCE)) { /* Hide error for a non-existing table. @@ -4366,7 +4444,8 @@ end: */ static int fill_schema_table_names(THD *thd, TABLE_LIST *tables, - LEX_STRING *db_name, LEX_STRING *table_name) + LEX_CSTRING *db_name, + LEX_CSTRING *table_name) { TABLE *table= tables->table; if (db_name == &INFORMATION_SCHEMA_NAME) @@ -4378,10 +4457,14 @@ static int fill_schema_table_names(THD *thd, TABLE_LIST *tables, { CHARSET_INFO *cs= system_charset_info; handlerton *hton; - if (ha_table_exists(thd, db_name->str, table_name->str, &hton)) + bool is_sequence; + if (ha_table_exists(thd, db_name->str, table_name->str, &hton, + &is_sequence)) { if (hton == view_pseudo_hton) table->field[3]->store(STRING_WITH_LEN("VIEW"), cs); + else if (is_sequence) + table->field[3]->store(STRING_WITH_LEN("SEQUENCE"), cs); else table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); } @@ -4531,8 +4614,8 @@ try_acquire_high_prio_shared_mdl_lock(THD *thd, TABLE_LIST *table, static int fill_schema_table_from_frm(THD *thd, TABLE *table, ST_SCHEMA_TABLE *schema_table, - LEX_STRING *db_name, - LEX_STRING *table_name, + LEX_CSTRING *db_name, + LEX_CSTRING *table_name, Open_tables_backup *open_tables_state_backup, bool can_deadlock) { @@ -4618,7 +4701,8 @@ static int fill_schema_table_from_frm(THD *thd, TABLE *table, if (!share) { if (thd->get_stmt_da()->sql_errno() == ER_NO_SUCH_TABLE || - thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT) + thd->get_stmt_da()->sql_errno() == ER_WRONG_OBJECT || + thd->get_stmt_da()->sql_errno() == ER_NOT_SEQUENCE) { res= 0; } @@ -4757,7 +4841,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) ST_SCHEMA_TABLE *schema_table= tables->schema_table; IS_table_read_plan *plan= tables->is_table_read_plan; enum enum_schema_tables schema_table_idx; - Dynamic_array<LEX_STRING*> db_names; + Dynamic_array<LEX_CSTRING*> db_names; Item *partial_cond= plan->partial_cond; int error= 1; Open_tables_backup open_tables_state_backup; @@ -4795,7 +4879,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) */ if (lsel && lsel->table_list.first) { - LEX_STRING db_name, table_name; + LEX_CSTRING db_name, table_name; db_name.str= lsel->table_list.first->db; db_name.length= lsel->table_list.first->db_length; @@ -4830,7 +4914,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) goto err; for (size_t i=0; i < db_names.elements(); i++) { - LEX_STRING *db_name= db_names.at(i); + LEX_CSTRING *db_name= db_names.at(i); DBUG_ASSERT(db_name->length <= NAME_LEN); #ifndef NO_EMBEDDED_ACCESS_CHECKS if (!(check_access(thd, SELECT_ACL, db_name->str, @@ -4840,7 +4924,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) acl_get(sctx->host, sctx->ip, sctx->priv_user, db_name->str, 0)) #endif { - Dynamic_array<LEX_STRING*> table_names; + Dynamic_array<LEX_CSTRING*> table_names; int res= make_table_name_list(thd, &table_names, lex, &plan->lookup_field_vals, db_name); if (res == 2) /* Not fatal error, continue */ @@ -4850,7 +4934,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond) for (size_t i=0; i < table_names.elements(); i++) { - LEX_STRING *table_name= table_names.at(i); + LEX_CSTRING *table_name= table_names.at(i); DBUG_ASSERT(table_name->length <= NAME_LEN); #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -4933,7 +5017,7 @@ err: } -bool store_schema_shemata(THD* thd, TABLE *table, LEX_STRING *db_name, +bool store_schema_shemata(THD* thd, TABLE *table, LEX_CSTRING *db_name, CHARSET_INFO *cs) { restore_record(table, s->default_values); @@ -4953,7 +5037,7 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond) */ LOOKUP_FIELD_VALUES lookup_field_vals; - Dynamic_array<LEX_STRING*> db_names; + Dynamic_array<LEX_CSTRING*> db_names; Schema_specification_st create; TABLE *table= tables->table; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -4989,7 +5073,7 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond) for (size_t i=0; i < db_names.elements(); i++) { - LEX_STRING *db_name= db_names.at(i); + LEX_CSTRING *db_name= db_names.at(i); DBUG_ASSERT(db_name->length <= NAME_LEN); if (db_name == &INFORMATION_SCHEMA_NAME) { @@ -5018,8 +5102,8 @@ int fill_schema_schemata(THD *thd, TABLE_LIST *tables, COND *cond) static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, - LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { const char *tmp_buff; MYSQL_TIME time; @@ -5061,7 +5145,7 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, String str(option_buff,sizeof(option_buff), system_charset_info); TABLE *show_table= tables->table; TABLE_SHARE *share= show_table->s; - handler *file= show_table->file; + handler *file= show_table->db_stat ? show_table->file : 0; handlerton *tmp_db_type= share->db_type(); #ifdef WITH_PARTITION_STORAGE_ENGINE bool is_partitioned= FALSE; @@ -5071,6 +5155,8 @@ static int get_schema_tables_record(THD *thd, TABLE_LIST *tables, table->field[3]->store(STRING_WITH_LEN("SYSTEM VIEW"), cs); else if (share->tmp_table) table->field[3]->store(STRING_WITH_LEN("LOCAL TEMPORARY"), cs); + else if (share->table_type == TABLE_TYPE_SEQUENCE) + table->field[3]->store(STRING_WITH_LEN("SEQUENCE"), cs); else table->field[3]->store(STRING_WITH_LEN("BASE TABLE"), cs); @@ -5358,7 +5444,7 @@ static void store_column_type(TABLE *table, Field *field, CHARSET_INFO *cs, field->real_type() == MYSQL_TYPE_STRING) // For binary type { uint32 octet_max_length= field->max_display_length(); - if (is_blob && octet_max_length != (uint32) 4294967295U) + if (is_blob && octet_max_length != (uint32) UINT_MAX32) octet_max_length /= field->charset()->mbmaxlen; longlong char_max_len= is_blob ? (longlong) octet_max_length / field->charset()->mbminlen : @@ -5445,8 +5531,8 @@ static void store_column_type(TABLE *table, Field *field, CHARSET_INFO *cs, static int get_schema_column_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, - LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { LEX *lex= thd->lex; const char *wild= lex->wild ? lex->wild->ptr() : NullS; @@ -5489,7 +5575,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, DEBUG_SYNC(thd, "get_schema_column"); if (wild && wild[0] && - wild_case_compare(system_charset_info, field->field_name,wild)) + wild_case_compare(system_charset_info, field->field_name.str, wild)) continue; count++; @@ -5502,7 +5588,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, &tables->grant.privilege, 0, 0, MY_TEST(tables->schema_table)); col_access= get_column_grant(thd, &tables->grant, db_name->str, table_name->str, - field->field_name) & COL_ACLS; + field->field_name.str) & COL_ACLS; if (!tables->schema_table && !col_access) continue; char *end= tmp; @@ -5520,7 +5606,7 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, table->field[0]->store(STRING_WITH_LEN("def"), cs); table->field[1]->store(db_name->str, db_name->length, cs); table->field[2]->store(table_name->str, table_name->length, cs); - table->field[3]->store(field->field_name, strlen(field->field_name), + table->field[3]->store(field->field_name.str, field->field_name.length, cs); table->field[4]->store((longlong) count, TRUE); @@ -5633,13 +5719,13 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin, if (!(hton->flags & HTON_HIDDEN)) { - LEX_STRING *name= plugin_name(plugin); + LEX_CSTRING *name= plugin_name(plugin); if (!(wild && wild[0] && wild_case_compare(scs, name->str,wild))) { - LEX_STRING yesno[2]= {{ C_STRING_WITH_LEN("NO") }, - { C_STRING_WITH_LEN("YES") }}; - LEX_STRING *tmp; + LEX_CSTRING yesno[2]= {{ STRING_WITH_LEN("NO") }, + { STRING_WITH_LEN("YES") }}; + LEX_CSTRING *tmp; const char *option_name= show_comp_option_name[(int) hton->state]; restore_record(table, s->default_values); @@ -5790,16 +5876,11 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, TABLE_SHARE share; TABLE tbl; CHARSET_INFO *cs= system_charset_info; - char params_buff[MAX_FIELD_WIDTH], returns_buff[MAX_FIELD_WIDTH], - sp_db_buff[NAME_LEN], sp_name_buff[NAME_LEN], path[FN_REFLEN], - definer_buff[DEFINER_LENGTH + 1]; - String params(params_buff, sizeof(params_buff), cs); - String returns(returns_buff, sizeof(returns_buff), cs); - String sp_db(sp_db_buff, sizeof(sp_db_buff), cs); - String sp_name(sp_name_buff, sizeof(sp_name_buff), cs); - String definer(definer_buff, sizeof(definer_buff), cs); + LEX_CSTRING definer, params, returns= empty_clex_str; + LEX_CSTRING db, name; + char path[FN_REFLEN]; sp_head *sp; - stored_procedure_type routine_type; + const Sp_handler *sph; bool free_sp_head; bool error= 0; DBUG_ENTER("store_schema_params"); @@ -5808,49 +5889,46 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, (void) build_table_filename(path, sizeof(path), "", "", "", 0); init_tmp_table_share(thd, &share, "", 0, "", path); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_DB], &sp_db); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_NAME], &sp_name); - get_field(thd->mem_root,proc_table->field[MYSQL_PROC_FIELD_DEFINER],&definer); - routine_type= (stored_procedure_type) proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int(); + proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); + proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); + proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); + sph= Sp_handler::handler((stored_procedure_type) proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int()); + if (!sph) + sph= &sp_handler_procedure; if (!full_access) - full_access= !strcmp(sp_user, definer.ptr()); + full_access= !strcmp(sp_user, definer.str); if (!full_access && - check_some_routine_access(thd, sp_db.ptr(),sp_name.ptr(), - routine_type == TYPE_ENUM_PROCEDURE)) + check_some_routine_access(thd, db.str, name.str, sph)) DBUG_RETURN(0); - params.length(0); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST], - ¶ms); - returns.length(0); - if (routine_type == TYPE_ENUM_FUNCTION) - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_FIELD_RETURNS], - &returns); - - sp= sp_load_for_information_schema(thd, proc_table, &sp_db, &sp_name, - (ulong) proc_table-> - field[MYSQL_PROC_FIELD_SQL_MODE]->val_int(), - routine_type, - returns.c_ptr_safe(), - params.c_ptr_safe(), - &free_sp_head); - + proc_table->field[MYSQL_PROC_FIELD_PARAM_LIST]->val_str_nopad(thd->mem_root, + ¶ms); + if (sph->type() == TYPE_ENUM_FUNCTION) + proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, + &returns); + sp= sph->sp_load_for_information_schema(thd, proc_table, db, name, + params, returns, + (ulong) proc_table-> + field[MYSQL_PROC_FIELD_SQL_MODE]-> + val_int(), + &free_sp_head); if (sp) { Field *field; - String tmp_string; - if (routine_type == TYPE_ENUM_FUNCTION) + LEX_CSTRING tmp_string; + if (sph->type() == TYPE_ENUM_FUNCTION) { restore_record(table, s->default_values); table->field[0]->store(STRING_WITH_LEN("def"), cs); - table->field[1]->store(sp_db.ptr(), sp_db.length(), cs); - table->field[2]->store(sp_name.ptr(), sp_name.length(), cs); + table->field[1]->store(db, cs); + table->field[2]->store(name, cs); table->field[3]->store((longlong) 0, TRUE); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_MYSQL_TYPE], - &tmp_string); - table->field[15]->store(tmp_string.ptr(), tmp_string.length(), cs); - field= sp->m_return_field_def.make_field(&share, thd->mem_root, ""); + proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root, + &tmp_string); + table->field[15]->store(tmp_string, cs); + field= sp->m_return_field_def.make_field(&share, thd->mem_root, + &empty_clex_str); field->table= &tbl; tbl.in_use= thd; store_column_type(table, field, cs, 6); @@ -5886,19 +5964,19 @@ bool store_schema_params(THD *thd, TABLE *table, TABLE *proc_table, restore_record(table, s->default_values); table->field[0]->store(STRING_WITH_LEN("def"), cs); - table->field[1]->store(sp_db.ptr(), sp_db.length(), cs); - table->field[2]->store(sp_name.ptr(), sp_name.length(), cs); + table->field[1]->store(db, cs); + table->field[2]->store(name, cs); table->field[3]->store((longlong) i + 1, TRUE); table->field[4]->store(tmp_buff, strlen(tmp_buff), cs); table->field[4]->set_notnull(); table->field[5]->store(spvar->name.str, spvar->name.length, cs); table->field[5]->set_notnull(); - get_field(thd->mem_root, proc_table->field[MYSQL_PROC_MYSQL_TYPE], - &tmp_string); - table->field[15]->store(tmp_string.ptr(), tmp_string.length(), cs); + proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_str_nopad(thd->mem_root, + &tmp_string); + table->field[15]->store(tmp_string, cs); field= spvar->field_def.make_field(&share, thd->mem_root, - spvar->name.str); + &spvar->name); field->table= &tbl; tbl.in_use= thd; store_column_type(table, field, cs, 6); @@ -5922,63 +6000,57 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, MYSQL_TIME time; LEX *lex= thd->lex; CHARSET_INFO *cs= system_charset_info; - char sp_db_buff[SAFE_NAME_LEN + 1], sp_name_buff[NAME_LEN + 1], - definer_buff[DEFINER_LENGTH + 1], - returns_buff[MAX_FIELD_WIDTH]; + const Sp_handler *sph; + LEX_CSTRING db, name, definer, returns= empty_clex_str; - String sp_db(sp_db_buff, sizeof(sp_db_buff), cs); - String sp_name(sp_name_buff, sizeof(sp_name_buff), cs); - String definer(definer_buff, sizeof(definer_buff), cs); - String returns(returns_buff, sizeof(returns_buff), cs); - - proc_table->field[MYSQL_PROC_FIELD_DB]->val_str(&sp_db); - proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str(&sp_name); - proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str(&definer); + proc_table->field[MYSQL_PROC_FIELD_DB]->val_str_nopad(thd->mem_root, &db); + proc_table->field[MYSQL_PROC_FIELD_NAME]->val_str_nopad(thd->mem_root, &name); + proc_table->field[MYSQL_PROC_FIELD_DEFINER]->val_str_nopad(thd->mem_root, &definer); + sph= Sp_handler::handler((stored_procedure_type) + proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int()); + if (!sph) + sph= &sp_handler_procedure; if (!full_access) - full_access= !strcmp(sp_user, definer.c_ptr_safe()); + full_access= !strcmp(sp_user, definer.str); if (!full_access && - check_some_routine_access(thd, sp_db.c_ptr_safe(), sp_name.c_ptr_safe(), - proc_table->field[MYSQL_PROC_MYSQL_TYPE]-> - val_int() == TYPE_ENUM_PROCEDURE)) + check_some_routine_access(thd, db.str, name.str, sph)) return 0; if ((lex->sql_command == SQLCOM_SHOW_STATUS_PROC && - proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == - TYPE_ENUM_PROCEDURE) || + sph->type() == TYPE_ENUM_PROCEDURE) || (lex->sql_command == SQLCOM_SHOW_STATUS_FUNC && - proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == - TYPE_ENUM_FUNCTION) || + sph->type() == TYPE_ENUM_FUNCTION) || (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) == 0) { restore_record(table, s->default_values); if (!wild || !wild[0] || !wild_case_compare(system_charset_info, - sp_name.c_ptr_safe(), wild)) + name.str, wild)) { int enum_idx= (int) proc_table->field[MYSQL_PROC_FIELD_ACCESS]->val_int(); - table->field[3]->store(sp_name.ptr(), sp_name.length(), cs); + table->field[3]->store(name, cs); copy_field_as_string(table->field[0], proc_table->field[MYSQL_PROC_FIELD_SPECIFIC_NAME]); table->field[1]->store(STRING_WITH_LEN("def"), cs); - table->field[2]->store(sp_db.ptr(), sp_db.length(), cs); + table->field[2]->store(db, cs); copy_field_as_string(table->field[4], proc_table->field[MYSQL_PROC_MYSQL_TYPE]); - if (proc_table->field[MYSQL_PROC_MYSQL_TYPE]->val_int() == - TYPE_ENUM_FUNCTION) + if (sph->type() == TYPE_ENUM_FUNCTION) { sp_head *sp; bool free_sp_head; - proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str(&returns); - sp= sp_load_for_information_schema(thd, proc_table, &sp_db, &sp_name, - (ulong) proc_table-> - field[MYSQL_PROC_FIELD_SQL_MODE]-> - val_int(), - TYPE_ENUM_FUNCTION, - returns.c_ptr_safe(), - "", &free_sp_head); - + proc_table->field[MYSQL_PROC_FIELD_RETURNS]->val_str_nopad(thd->mem_root, + &returns); + sp= sph->sp_load_for_information_schema(thd, proc_table, + db, name, + empty_clex_str /*params*/, + returns, + (ulong) proc_table-> + field[MYSQL_PROC_FIELD_SQL_MODE]-> + val_int(), + &free_sp_head); if (sp) { char path[FN_REFLEN]; @@ -5989,7 +6061,8 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, bzero((char*) &tbl, sizeof(TABLE)); (void) build_table_filename(path, sizeof(path), "", "", "", 0); init_tmp_table_share(thd, &share, "", 0, "", path); - field= sp->m_return_field_def.make_field(&share, thd->mem_root, ""); + field= sp->m_return_field_def.make_field(&share, thd->mem_root, + &empty_clex_str); field->table= &tbl; tbl.in_use= thd; store_column_type(table, field, cs, 5); @@ -6027,7 +6100,7 @@ bool store_schema_proc(THD *thd, TABLE *table, TABLE *proc_table, copy_field_as_string(table->field[26], proc_table->field[MYSQL_PROC_FIELD_COMMENT]); - table->field[27]->store(definer.ptr(), definer.length(), cs); + table->field[27]->store(definer, cs); copy_field_as_string(table->field[28], proc_table-> field[MYSQL_PROC_FIELD_CHARACTER_SET_CLIENT]); @@ -6115,8 +6188,8 @@ err: static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, - LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { CHARSET_INFO *cs= system_charset_info; DBUG_ENTER("get_schema_stat_record"); @@ -6151,7 +6224,8 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, for (uint i=0 ; i < show_table->s->keys ; i++,key_info++) { KEY_PART_INFO *key_part= key_info->key_part; - const char *str; + LEX_CSTRING *str; + LEX_CSTRING unknown= {STRING_WITH_LEN("?unknown field?") }; for (uint j=0 ; j < key_info->user_defined_key_parts ; j++,key_part++) { restore_record(table, s->default_values); @@ -6163,9 +6237,9 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, table->field[4]->store(db_name->str, db_name->length, cs); table->field[5]->store(key_info->name, strlen(key_info->name), cs); table->field[6]->store((longlong) (j+1), TRUE); - str=(key_part->field ? key_part->field->field_name : - "?unknown field?"); - table->field[7]->store(str, strlen(str), cs); + str= (key_part->field ? &key_part->field->field_name : + &unknown); + table->field[7]->store(str->str, str->length, cs); if (show_table->file) { if (show_table->file->index_flags(i, j, 0) & HA_READ_ORDER) @@ -6183,8 +6257,8 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, table->field[9]->store((longlong) records, TRUE); table->field[9]->set_notnull(); } - str= show_table->file->index_type(i); - table->field[13]->store(str, strlen(str), cs); + const char *tmp= show_table->file->index_type(i); + table->field[13]->store(tmp, strlen(tmp), cs); } if (!(key_info->flags & HA_FULLTEXT) && (key_part->field && @@ -6219,8 +6293,8 @@ static int get_schema_stat_record(THD *thd, TABLE_LIST *tables, static int get_schema_views_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, - LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { CHARSET_INFO *cs= system_charset_info; char definer[USER_HOST_BUFF_SIZE]; @@ -6363,9 +6437,10 @@ static int get_schema_views_record(THD *thd, TABLE_LIST *tables, } -bool store_constraints(THD *thd, TABLE *table, LEX_STRING *db_name, - LEX_STRING *table_name, const char *key_name, - uint key_len, const char *con_type, uint con_len) +static bool +store_constraints(THD *thd, TABLE *table, const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name, const char *key_name, + uint key_len, const char *con_type, uint con_len) { CHARSET_INFO *cs= system_charset_info; restore_record(table, s->default_values); @@ -6381,8 +6456,8 @@ bool store_constraints(THD *thd, TABLE *table, LEX_STRING *db_name, static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, - LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { DBUG_ENTER("get_schema_constraints_record"); if (res) @@ -6454,14 +6529,15 @@ static int get_schema_constraints_record(THD *thd, TABLE_LIST *tables, static bool store_trigger(THD *thd, Trigger *trigger, - TABLE *table, LEX_STRING *db_name, - LEX_STRING *table_name) + TABLE *table, const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { CHARSET_INFO *cs= system_charset_info; - LEX_STRING sql_mode_rep; + LEX_CSTRING sql_mode_rep; MYSQL_TIME timestamp; char definer_holder[USER_HOST_BUFF_SIZE]; - LEX_STRING definer_buffer, trigger_stmt, trigger_body; + LEX_STRING definer_buffer; + LEX_CSTRING trigger_stmt, trigger_body; definer_buffer.str= definer_holder; trigger->get_trigger_info(&trigger_stmt, &trigger_body, &definer_buffer); @@ -6510,8 +6586,8 @@ static bool store_trigger(THD *thd, Trigger *trigger, static int get_schema_triggers_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, - LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { DBUG_ENTER("get_schema_triggers_record"); /* @@ -6557,10 +6633,11 @@ ret: } -void store_key_column_usage(TABLE *table, LEX_STRING *db_name, - LEX_STRING *table_name, const char *key_name, - uint key_len, const char *con_type, uint con_len, - longlong idx) +static void +store_key_column_usage(TABLE *table, const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name, const char *key_name, + uint key_len, const char *con_type, uint con_len, + longlong idx) { CHARSET_INFO *cs= system_charset_info; table->field[0]->store(STRING_WITH_LEN("def"), cs); @@ -6577,8 +6654,8 @@ void store_key_column_usage(TABLE *table, LEX_STRING *db_name, static int get_schema_key_column_usage_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, - LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { DBUG_ENTER("get_schema_key_column_usage_record"); if (res) @@ -6614,8 +6691,8 @@ static int get_schema_key_column_usage_record(THD *thd, store_key_column_usage(table, db_name, table_name, key_info->name, strlen(key_info->name), - key_part->field->field_name, - strlen(key_part->field->field_name), + key_part->field->field_name.str, + key_part->field->field_name.length, (longlong) f_idx); if (schema_table_store_record(thd, table)) DBUG_RETURN(1); @@ -6628,9 +6705,9 @@ static int get_schema_key_column_usage_record(THD *thd, List_iterator_fast<FOREIGN_KEY_INFO> fkey_it(f_key_list); while ((f_key_info= fkey_it++)) { - LEX_STRING *f_info; - LEX_STRING *r_info; - List_iterator_fast<LEX_STRING> it(f_key_info->foreign_fields), + LEX_CSTRING *f_info; + LEX_CSTRING *r_info; + List_iterator_fast<LEX_CSTRING> it(f_key_info->foreign_fields), it1(f_key_info->referenced_fields); uint f_idx= 0; while ((f_info= it++)) @@ -6666,10 +6743,10 @@ static int get_schema_key_column_usage_record(THD *thd, #ifdef WITH_PARTITION_STORAGE_ENGINE -static void collect_partition_expr(THD *thd, List<char> &field_list, +static void collect_partition_expr(THD *thd, List<const char> &field_list, String *str) { - List_iterator<char> part_it(field_list); + List_iterator<const char> part_it(field_list); ulong no_fields= field_list.elements; const char *field_str; str->length(0); @@ -6858,8 +6935,8 @@ get_partition_column_description(THD *thd, static int get_schema_partitions_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, - LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { CHARSET_INFO *cs= system_charset_info; char buff[61]; @@ -7153,7 +7230,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) /* SQL_MODE */ { - LEX_STRING sql_mode; + LEX_CSTRING sql_mode; sql_mode_string_representation(thd, et.sql_mode, &sql_mode); sch_table->field[ISE_SQL_MODE]-> store(sql_mode.str, sql_mode.length, scs); @@ -7175,7 +7252,7 @@ copy_event_to_schema_table(THD *thd, TABLE *sch_table, TABLE *event_table) sch_table->field[ISE_INTERVAL_VALUE]-> store(show_str.ptr(), show_str.length(), scs); - LEX_STRING *ival= &interval_type_to_name[et.interval]; + LEX_CSTRING *ival= &interval_type_to_name[et.interval]; sch_table->field[ISE_INTERVAL_FIELD]->set_notnull(); sch_table->field[ISE_INTERVAL_FIELD]->store(ival->str, ival->length, scs); @@ -7409,7 +7486,8 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond) static int get_referential_constraints_record(THD *thd, TABLE_LIST *tables, TABLE *table, bool res, - LEX_STRING *db_name, LEX_STRING *table_name) + const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name) { CHARSET_INFO *cs= system_charset_info; LEX_CSTRING *s; @@ -7587,6 +7665,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) for (; fields_info->field_name; fields_info++) { + uint field_name_length= strlen(fields_info->field_name); switch (fields_info->field_type) { case MYSQL_TYPE_TINY: case MYSQL_TYPE_LONG: @@ -7606,14 +7685,14 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) case MYSQL_TYPE_DATE: if (!(item=new (mem_root) Item_return_date_time(thd, fields_info->field_name, - strlen(fields_info->field_name), + field_name_length, fields_info->field_type))) DBUG_RETURN(0); break; case MYSQL_TYPE_TIME: if (!(item=new (mem_root) Item_return_date_time(thd, fields_info->field_name, - strlen(fields_info->field_name), + field_name_length, fields_info->field_type))) DBUG_RETURN(0); break; @@ -7621,7 +7700,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) case MYSQL_TYPE_DATETIME: if (!(item=new (mem_root) Item_return_date_time(thd, fields_info->field_name, - strlen(fields_info->field_name), + field_name_length, fields_info->field_type))) DBUG_RETURN(0); item->decimals= fields_info->field_length; @@ -7654,8 +7733,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) item->max_length+= 1; if (item->decimals > 0) item->max_length+= 1; - item->set_name(thd, fields_info->field_name, - strlen(fields_info->field_name), cs); + item->set_name(thd, fields_info->field_name, field_name_length, cs); break; case MYSQL_TYPE_TINY_BLOB: case MYSQL_TYPE_MEDIUM_BLOB: @@ -7678,7 +7756,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list) DBUG_RETURN(0); } item->set_name(thd, fields_info->field_name, - strlen(fields_info->field_name), cs); + field_name_length, cs); break; } field_list.push_back(item, thd->mem_root); @@ -7733,8 +7811,10 @@ static int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) { if (field_info->old_name) { + LEX_CSTRING field_name= {field_info->field_name, + strlen(field_info->field_name)}; Item_field *field= new (thd->mem_root) - Item_field(thd, context, NullS, NullS, field_info->field_name); + Item_field(thd, context, NullS, NullS, &field_name); if (field) { field->set_name(thd, field_info->old_name, @@ -7760,8 +7840,11 @@ int make_schemata_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) { ST_FIELD_INFO *field_info= &schema_table->fields_info[1]; String buffer(tmp,sizeof(tmp), system_charset_info); + LEX_CSTRING field_name= {field_info->field_name, + strlen(field_info->field_name) }; + Item_field *field= new (thd->mem_root) Item_field(thd, context, - NullS, NullS, field_info->field_name); + NullS, NullS, &field_name); if (!field || add_item_to_list(thd, field)) return 1; buffer.length(0); @@ -7784,8 +7867,10 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) String buffer(tmp,sizeof(tmp), thd->charset()); LEX *lex= thd->lex; Name_resolution_context *context= &lex->select_lex.context; - ST_FIELD_INFO *field_info= &schema_table->fields_info[2]; + LEX_CSTRING field_name= {field_info->field_name, + strlen(field_info->field_name) }; + buffer.length(0); buffer.append(field_info->old_name); buffer.append(lex->select_lex.db); @@ -7796,15 +7881,17 @@ int make_table_names_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) buffer.append(')'); } Item_field *field= new (thd->mem_root) Item_field(thd, context, - NullS, NullS, field_info->field_name); + NullS, NullS, &field_name); if (add_item_to_list(thd, field)) return 1; field->set_name(thd, buffer.ptr(), buffer.length(), system_charset_info); if (thd->lex->verbose) { - field->set_name(thd, buffer.ptr(), buffer.length(), system_charset_info); field_info= &schema_table->fields_info[3]; - field= new (thd->mem_root) Item_field(thd, context, NullS, NullS, field_info->field_name); + LEX_CSTRING field_name2= {field_info->field_name, + strlen(field_info->field_name) }; + field= new (thd->mem_root) Item_field(thd, context, NullS, NullS, + &field_name2); if (add_item_to_list(thd, field)) return 1; field->set_name(thd, field_info->old_name, strlen(field_info->old_name), @@ -7824,12 +7911,14 @@ int make_columns_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) for (; *field_num >= 0; field_num++) { field_info= &schema_table->fields_info[*field_num]; + LEX_CSTRING field_name= {field_info->field_name, + strlen(field_info->field_name)}; if (!thd->lex->verbose && (*field_num == 14 || *field_num == 18 || *field_num == 19)) continue; Item_field *field= new (thd->mem_root) Item_field(thd, context, - NullS, NullS, field_info->field_name); + NullS, NullS, &field_name); if (field) { field->set_name(thd, field_info->old_name, @@ -7853,8 +7942,10 @@ int make_character_sets_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) for (; *field_num >= 0; field_num++) { field_info= &schema_table->fields_info[*field_num]; + LEX_CSTRING field_name= {field_info->field_name, + strlen(field_info->field_name)}; Item_field *field= new (thd->mem_root) Item_field(thd, context, - NullS, NullS, field_info->field_name); + NullS, NullS, &field_name); if (field) { field->set_name(thd, field_info->old_name, @@ -7878,8 +7969,10 @@ int make_proc_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table) for (; *field_num >= 0; field_num++) { field_info= &schema_table->fields_info[*field_num]; + LEX_CSTRING field_name= {field_info->field_name, + strlen(field_info->field_name)}; Item_field *field= new (thd->mem_root) Item_field(thd, context, - NullS, NullS, field_info->field_name); + NullS, NullS, &field_name); if (field) { field->set_name(thd, field_info->old_name, @@ -7993,7 +8086,7 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list) int make_schema_select(THD *thd, SELECT_LEX *sel, ST_SCHEMA_TABLE *schema_table) { - LEX_STRING db, table; + LEX_CSTRING db, table; DBUG_ENTER("make_schema_select"); DBUG_PRINT("enter", ("mysql_schema_select: %s", schema_table->table_name)); /* @@ -8011,7 +8104,7 @@ int make_schema_select(THD *thd, SELECT_LEX *sel, if (schema_table->old_format(thd, schema_table)) DBUG_RETURN(1); - if (!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0), + if (!sel->add_table_to_list(thd, new Table_ident(thd, &db, &table, 0), 0, 0, TL_READ, MDL_SHARED_READ)) DBUG_RETURN(1); @@ -9389,8 +9482,8 @@ static bool show_create_trigger_impl(THD *thd, Trigger *trigger) int ret_code; Protocol *p= thd->protocol; List<Item> fields; - LEX_STRING trg_sql_mode_str, trg_body; - LEX_STRING trg_sql_original_stmt; + LEX_CSTRING trg_sql_mode_str, trg_body; + LEX_CSTRING trg_sql_original_stmt; LEX_STRING trg_definer; CHARSET_INFO *trg_client_cs; MEM_ROOT *mem_root= thd->mem_root; @@ -9544,8 +9637,8 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) { char trn_path_buff[FN_REFLEN]; LEX_STRING trn_path= { trn_path_buff, 0 }; - LEX_STRING db; - LEX_STRING tbl_name; + LEX_CSTRING db; + LEX_CSTRING tbl_name; TABLE_LIST *table; build_trn_path(thd, trg_name, &trn_path); @@ -9556,7 +9649,8 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) return NULL; } - if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name)) + if (load_table_name_for_trigger(thd, trg_name, (LEX_CSTRING*) &trn_path, + &tbl_name)) return NULL; /* We need to reset statement table list to be PS/SP friendly. */ @@ -9567,7 +9661,7 @@ TABLE_LIST *get_trigger_table(THD *thd, const sp_name *trg_name) db.str= thd->strmake(db.str, db.length); if (lower_case_table_names) - db.length= my_casedn_str(files_charset_info, db.str); + db.length= my_casedn_str(files_charset_info, (char*) db.str); tbl_name.str= thd->strmake(tbl_name.str, tbl_name.length); diff --git a/sql/sql_show.h b/sql/sql_show.h index e93b855450c..dc2fe9738fe 100644 --- a/sql/sql_show.h +++ b/sql/sql_show.h @@ -90,8 +90,8 @@ bool mysqld_show_create_get_fields(THD *thd, TABLE_LIST *table_list, List<Item> *field_list, String *buffer); bool mysqld_show_create(THD *thd, TABLE_LIST *table_list); void mysqld_show_create_db_get_fields(THD *thd, List<Item> *field_list); -bool mysqld_show_create_db(THD *thd, LEX_STRING *db_name, - LEX_STRING *orig_db_name, +bool mysqld_show_create_db(THD *thd, LEX_CSTRING *db_name, + LEX_CSTRING *orig_db_name, const DDL_options_st &options); void mysqld_list_processes(THD *thd,const char *user,bool verbose); @@ -103,8 +103,8 @@ bool mysqld_show_contributors(THD *thd); bool mysqld_show_privileges(THD *thd); char *make_backup_log_name(char *buff, const char *name, const char* log_ext); uint calc_sum_of_all_status(STATUS_VAR *to); -void append_definer(THD *thd, String *buffer, const LEX_STRING *definer_user, - const LEX_STRING *definer_host); +void append_definer(THD *thd, String *buffer, const LEX_CSTRING *definer_user, + const LEX_CSTRING *definer_host); int add_status_vars(SHOW_VAR *list); void remove_status_vars(SHOW_VAR *list); void init_status_vars(); @@ -180,13 +180,13 @@ typedef struct st_lookup_field_values Note that this value length may exceed @c NAME_LEN. @sa wild_db_value */ - LEX_STRING db_value; + LEX_CSTRING db_value; /** Value of a TABLE_NAME clause. Note that this value length may exceed @c NAME_LEN. @sa wild_table_value */ - LEX_STRING table_value; + LEX_CSTRING table_value; /** True when @c db_value is a LIKE clause, false when @c db_value is an '=' clause. diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc index 1b7edbee54a..6a57b8fc9ce 100644 --- a/sql/sql_signal.cc +++ b/sql/sql_signal.cc @@ -75,63 +75,6 @@ void Set_signal_information::clear() memset(m_item, 0, sizeof(m_item)); } -void -Sql_cmd_common_signal::assign_defaults(Sql_condition *cond, - bool set_level_code, - Sql_condition::enum_warning_level level, - int sqlcode) -{ - if (set_level_code) - { - cond->m_level= level; - cond->m_sql_errno= sqlcode; - } - if (! cond->get_message_text()) - cond->set_builtin_message_text(ER(sqlcode)); -} - -void Sql_cmd_common_signal::eval_defaults(THD *thd, Sql_condition *cond) -{ - DBUG_ASSERT(cond); - - const char* sqlstate; - bool set_defaults= (m_cond != 0); - - if (set_defaults) - { - /* - SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions. - */ - DBUG_ASSERT(m_cond->type == sp_condition_value::SQLSTATE); - sqlstate= m_cond->sql_state; - cond->set_sqlstate(sqlstate); - } - else - sqlstate= cond->get_sqlstate(); - - DBUG_ASSERT(sqlstate); - /* SQLSTATE class "00": illegal, rejected in the parser. */ - DBUG_ASSERT((sqlstate[0] != '0') || (sqlstate[1] != '0')); - - if ((sqlstate[0] == '0') && (sqlstate[1] == '1')) - { - /* SQLSTATE class "01": warning. */ - assign_defaults(cond, set_defaults, - Sql_condition::WARN_LEVEL_WARN, ER_SIGNAL_WARN); - } - else if ((sqlstate[0] == '0') && (sqlstate[1] == '2')) - { - /* SQLSTATE class "02": not found. */ - assign_defaults(cond, set_defaults, - Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_NOT_FOUND); - } - else - { - /* other SQLSTATE classes : error. */ - assign_defaults(cond, set_defaults, - Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_EXCEPTION); - } -} static bool assign_fixed_string(MEM_ROOT *mem_root, CHARSET_INFO *dst_cs, @@ -402,7 +345,7 @@ bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond) DBUG_ASSERT(thd->lex->query_tables == NULL); - eval_defaults(thd, cond); + cond->assign_defaults(thd, m_cond); if (eval_signal_informations(thd, cond)) DBUG_RETURN(result); @@ -410,13 +353,7 @@ bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond) DBUG_ASSERT((cond->m_level == Sql_condition::WARN_LEVEL_WARN) || (cond->m_level == Sql_condition::WARN_LEVEL_ERROR)); - Sql_condition *raised= NULL; - raised= thd->raise_condition(cond->get_sql_errno(), - cond->get_sqlstate(), - cond->get_level(), - cond->get_message_text()); - if (raised) - raised->copy_opt_attributes(cond); + (void) thd->raise_condition(cond); if (cond->m_level == Sql_condition::WARN_LEVEL_WARN) { @@ -430,7 +367,8 @@ bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond) bool Sql_cmd_signal::execute(THD *thd) { bool result= TRUE; - Sql_condition cond(thd->mem_root); + DBUG_ASSERT(m_cond); + Sql_condition cond(thd->mem_root, m_cond->get_user_condition_identity()); DBUG_ENTER("Sql_cmd_signal::execute"); @@ -484,11 +422,7 @@ bool Sql_cmd_resignal::execute(THD *thd) DBUG_RETURN(result); } - Sql_condition signaled_err(thd->mem_root); - signaled_err.set(signaled->sql_errno, - signaled->sql_state, - signaled->level, - signaled->message); + Sql_condition signaled_err(thd->mem_root, *signaled, signaled->message); if (m_cond) { @@ -500,13 +434,19 @@ bool Sql_cmd_resignal::execute(THD *thd) /* Check if the old condition still exists. */ if (da->has_sql_condition(signaled->message, strlen(signaled->message))) { - /* Make room for the new RESIGNAL condition. */ - da->reserve_space(thd, 1); + /* + Make room for the new RESIGNAL condition and one for the stack trace + note. + */ + da->reserve_space(thd, 2); } else { - /* Make room for old condition + the new RESIGNAL condition. */ - da->reserve_space(thd, 2); + /* + Make room for old condition + the new RESIGNAL condition + the stack + trace note. + */ + da->reserve_space(thd, 3); da->push_warning(thd, &signaled_err); } diff --git a/sql/sql_signal.h b/sql/sql_signal.h index 2a508eed5bf..4d4601a5ec1 100644 --- a/sql/sql_signal.h +++ b/sql/sql_signal.h @@ -40,27 +40,6 @@ protected: {} /** - Assign the condition items 'MYSQL_ERRNO', 'level' and 'MESSAGE_TEXT' - default values of a condition. - @param cond the condition to update. - @param set_level_code true if 'level' and 'MYSQL_ERRNO' needs to be overwritten - @param level the level to assign - @param sqlcode the sql code to assign - */ - static void assign_defaults(Sql_condition *cond, - bool set_level_code, - Sql_condition::enum_warning_level level, - int sqlcode); - - /** - Evaluate the condition items 'SQLSTATE', 'MYSQL_ERRNO', 'level' and 'MESSAGE_TEXT' - default values for this statement. - @param thd the current thread. - @param cond the condition to update. - */ - void eval_defaults(THD *thd, Sql_condition *cond); - - /** Evaluate each signal condition items for this statement. @param thd the current thread. @param cond the condition to update. diff --git a/sql/sql_statistics.cc b/sql/sql_statistics.cc index ad71f634265..3ec8fea29c6 100644 --- a/sql/sql_statistics.cc +++ b/sql/sql_statistics.cc @@ -471,9 +471,9 @@ protected: /* Table for which statistical data is read / updated */ TABLE *table; - TABLE_SHARE *table_share; /* Table share for 'table */ - LEX_STRING *db_name; /* Name of the database containing 'table' */ - LEX_STRING *table_name; /* Name of the table 'table' */ + TABLE_SHARE *table_share; /* Table share for 'table */ + LEX_CSTRING *db_name; /* Name of the database containing 'table' */ + LEX_CSTRING *table_name; /* Name of the table 'table' */ void store_record_for_update() { @@ -528,7 +528,7 @@ public: by the database name 'db' and the table name 'tab'. */ - Stat_table(TABLE *stat, LEX_STRING *db, LEX_STRING *tab) + Stat_table(TABLE *stat, LEX_CSTRING *db, LEX_CSTRING *tab) :stat_table(stat), table_share(NULL) { common_init_stat_table(); @@ -553,7 +553,7 @@ public: The method is called by the update_table_name_key_parts function. */ - virtual void change_full_table_name(LEX_STRING *db, LEX_STRING *tab)= 0; + virtual void change_full_table_name(LEX_CSTRING *db, LEX_CSTRING *tab)= 0; /** @@ -703,7 +703,7 @@ public: to store the new names in the record buffer used for updates. */ - bool update_table_name_key_parts(LEX_STRING *db, LEX_STRING *tab) + bool update_table_name_key_parts(LEX_CSTRING *db, LEX_CSTRING *tab) { store_record_for_update(); change_full_table_name(db, tab); @@ -765,7 +765,7 @@ private: table_name_field= stat_table->field[TABLE_STAT_TABLE_NAME]; } - void change_full_table_name(LEX_STRING *db, LEX_STRING *tab) + void change_full_table_name(LEX_CSTRING *db, LEX_CSTRING *tab) { db_name_field->store(db->str, db->length, system_charset_info); table_name_field->store(tab->str, tab->length, system_charset_info); @@ -795,7 +795,7 @@ public: from the database 'db'. */ - Table_stat(TABLE *stat, LEX_STRING *db, LEX_STRING *tab) + Table_stat(TABLE *stat, LEX_CSTRING *db, LEX_CSTRING *tab) :Stat_table(stat, db, tab) { common_init_table_stat(); @@ -909,7 +909,7 @@ private: column_name_field= stat_table->field[COLUMN_STAT_COLUMN_NAME]; } - void change_full_table_name(LEX_STRING *db, LEX_STRING *tab) + void change_full_table_name(LEX_CSTRING *db, LEX_CSTRING *tab) { db_name_field->store(db->str, db->length, system_charset_info); table_name_field->store(tab->str, tab->length, system_charset_info); @@ -939,7 +939,7 @@ public: from the database 'db'. */ - Column_stat(TABLE *stat, LEX_STRING *db, LEX_STRING *tab) + Column_stat(TABLE *stat, LEX_CSTRING *db, LEX_CSTRING *tab) :Stat_table(stat, db, tab) { common_init_column_stat_table(); @@ -983,8 +983,7 @@ public: void set_key_fields(Field *col) { set_full_table_name(); - const char *column_name= col->field_name; - column_name_field->store(column_name, strlen(column_name), + column_name_field->store(col->field_name.str, col->field_name.length, system_charset_info); table_field= col; } @@ -1245,7 +1244,7 @@ private: prefix_arity_field= stat_table->field[INDEX_STAT_PREFIX_ARITY]; } - void change_full_table_name(LEX_STRING *db, LEX_STRING *tab) + void change_full_table_name(LEX_CSTRING *db, LEX_CSTRING *tab) { db_name_field->store(db->str, db->length, system_charset_info); table_name_field->store(tab->str, tab->length, system_charset_info); @@ -1277,7 +1276,7 @@ public: from the database 'db'. */ - Index_stat(TABLE *stat, LEX_STRING *db, LEX_STRING *tab) + Index_stat(TABLE *stat, LEX_CSTRING *db, LEX_CSTRING *tab) :Stat_table(stat, db, tab) { common_init_index_stat_table(); @@ -1320,7 +1319,7 @@ public: void set_index_prefix_key_fields(KEY *index_info) { set_full_table_name(); - char *index_name= index_info->name; + const char *index_name= index_info->name; index_name_field->store(index_name, strlen(index_name), system_charset_info); table_key_info= index_info; @@ -1961,7 +1960,7 @@ void create_min_max_statistical_fields_for_table(TABLE *table) for (uint i=0; i < 2; i++, record+= rec_buff_length) { - for (Field **field_ptr= table->field; *field_ptr; field_ptr++) + for (Field **field_ptr= table->field; *field_ptr; field_ptr++) { Field *fld; Field *table_field= *field_ptr; @@ -2030,7 +2029,7 @@ void create_min_max_statistical_fields_for_table_share(THD *thd, for (uint i=0; i < 2; i++, record+= rec_buff_length) { - for (Field **field_ptr= table_share->field; *field_ptr; field_ptr++) + for (Field **field_ptr= table_share->field; *field_ptr; field_ptr++) { Field *fld; Field *table_field= *field_ptr; @@ -2493,7 +2492,7 @@ bool Column_statistics_collected::add(ha_rows rowno) set_not_null(COLUMN_STAT_MIN_VALUE); if (max_value && column->update_max(max_value, rowno == nulls)) set_not_null(COLUMN_STAT_MAX_VALUE); - if (count_distinct) + if (count_distinct) err= count_distinct->add(); } return err; @@ -3283,7 +3282,7 @@ int read_statistics_for_tables_if_needed(THD *thd, TABLE_LIST *tables) The function is called when executing the statement DROP TABLE 'tab'. */ -int delete_statistics_for_table(THD *thd, LEX_STRING *db, LEX_STRING *tab) +int delete_statistics_for_table(THD *thd, LEX_CSTRING *db, LEX_CSTRING *tab) { int err; enum_binlog_format save_binlog_format; @@ -3521,8 +3520,8 @@ int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info, The function is called when executing any statement that renames a table */ -int rename_table_in_stat_tables(THD *thd, LEX_STRING *db, LEX_STRING *tab, - LEX_STRING *new_db, LEX_STRING *new_tab) +int rename_table_in_stat_tables(THD *thd, LEX_CSTRING *db, LEX_CSTRING *tab, + LEX_CSTRING *new_db, LEX_CSTRING *new_tab) { int err; enum_binlog_format save_binlog_format; diff --git a/sql/sql_statistics.h b/sql/sql_statistics.h index f46583839d1..eb59b149753 100644 --- a/sql/sql_statistics.h +++ b/sql/sql_statistics.h @@ -94,12 +94,12 @@ int alloc_statistics_for_table_share(THD* thd, TABLE_SHARE *share, bool is_safe); int alloc_statistics_for_table(THD *thd, TABLE *table); int update_statistics_for_table(THD *thd, TABLE *table); -int delete_statistics_for_table(THD *thd, LEX_STRING *db, LEX_STRING *tab); +int delete_statistics_for_table(THD *thd, LEX_CSTRING *db, LEX_CSTRING *tab); int delete_statistics_for_column(THD *thd, TABLE *tab, Field *col); int delete_statistics_for_index(THD *thd, TABLE *tab, KEY *key_info, bool ext_prefixes_only); -int rename_table_in_stat_tables(THD *thd, LEX_STRING *db, LEX_STRING *tab, - LEX_STRING *new_db, LEX_STRING *new_tab); +int rename_table_in_stat_tables(THD *thd, LEX_CSTRING *db, LEX_CSTRING *tab, + LEX_CSTRING *new_db, LEX_CSTRING *new_tab); int rename_column_in_stat_tables(THD *thd, TABLE *tab, Field *col, const char *new_name); void set_statistics_for_table(THD *thd, TABLE *table); @@ -259,10 +259,10 @@ static inline int rename_table_in_stat_tables(THD *thd, const char *db, const char *tab, const char *new_db, const char *new_tab) { - LEX_STRING od= { const_cast<char*>(db), strlen(db) }; - LEX_STRING ot= { const_cast<char*>(tab), strlen(tab) }; - LEX_STRING nd= { const_cast<char*>(new_db), strlen(new_db) }; - LEX_STRING nt= { const_cast<char*>(new_tab), strlen(new_tab) }; + LEX_CSTRING od= { db, strlen(db) }; + LEX_CSTRING ot= { tab, strlen(tab) }; + LEX_CSTRING nd= { new_db, strlen(new_db) }; + LEX_CSTRING nt= { new_tab, strlen(new_tab) }; return rename_table_in_stat_tables(thd, &od, &ot, &nd, &nt); } diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 9808c081a54..e07a1cd6820 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -132,6 +132,61 @@ bool String::set_int(longlong num, bool unsigned_flag, CHARSET_INFO *cs) return FALSE; } + +// Convert a number into its HEX representation +bool String::set_hex(ulonglong num) +{ + char *n_end; + if (alloc(65) || !(n_end= longlong2str(num, Ptr, 16))) + return true; + length((uint32) (n_end - Ptr)); + set_charset(&my_charset_latin1); + return false; +} + + +/** + Append a hex representation of the byte "value" into "to". + Note: + "to" is incremented for the caller by two bytes. It's passed by reference! + So it resembles a macros, hence capital letters in the name. +*/ +static inline void APPEND_HEX(char *&to, uchar value) +{ + *to++= _dig_vec_upper[((uchar) value) >> 4]; + *to++= _dig_vec_upper[((uchar) value) & 0x0F]; +} + + +void String::qs_append_hex(const char *str, uint32 len) +{ + const char *str_end= str + len; + for (char *to= Ptr + str_length ; str < str_end; str++) + APPEND_HEX(to, (uchar) *str); + str_length+= len * 2; +} + + +// Convert a string to its HEX representation +bool String::set_hex(const char *str, uint32 len) +{ + /* + Safety: cut the source string if "len" is too large. + Note, alloc() can allocate some more space than requested, due to: + - ALIGN_SIZE + - one extra byte for a null terminator + So cut the source string to 0x7FFFFFF0 rather than 0x7FFFFFFE. + */ + set_if_smaller(len, 0x7FFFFFF0); + if (alloc(len * 2)) + return true; + length(0); + qs_append_hex(str, len); + set_charset(&my_charset_latin1); + return false; +} + + bool String::set_real(double num,uint decimals, CHARSET_INFO *cs) { char buff[FLOATING_POINT_BUFFER]; @@ -509,7 +564,7 @@ bool String::append_ulonglong(ulonglong val) with character set recoding */ -bool String::append(const char *s,uint32 arg_length, CHARSET_INFO *cs) +bool String::append(const char *s, uint arg_length, CHARSET_INFO *cs) { uint32 offset; @@ -973,8 +1028,7 @@ my_copy_with_hex_escaping(CHARSET_INFO *cs, break; /* purecov: inspected */ *dst++= '\\'; *dst++= 'x'; - *dst++= _dig_vec_upper[((unsigned char) *src) >> 4]; - *dst++= _dig_vec_upper[((unsigned char) *src) & 15]; + APPEND_HEX(dst, (uchar) *src); src++; dstlen-= 4; } @@ -1159,8 +1213,7 @@ uint convert_to_printable(char *to, size_t to_len, break; *t++= '\\'; *t++= 'x'; - *t++= _dig_vec_upper[((unsigned char) *f) >> 4]; - *t++= _dig_vec_upper[((unsigned char) *f) & 0x0F]; + APPEND_HEX(t, *f); } if (t_end - t >= 3) // '...' dots= t; diff --git a/sql/sql_string.h b/sql/sql_string.h index 1a4690a92f4..3908cdff252 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -293,6 +293,9 @@ public: bool set(ulonglong num, CHARSET_INFO *cs) { return set_int((longlong)num, true, cs); } bool set_real(double num,uint decimals, CHARSET_INFO *cs); + bool set_hex(ulonglong num); + bool set_hex(const char *str, uint32 len); + /* Take over handling of buffer from some other object */ void reset(char *ptr_arg, uint32 length_arg, uint32 alloced_length_arg, CHARSET_INFO *cs) @@ -466,8 +469,21 @@ public: } bool append(const String &s); bool append(const char *s); - bool append(const LEX_STRING *ls) { return append(ls->str, (uint32) ls->length); } - bool append(const LEX_CSTRING *ls) { return append(ls->str, (uint32) ls->length); } + bool append(const LEX_STRING *ls) + { + DBUG_ASSERT(ls->length < UINT_MAX32); + return append(ls->str, (uint32) ls->length); + } + bool append(const LEX_CSTRING *ls) + { + DBUG_ASSERT(ls->length < UINT_MAX32); + return append(ls->str, (uint32) ls->length); + } + bool append(const LEX_CSTRING &ls) + { + DBUG_ASSERT(ls.length < UINT_MAX32); + return append(ls.str, (uint32) ls.length); + } bool append(const char *s, size_t size); bool append(const char *s, uint arg_length, CHARSET_INFO *cs); bool append_ulonglong(ulonglong val); @@ -558,6 +574,11 @@ public: DBUG_ASSERT(str_length <= UINT_MAX32 - data_len); str_length += (uint)data_len; } + void q_append(const LEX_CSTRING *ls) + { + DBUG_ASSERT(ls->length < UINT_MAX32); + q_append(ls->str, (uint32) ls->length); + } void write_at_position(int position, uint32 value) { @@ -569,6 +590,7 @@ public: qs_append(str, (uint32)strlen(str)); } void qs_append(const char *str, uint32 len); + void qs_append_hex(const char *str, uint32 len); void qs_append(double d); void qs_append(double *d); inline void qs_append(const char c) diff --git a/sql/sql_table.cc b/sql/sql_table.cc index de367eda992..cfc571b22ef 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -54,7 +54,7 @@ #include "sql_show.h" #include "transaction.h" #include "sql_audit.h" - +#include "sql_sequence.h" #ifdef __WIN__ #include <io.h> @@ -65,7 +65,7 @@ const char *primary_key_name="PRIMARY"; static bool check_if_keyname_exists(const char *name,KEY *start, KEY *end); static char *make_unique_key_name(THD *thd, const char *field_name, KEY *start, KEY *end); -static void make_unique_constraint_name(THD *thd, LEX_STRING *name, +static void make_unique_constraint_name(THD *thd, LEX_CSTRING *name, List<Virtual_column_info> *vcol, uint *nr); static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, @@ -75,7 +75,6 @@ static int copy_data_between_tables(THD *thd, TABLE *from,TABLE *to, Alter_info::enum_enable_or_disable keys_onoff, Alter_table_ctx *alter_ctx); -static bool prepare_blob_field(THD *thd, Column_definition *sql_field); static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, uint *, handler *, KEY **, uint *, int); static uint blob_length_by_type(enum_field_types type); @@ -1085,7 +1084,7 @@ static bool deactivate_ddl_log_entry_no_lock(uint entry_no) static int execute_ddl_log_action(THD *thd, DDL_LOG_ENTRY *ddl_log_entry) { bool frm_action= FALSE; - LEX_STRING handler_name; + LEX_CSTRING handler_name; handler *file= NULL; MEM_ROOT mem_root; int error= TRUE; @@ -1990,6 +1989,8 @@ int write_bin_log(THD *thd, bool clear_error, thd Thread handle tables List of tables to delete if_exists If 1, don't give error if one table doesn't exists + drop_temporary 1 if DROP TEMPORARY + drop_seqeunce 1 if DROP SEQUENCE NOTES Will delete all tables that can be deleted and give a compact error @@ -2006,8 +2007,8 @@ int write_bin_log(THD *thd, bool clear_error, */ -bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, - my_bool drop_temporary) +bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, + bool drop_temporary, bool drop_sequence) { bool error; Drop_table_error_handler err_handler; @@ -2027,8 +2028,8 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, { for (table= tables; table; table= table->next_local) { - LEX_STRING db_name= { table->db, table->db_length }; - LEX_STRING table_name= { table->table_name, table->table_name_length }; + LEX_CSTRING db_name= { table->db, table->db_length }; + LEX_CSTRING table_name= { table->table_name, table->table_name_length }; if (table->open_type == OT_BASE_ONLY || !thd->find_temporary_table(table)) (void) delete_statistics_for_table(thd, &db_name, &table_name); @@ -2084,7 +2085,7 @@ bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, /* mark for close and remove all cached entries */ thd->push_internal_handler(&err_handler); error= mysql_rm_table_no_locks(thd, tables, if_exists, drop_temporary, - false, false, false); + false, drop_sequence, false, false); thd->pop_internal_handler(); if (error) @@ -2173,11 +2174,13 @@ static uint32 comment_length(THD *thd, uint32 comment_pos, int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, + bool drop_sequence, bool dont_log_query, bool dont_free_locks) { TABLE_LIST *table; - char path[FN_REFLEN + 1], wrong_tables_buff[160], *alias= NULL; + char path[FN_REFLEN + 1], wrong_tables_buff[160]; + const char *alias= NULL; String wrong_tables(wrong_tables_buff, sizeof(wrong_tables_buff)-1, system_charset_info); uint path_length= 0, errors= 0; @@ -2187,7 +2190,7 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool trans_tmp_table_deleted= 0, non_trans_tmp_table_deleted= 0; bool non_tmp_table_deleted= 0; bool is_drop_tmp_if_exists_added= 0; - bool was_view= 0; + bool was_view= 0, was_table= 0, is_sequence; String built_query; String built_trans_tmp_query, built_non_trans_tmp_query; DBUG_ENTER("mysql_rm_table_no_locks"); @@ -2229,17 +2232,21 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, */ if (!dont_log_query) { + const char *object_to_drop= (drop_sequence) ? "SEQUENCE" : "TABLE"; + if (!drop_temporary) { const char *comment_start; uint32 comment_len; built_query.set_charset(thd->charset()); + built_query.append("DROP "); + built_query.append(object_to_drop); + built_query.append(' '); if (if_exists) - built_query.append("DROP TABLE IF EXISTS "); - else - built_query.append("DROP TABLE "); + built_query.append("IF EXISTS "); + /* Preserve comment in original query */ if ((comment_len= comment_length(thd, if_exists ? 17:9, &comment_start))) { built_query.append(comment_start, comment_len); @@ -2247,28 +2254,24 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, } } + built_trans_tmp_query.set_charset(system_charset_info); + built_trans_tmp_query.append("DROP TEMPORARY "); + built_trans_tmp_query.append(object_to_drop); + built_trans_tmp_query.append(' '); if (thd->is_current_stmt_binlog_format_row() || if_exists) { is_drop_tmp_if_exists_added= true; - built_trans_tmp_query.set_charset(system_charset_info); - built_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS "); - built_non_trans_tmp_query.set_charset(system_charset_info); - built_non_trans_tmp_query.append("DROP TEMPORARY TABLE IF EXISTS "); - } - else - { - built_trans_tmp_query.set_charset(system_charset_info); - built_trans_tmp_query.append("DROP TEMPORARY TABLE "); - built_non_trans_tmp_query.set_charset(system_charset_info); - built_non_trans_tmp_query.append("DROP TEMPORARY TABLE "); + built_trans_tmp_query.append("IF EXISTS "); } + built_non_trans_tmp_query.set_charset(system_charset_info); + built_non_trans_tmp_query.copy(built_trans_tmp_query); } for (table= tables; table; table= table->next_local) { bool is_trans= 0; bool table_creation_was_logged= 1; - char *db=table->db; + const char *db= table->db; size_t db_length= table->db_length; handlerton *table_type= 0; @@ -2286,7 +2289,8 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, thd->find_temporary_table(table) && table->mdl_request.ticket != NULL)); - if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table)) + if (table->open_type == OT_BASE_ONLY || !is_temporary_table(table) || + (drop_sequence && table->table->s->table_type != TABLE_TYPE_SEQUENCE)) error= 1; else { @@ -2394,26 +2398,31 @@ int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, DEBUG_SYNC(thd, "rm_table_no_locks_before_delete_table"); error= 0; if (drop_temporary || - (ha_table_exists(thd, db, alias, &table_type) == 0 && table_type == 0) || - (!drop_view && (was_view= (table_type == view_pseudo_hton)))) + (ha_table_exists(thd, db, alias, &table_type, &is_sequence) == 0 && + table_type == 0) || + (!drop_view && (was_view= (table_type == view_pseudo_hton))) || + (drop_sequence && !is_sequence)) { /* One of the following cases happened: . "DROP TEMPORARY" but a temporary table was not found. . "DROP" but table was not found . "DROP TABLE" statement, but it's a view. + . "DROP SEQUENCE", but it's not a sequence */ + was_table= drop_sequence && table_type; if (if_exists) { char buff[FN_REFLEN]; + int err= (drop_sequence ? ER_UNKNOWN_SEQUENCES : + ER_BAD_TABLE_ERROR); String tbl_name(buff, sizeof(buff), system_charset_info); tbl_name.length(0); tbl_name.append(db); tbl_name.append('.'); tbl_name.append(table->table_name); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_BAD_TABLE_ERROR, - ER_THD(thd, ER_BAD_TABLE_ERROR), + err, ER_THD(thd, err), tbl_name.c_ptr_safe()); } else @@ -2530,8 +2539,12 @@ err: DBUG_ASSERT(errors); if (errors == 1 && was_view) my_error(ER_IT_IS_A_VIEW, MYF(0), wrong_tables.c_ptr_safe()); + else if (errors == 1 && drop_sequence && was_table) + my_error(ER_NOT_SEQUENCE2, MYF(0), wrong_tables.c_ptr_safe()); else if (errors > 1 || !thd->is_error()) - my_error(ER_BAD_TABLE_ERROR, MYF(0), wrong_tables.c_ptr_safe()); + my_error((drop_sequence ? ER_UNKNOWN_SEQUENCES : + ER_BAD_TABLE_ERROR), + MYF(0), wrong_tables.c_ptr_safe()); error= 1; } @@ -2842,205 +2855,98 @@ bool check_duplicates_in_interval(const char *set_or_name, } -/* - Check TYPELIB (set or enum) max and total lengths +bool Column_definition::prepare_stage2_blob(handler *file, + ulonglong table_flags, + uint field_flags) +{ + if (table_flags & HA_NO_BLOBS) + { + my_error(ER_TABLE_CANT_HANDLE_BLOB, MYF(0), file->table_type()); + return true; + } + pack_flag= field_flags | + pack_length_to_packflag(pack_length - portable_sizeof_char_ptr); + if (charset->state & MY_CS_BINSORT) + pack_flag|= FIELDFLAG_BINARY; + length= 8; // Unireg field length + return false; +} - SYNOPSIS - calculate_interval_lengths() - cs charset+collation pair of the interval - typelib list of values for the column - max_length length of the longest item - tot_length sum of the item lengths - DESCRIPTION - After this function call: - - ENUM uses max_length - - SET uses tot_length. +bool Column_definition::prepare_stage2_typelib(const char *type_name, + uint field_flags, + uint *dup_val_count) +{ + pack_flag= pack_length_to_packflag(pack_length) | field_flags; + if (charset->state & MY_CS_BINSORT) + pack_flag|= FIELDFLAG_BINARY; + return check_duplicates_in_interval(type_name, field_name.str, interval, + charset, dup_val_count); +} - RETURN VALUES - void -*/ -void calculate_interval_lengths(CHARSET_INFO *cs, TYPELIB *interval, - uint32 *max_length, uint32 *tot_length) + +uint Column_definition::pack_flag_numeric(uint dec) const +{ + return (FIELDFLAG_NUMBER | + (flags & UNSIGNED_FLAG ? 0 : FIELDFLAG_DECIMAL) | + (flags & ZEROFILL_FLAG ? FIELDFLAG_ZEROFILL : 0) | + (dec << FIELDFLAG_DEC_SHIFT)); +} + + +bool Column_definition::prepare_stage2_varchar(ulonglong table_flags) { - const char **pos; - uint *len; - *max_length= *tot_length= 0; - for (pos= interval->type_names, len= interval->type_lengths; - *pos ; pos++, len++) +#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR + if (table_flags & HA_NO_VARCHAR) { - size_t length= cs->cset->numchars(cs, *pos, *pos + *len); - *tot_length+= length; - set_if_bigger(*max_length, (uint32)length); + /* convert VARCHAR to CHAR because handler is not yet up to date */ + set_handler(&type_handler_var_string); + pack_length= type_handler()->calc_pack_length((uint) length); + if ((length / charset->mbmaxlen) > MAX_FIELD_CHARLENGTH) + { + my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name.str, + static_cast<ulong>(MAX_FIELD_CHARLENGTH)); + return true; + } } +#endif + pack_flag= (charset->state & MY_CS_BINSORT) ? FIELDFLAG_BINARY : 0; + return false; } /* - Prepare a create_table instance for packing - - SYNOPSIS - prepare_create_field() - sql_field field to prepare for packing - blob_columns count for BLOBs - table_flags table flags + Prepare a Column_definition instance for packing + Members such as pack_flag are valid after this call. - DESCRIPTION - This function prepares a Create_field instance. - Fields such as pack_flag are valid after this call. + @param IN handler - storage engine handler, + or NULL if preparing for an SP variable + @param IN table_flags - table flags - RETURN VALUES - 0 ok - 1 Error + @retval false - ok + @retval true - error (not supported type, bad definition, etc) */ -int prepare_create_field(Column_definition *sql_field, - uint *blob_columns, - ulonglong table_flags) +bool Column_definition::prepare_stage2(handler *file, + ulonglong table_flags) { - uint dup_val_count; - uint decimals= sql_field->decimals; - DBUG_ENTER("prepare_create_field"); + DBUG_ENTER("Column_definition::prepare_stage2"); /* This code came from mysql_prepare_create_table. Indent preserved to make patching easier */ - DBUG_ASSERT(sql_field->charset); + DBUG_ASSERT(charset); - switch (sql_field->sql_type) { - case MYSQL_TYPE_BLOB: - case MYSQL_TYPE_MEDIUM_BLOB: - case MYSQL_TYPE_TINY_BLOB: - case MYSQL_TYPE_LONG_BLOB: - sql_field->pack_flag=FIELDFLAG_BLOB | - pack_length_to_packflag(sql_field->pack_length - - portable_sizeof_char_ptr); - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|=FIELDFLAG_BINARY; - sql_field->length=8; // Unireg field length - (*blob_columns)++; - break; - case MYSQL_TYPE_GEOMETRY: -#ifdef HAVE_SPATIAL - if (!(table_flags & HA_CAN_GEOMETRY)) - { - my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "GEOMETRY"); - DBUG_RETURN(1); - } - sql_field->pack_flag=FIELDFLAG_GEOM | - pack_length_to_packflag(sql_field->pack_length - - portable_sizeof_char_ptr); - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|=FIELDFLAG_BINARY; - sql_field->length=8; // Unireg field length - (*blob_columns)++; - break; -#else - my_error(ER_FEATURE_DISABLED, MYF(0), - sym_group_geom.name, sym_group_geom.needed_define); - DBUG_RETURN(1); -#endif /*HAVE_SPATIAL*/ - case MYSQL_TYPE_VARCHAR: -#ifndef QQ_ALL_HANDLERS_SUPPORT_VARCHAR - if (table_flags & HA_NO_VARCHAR) - { - /* convert VARCHAR to CHAR because handler is not yet up to date */ - sql_field->sql_type= MYSQL_TYPE_VAR_STRING; - sql_field->pack_length= calc_pack_length(sql_field->sql_type, - (uint) sql_field->length); - if ((sql_field->length / sql_field->charset->mbmaxlen) > - MAX_FIELD_CHARLENGTH) - { - my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name, - static_cast<ulong>(MAX_FIELD_CHARLENGTH)); - DBUG_RETURN(1); - } - } -#endif - /* fall through */ - case MYSQL_TYPE_STRING: - sql_field->pack_flag=0; - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|=FIELDFLAG_BINARY; - break; - case MYSQL_TYPE_ENUM: - sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | - FIELDFLAG_INTERVAL; - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|=FIELDFLAG_BINARY; - if (check_duplicates_in_interval("ENUM",sql_field->field_name, - sql_field->interval, - sql_field->charset, &dup_val_count)) - DBUG_RETURN(1); - break; - case MYSQL_TYPE_SET: - sql_field->pack_flag=pack_length_to_packflag(sql_field->pack_length) | - FIELDFLAG_BITFIELD; - if (sql_field->charset->state & MY_CS_BINSORT) - sql_field->pack_flag|=FIELDFLAG_BINARY; - if (check_duplicates_in_interval("SET",sql_field->field_name, - sql_field->interval, - sql_field->charset, &dup_val_count)) - DBUG_RETURN(1); - /* Check that count of unique members is not more then 64 */ - if (sql_field->interval->count - dup_val_count > sizeof(longlong)*8) - { - my_error(ER_TOO_BIG_SET, MYF(0), sql_field->field_name); - DBUG_RETURN(1); - } - break; - case MYSQL_TYPE_DATE: // Rest of string types - case MYSQL_TYPE_NEWDATE: - case MYSQL_TYPE_TIME: - case MYSQL_TYPE_DATETIME: - case MYSQL_TYPE_TIME2: - case MYSQL_TYPE_DATETIME2: - case MYSQL_TYPE_NULL: - sql_field->pack_flag=f_settype((uint) sql_field->sql_type); - break; - case MYSQL_TYPE_BIT: - /* - We have sql_field->pack_flag already set here, see - mysql_prepare_create_table(). - */ - break; - case MYSQL_TYPE_NEWDECIMAL: - sql_field->pack_flag=(FIELDFLAG_NUMBER | - (sql_field->flags & UNSIGNED_FLAG ? 0 : - FIELDFLAG_DECIMAL) | - (sql_field->flags & ZEROFILL_FLAG ? - FIELDFLAG_ZEROFILL : 0) | - (decimals << FIELDFLAG_DEC_SHIFT)); - break; - case MYSQL_TYPE_FLOAT: - case MYSQL_TYPE_DOUBLE: - /* - User specified FLOAT() or DOUBLE() without precision. Change to - FLOATING_POINT_DECIMALS to keep things compatible with earlier MariaDB - versions. - */ - if (decimals >= FLOATING_POINT_DECIMALS) - decimals= FLOATING_POINT_DECIMALS; - /* fall through */ - case MYSQL_TYPE_TIMESTAMP: - case MYSQL_TYPE_TIMESTAMP2: - default: - sql_field->pack_flag=(FIELDFLAG_NUMBER | - (sql_field->flags & UNSIGNED_FLAG ? 0 : - FIELDFLAG_DECIMAL) | - (sql_field->flags & ZEROFILL_FLAG ? - FIELDFLAG_ZEROFILL : 0) | - f_settype((uint) sql_field->sql_type) | - (decimals << FIELDFLAG_DEC_SHIFT)); - break; - } - if (!(sql_field->flags & NOT_NULL_FLAG) || - (sql_field->vcol_info)) /* Make virtual columns allow NULL values */ - sql_field->pack_flag|= FIELDFLAG_MAYBE_NULL; - if (sql_field->flags & NO_DEFAULT_VALUE_FLAG) - sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT; - DBUG_RETURN(0); + if (type_handler()->Column_definition_prepare_stage2(this, file, table_flags)) + DBUG_RETURN(true); + + if (!(flags & NOT_NULL_FLAG) || + (vcol_info)) /* Make virtual columns allow NULL values */ + pack_flag|= FIELDFLAG_MAYBE_NULL; + if (flags & NO_DEFAULT_VALUE_FLAG) + pack_flag|= FIELDFLAG_NO_DEFAULT; + DBUG_RETURN(false); } @@ -3057,7 +2963,7 @@ int prepare_create_field(Column_definition *sql_field, cs Character set */ -CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, +CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, HA_CREATE_INFO *create_info) { CHARSET_INFO *cs= sql_field->charset; @@ -3094,7 +3000,7 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions) while ((column_definition= it++) != NULL) { - if (is_timestamp_type(column_definition->sql_type) || // TIMESTAMP + if (column_definition->is_timestamp_type() || // TIMESTAMP column_definition->unireg_check == Field::TIMESTAMP_OLD_FIELD) // Legacy { if ((column_definition->flags & NOT_NULL_FLAG) != 0 && // NOT NULL, @@ -3105,7 +3011,7 @@ void promote_first_timestamp_column(List<Create_field> *column_definitions) DBUG_PRINT("info", ("First TIMESTAMP column '%s' was promoted to " "DEFAULT CURRENT_TIMESTAMP ON UPDATE " "CURRENT_TIMESTAMP", - column_definition->field_name + column_definition->field_name.str )); column_definition->unireg_check= Field::TIMESTAMP_DNUN_FIELD; } @@ -3196,6 +3102,143 @@ static void check_duplicate_key(THD *thd, Key *key, KEY *key_info, } +bool Column_definition::prepare_stage1_typelib(THD *thd, + MEM_ROOT *mem_root, + handler *file, + ulonglong table_flags) +{ + /* + Pass the last parameter to prepare_interval_field() as follows: + - If we are preparing for an SP variable (file is NULL), we pass "false", + to force allocation and full copying of TYPELIB values on the given + mem_root, even if no character set conversion is needed. This is needed + because a life cycle of an SP variable is longer than the current query. + + - If we are preparing for a CREATE TABLE, (file != NULL), we pass "true". + This will create the typelib in runtime memory - we will free the + occupied memory at the same time when we free this + sql_field -- at the end of execution. + Pass "true" as the last argument to reuse "interval_list" + values in "interval" in cases when no character conversion is needed, + to avoid extra copying. + */ + if (prepare_interval_field(mem_root, file != NULL)) + return true; // E.g. wrong values with commas: SET('a,b') + create_length_to_internal_length_typelib(); + + DBUG_ASSERT(file || !default_value); // SP variables have no default_value + if (default_value && default_value->expr->basic_const_item()) + { + if ((charset != default_value->expr->collation.collation && + prepare_stage1_convert_default(thd, mem_root, charset)) || + prepare_stage1_check_typelib_default()) + return true; + } + return false; +} + + +bool Column_definition::prepare_stage1_string(THD *thd, + MEM_ROOT *mem_root, + handler *file, + ulonglong table_flags) +{ + create_length_to_internal_length_string(); + if (prepare_blob_field(thd)) + return true; + DBUG_ASSERT(file || !default_value); // SP variables have no default_value + /* + Convert the default value from client character + set into the column character set if necessary. + We can only do this for constants as we have not yet run fix_fields. + */ + if (default_value && + default_value->expr->basic_const_item() && + charset != default_value->expr->collation.collation) + { + if (prepare_stage1_convert_default(thd, mem_root, charset)) + return true; + } + return false; +} + + +bool Column_definition::prepare_stage1_bit(THD *thd, + MEM_ROOT *mem_root, + handler *file, + ulonglong table_flags) +{ + pack_flag= FIELDFLAG_NUMBER; + if (!(table_flags & HA_CAN_BIT_FIELD)) + pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR; + create_length_to_internal_length_bit(); + return false; +} + + +bool Column_definition::prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + handler *file, + ulonglong table_flags) +{ + return type_handler()->Column_definition_prepare_stage1(thd, mem_root, + this, file, + table_flags); +} + + +bool Column_definition::prepare_stage1_convert_default(THD *thd, + MEM_ROOT *mem_root, + CHARSET_INFO *cs) +{ + DBUG_ASSERT(thd->mem_root == mem_root); + Item *item; + if (!(item= default_value->expr->safe_charset_converter(thd, cs))) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); + return true; // Could not convert + } + /* Fix for prepare statement */ + thd->change_item_tree(&default_value->expr, item); + return false; +} + + +bool Column_definition::prepare_stage1_check_typelib_default() +{ + StringBuffer<MAX_FIELD_WIDTH> str; + String *def= default_value->expr->val_str(&str); + bool not_found; + if (def == NULL) /* SQL "NULL" maps to NULL */ + { + not_found= flags & NOT_NULL_FLAG; + } + else + { + not_found= false; + if (real_field_type() == MYSQL_TYPE_SET) + { + char *not_used; + uint not_used2; + find_set(interval, def->ptr(), def->length(), + charset, ¬_used, ¬_used2, ¬_found); + } + else /* MYSQL_TYPE_ENUM */ + { + def->length(charset->cset->lengthsp(charset, + def->ptr(), def->length())); + not_found= !find_type2(interval, def->ptr(), def->length(), charset); + } + } + if (not_found) + { + my_error(ER_INVALID_DEFAULT, MYF(0), field_name.str); + return true; + } + return false; +} + + /* Preparation for table creation @@ -3230,7 +3273,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { const char *key_name; Create_field *sql_field,*dup_field; - uint field,null_fields,blob_columns,max_key_length; + uint field,null_fields,max_key_length; ulong record_offset= 0; KEY *key_info; KEY_PART_INFO *key_part_info; @@ -3243,7 +3286,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, bool tmp_table= create_table_mode == C_ALTER_TABLE; DBUG_ENTER("mysql_prepare_create_table"); - LEX_STRING* connect_string = &create_info->connect_string; + LEX_CSTRING* connect_string = &create_info->connect_string; if (connect_string->length != 0 && connect_string->length > CONNECT_STRING_MAXLEN && (system_charset_info->cset->charpos(system_charset_info, @@ -3259,14 +3302,28 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } select_field_pos= alter_info->create_list.elements - select_field_count; - null_fields=blob_columns=0; + null_fields= 0; create_info->varchar= 0; max_key_length= file->max_key_length(); - for (field_no=0; (sql_field=it++) ; field_no++) + /* Handle creation of sequences */ + if (create_info->sequence) { - CHARSET_INFO *save_cs; + if (!(file->ha_table_flags() & HA_CAN_TABLES_WITHOUT_ROLLBACK)) + { + my_error(ER_ILLEGAL_HA_CREATE_OPTION, MYF(0), file->engine_name()->str, + "SEQUENCE"); + DBUG_RETURN(TRUE); + } + + /* The user specified fields: check that structure is ok */ + if (check_sequence_fields(thd->lex, &alter_info->create_list)) + DBUG_RETURN(TRUE); + } + select_field_pos= alter_info->create_list.elements - select_field_count; + for (field_no=0; (sql_field=it++) ; field_no++) + { /* Initialize length from its original value (number of characters), which was set in the parser. This is necessary if we're @@ -3274,175 +3331,25 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, */ sql_field->length= sql_field->char_length; /* Set field charset. */ - save_cs= sql_field->charset= get_sql_field_charset(sql_field, create_info); + sql_field->charset= get_sql_field_charset(sql_field, create_info); if ((sql_field->flags & BINCMP_FLAG) && - !(sql_field->charset= find_bin_collation(sql_field->charset))) - DBUG_RETURN(TRUE); - - if (sql_field->sql_type == MYSQL_TYPE_SET || - sql_field->sql_type == MYSQL_TYPE_ENUM) - { - uint32 dummy; - CHARSET_INFO *cs= sql_field->charset; - TYPELIB *interval= sql_field->interval; - - /* - Create typelib from interval_list, and if necessary - convert strings from client character set to the - column character set. - */ - if (!interval) - { - /* - Create the typelib in runtime memory - we will free the - occupied memory at the same time when we free this - sql_field -- at the end of execution. - */ - interval= sql_field->interval= typelib(thd->mem_root, - sql_field->interval_list); - List_iterator<String> int_it(sql_field->interval_list); - String conv, *tmp; - char comma_buf[5]; /* 5 bytes for 'filename' charset */ - DBUG_ASSERT(sizeof(comma_buf) >= cs->mbmaxlen); - int comma_length= cs->cset->wc_mb(cs, ',', (uchar*) comma_buf, - (uchar*) comma_buf + - sizeof(comma_buf)); - DBUG_ASSERT(comma_length > 0); - for (uint i= 0; (tmp= int_it++); i++) - { - size_t lengthsp; - if (String::needs_conversion(tmp->length(), tmp->charset(), - cs, &dummy)) - { - uint cnv_errs; - conv.copy(tmp->ptr(), tmp->length(), tmp->charset(), cs, &cnv_errs); - interval->type_names[i]= strmake_root(thd->mem_root, conv.ptr(), - conv.length()); - interval->type_lengths[i]= conv.length(); - } - - // Strip trailing spaces. - lengthsp= cs->cset->lengthsp(cs, interval->type_names[i], - interval->type_lengths[i]); - interval->type_lengths[i]= lengthsp; - ((uchar *)interval->type_names[i])[lengthsp]= '\0'; - if (sql_field->sql_type == MYSQL_TYPE_SET) - { - if (cs->coll->instr(cs, interval->type_names[i], - interval->type_lengths[i], - comma_buf, comma_length, NULL, 0)) - { - ErrConvString err(tmp->ptr(), tmp->length(), cs); - my_error(ER_ILLEGAL_VALUE_FOR_TYPE, MYF(0), "set", err.ptr()); - DBUG_RETURN(TRUE); - } - } - } - sql_field->interval_list.empty(); // Don't need interval_list anymore - } - - if (sql_field->sql_type == MYSQL_TYPE_SET) - { - uint32 field_length; - calculate_interval_lengths(cs, interval, &dummy, &field_length); - sql_field->length= field_length + (interval->count - 1); - } - else /* MYSQL_TYPE_ENUM */ - { - uint32 field_length; - DBUG_ASSERT(sql_field->sql_type == MYSQL_TYPE_ENUM); - calculate_interval_lengths(cs, interval, &field_length, &dummy); - sql_field->length= field_length; - } - set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1); - } - - if (sql_field->sql_type == MYSQL_TYPE_BIT) - { - sql_field->pack_flag= FIELDFLAG_NUMBER; - if (file->ha_table_flags() & HA_CAN_BIT_FIELD) - total_uneven_bit_length+= sql_field->length & 7; - else - sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR; - } - - sql_field->create_length_to_internal_length(); - if (prepare_blob_field(thd, sql_field)) - DBUG_RETURN(TRUE); - - /* - Convert the default value from client character - set into the column character set if necessary. - We can only do this for constants as we have not yet run fix_fields. - */ - if (sql_field->default_value && - sql_field->default_value->expr->basic_const_item() && - save_cs != sql_field->default_value->expr->collation.collation && - (sql_field->sql_type == MYSQL_TYPE_VAR_STRING || - sql_field->sql_type == MYSQL_TYPE_STRING || - sql_field->sql_type == MYSQL_TYPE_SET || - sql_field->sql_type == MYSQL_TYPE_TINY_BLOB || - sql_field->sql_type == MYSQL_TYPE_MEDIUM_BLOB || - sql_field->sql_type == MYSQL_TYPE_LONG_BLOB || - sql_field->sql_type == MYSQL_TYPE_BLOB || - sql_field->sql_type == MYSQL_TYPE_ENUM)) - { - Item *item; - if (!(item= sql_field->default_value->expr-> - safe_charset_converter(thd, save_cs))) - { - /* Could not convert */ - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - /* Fix for prepare statement */ - thd->change_item_tree(&sql_field->default_value->expr, item); - } + !(sql_field->charset= find_bin_collation(sql_field->charset))) + DBUG_RETURN(true); - if (sql_field->default_value && - sql_field->default_value->expr->basic_const_item() && - (sql_field->sql_type == MYSQL_TYPE_SET || - sql_field->sql_type == MYSQL_TYPE_ENUM)) - { - StringBuffer<MAX_FIELD_WIDTH> str; - String *def= sql_field->default_value->expr->val_str(&str); - bool not_found; - if (def == NULL) /* SQL "NULL" maps to NULL */ - { - not_found= sql_field->flags & NOT_NULL_FLAG; - } - else - { - not_found= false; - if (sql_field->sql_type == MYSQL_TYPE_SET) - { - char *not_used; - uint not_used2; - find_set(sql_field->interval, def->ptr(), def->length(), - sql_field->charset, ¬_used, ¬_used2, ¬_found); - } - else /* MYSQL_TYPE_ENUM */ - { - def->length(sql_field->charset->cset->lengthsp(sql_field->charset, - def->ptr(), def->length())); - not_found= !find_type2(sql_field->interval, def->ptr(), - def->length(), sql_field->charset); - } - } + if (sql_field->prepare_stage1(thd, thd->mem_root, + file, file->ha_table_flags())) + DBUG_RETURN(true); - if (not_found) - { - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); - DBUG_RETURN(TRUE); - } - } + if (sql_field->real_field_type() == MYSQL_TYPE_BIT && + file->ha_table_flags() & HA_CAN_BIT_FIELD) + total_uneven_bit_length+= sql_field->length & 7; if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields++; - if (check_column_name(sql_field->field_name)) + if (check_column_name(sql_field->field_name.str)) { - my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name); + my_error(ER_WRONG_COLUMN_NAME, MYF(0), sql_field->field_name.str); DBUG_RETURN(TRUE); } @@ -3450,8 +3357,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, for (dup_no=0; (dup_field=it2++) != sql_field; dup_no++) { if (my_strcasecmp(system_charset_info, - sql_field->field_name, - dup_field->field_name) == 0) + sql_field->field_name.str, + dup_field->field_name.str) == 0) { /* If this was a CREATE ... SELECT statement, accept a field @@ -3459,7 +3366,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, */ if (field_no < select_field_pos || dup_no >= select_field_pos) { - my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name); + my_error(ER_DUP_FIELDNAME, MYF(0), sql_field->field_name.str); DBUG_RETURN(TRUE); } else @@ -3470,34 +3377,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, If we are replacing a BIT field, revert the increment of total_uneven_bit_length that was done above. */ - if (sql_field->sql_type == MYSQL_TYPE_BIT && + if (sql_field->real_field_type() == MYSQL_TYPE_BIT && file->ha_table_flags() & HA_CAN_BIT_FIELD) total_uneven_bit_length-= sql_field->length & 7; - sql_field->default_value= dup_field->default_value; - sql_field->sql_type= dup_field->sql_type; - - /* - If we are replacing a field with a BIT field, we need - to initialize pack_flag. Note that we do not need to - increment total_uneven_bit_length here as this dup_field - has already been processed. - */ - if (sql_field->sql_type == MYSQL_TYPE_BIT) - { - sql_field->pack_flag= FIELDFLAG_NUMBER; - if (!(file->ha_table_flags() & HA_CAN_BIT_FIELD)) - sql_field->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR; - } - - sql_field->charset= (dup_field->charset ? - dup_field->charset : - create_info->default_table_charset); - sql_field->length= dup_field->char_length; - sql_field->pack_length= dup_field->pack_length; - sql_field->key_length= dup_field->key_length; - sql_field->decimals= dup_field->decimals; - sql_field->unireg_check= dup_field->unireg_check; /* We're making one field from two, the result field will have dup_field->flags as flags. If we've incremented null_fields @@ -3505,10 +3388,10 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, */ if (!(sql_field->flags & NOT_NULL_FLAG)) null_fields--; - sql_field->flags= dup_field->flags; - sql_field->create_length_to_internal_length(); - sql_field->interval= dup_field->interval; - sql_field->vcol_info= dup_field->vcol_info; + + if (sql_field->redefine_stage1(dup_field, file, create_info)) + DBUG_RETURN(true); + it2.remove(); // Remove first (create) definition select_field_pos--; break; @@ -3517,7 +3400,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } /* Don't pack rows in old tables if the user has requested this */ if ((sql_field->flags & BLOB_FLAG) || - (sql_field->sql_type == MYSQL_TYPE_VARCHAR && + (sql_field->real_field_type() == MYSQL_TYPE_VARCHAR && create_info->row_type != ROW_TYPE_FIXED)) (*db_options)|= HA_OPTION_PACK_RECORD; it2.rewind(); @@ -3532,10 +3415,9 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, { DBUG_ASSERT(sql_field->charset != 0); - if (prepare_create_field(sql_field, &blob_columns, - file->ha_table_flags())) + if (sql_field->prepare_stage2(file, file->ha_table_flags())) DBUG_RETURN(TRUE); - if (sql_field->sql_type == MYSQL_TYPE_VARCHAR) + if (sql_field->real_field_type() == MYSQL_TYPE_VARCHAR) create_info->varchar= TRUE; sql_field->offset= record_offset; if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) @@ -3575,12 +3457,6 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, DBUG_RETURN(TRUE); } - if (blob_columns && (file->ha_table_flags() & HA_NO_BLOBS)) - { - my_error(ER_TABLE_CANT_HANDLE_BLOB, MYF(0), file->table_type()); - DBUG_RETURN(TRUE); - } - /* CREATE TABLE[with auto_increment column] SELECT is unsafe as the rows inserted in the created table depends on the order of the rows fetched @@ -3816,7 +3692,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, while ((sql_field=it++) && my_strcasecmp(system_charset_info, column->field_name.str, - sql_field->field_name)) + sql_field->field_name.str)) field++; if (!sql_field) { @@ -3835,8 +3711,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, cols2.rewind(); if (key->type == Key::FULLTEXT) { - if ((sql_field->sql_type != MYSQL_TYPE_STRING && - sql_field->sql_type != MYSQL_TYPE_VARCHAR && + if ((sql_field->real_field_type() != MYSQL_TYPE_STRING && + sql_field->real_field_type() != MYSQL_TYPE_VARCHAR && !f_is_blob(sql_field->pack_flag)) || sql_field->charset == &my_charset_bin || sql_field->charset->mbminlen > 1 || // ucs2 doesn't work yet @@ -3914,7 +3790,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (sql_field->vcol_info->flags & VCOL_NOT_STRICTLY_DETERMINISTIC) { /* use check_expression() to report an error */ - check_expression(sql_field->vcol_info, sql_field->field_name, + check_expression(sql_field->vcol_info, &sql_field->field_name, VCOL_GENERATED_STORED); DBUG_ASSERT(thd->is_error()); DBUG_RETURN(TRUE); @@ -3962,7 +3838,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (f_is_blob(sql_field->pack_flag)) { key_part_length= MY_MIN(column->length, - blob_length_by_type(sql_field->sql_type) + blob_length_by_type(sql_field->real_field_type()) * sql_field->charset->mbmaxlen); if (key_part_length > max_key_length || key_part_length > file->max_key_part_length()) @@ -3992,7 +3868,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, // is prefix length bigger than field length? (column->length > key_part_length || // can the field have a partial key? - !Field::type_can_have_key_part (sql_field->sql_type) || + !sql_field->type_handler()->type_can_have_key_part() || // a packed field can't be used in a partial key f_is_packed(sql_field->pack_flag) || // does the storage engine allow prefixed search? @@ -4036,12 +3912,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (!((*db_options) & HA_OPTION_NO_PACK_KEYS) && !((create_info->table_options & HA_OPTION_NO_PACK_KEYS)) && (key_part_length >= KEY_DEFAULT_PACK_LENGTH && - (sql_field->sql_type == MYSQL_TYPE_STRING || - sql_field->sql_type == MYSQL_TYPE_VARCHAR || + (sql_field->real_field_type() == MYSQL_TYPE_STRING || + sql_field->real_field_type() == MYSQL_TYPE_VARCHAR || sql_field->pack_flag & FIELDFLAG_BLOB))) { if ((column_nr == 0 && (sql_field->pack_flag & FIELDFLAG_BLOB)) || - sql_field->sql_type == MYSQL_TYPE_VARCHAR) + sql_field->real_field_type() == MYSQL_TYPE_VARCHAR) key_info->flags|= HA_BINARY_PACK_KEY | HA_VAR_LENGTH_KEY; else key_info->flags|= HA_PACK_KEY; @@ -4068,7 +3944,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, primary_key=1; } else if (!(key_name= key->name.str)) - key_name=make_unique_key_name(thd, sql_field->field_name, + key_name=make_unique_key_name(thd, sql_field->field_name.str, *key_info_buffer, key_info); if (check_if_keyname_exists(key_name, *key_info_buffer, key_info)) { @@ -4093,7 +3969,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } if (validate_comment_length(thd, &key->key_create_info.comment, - INDEX_COMMENT_MAXLEN, ER_TOO_LONG_INDEX_COMMENT, + INDEX_COMMENT_MAXLEN, + ER_TOO_LONG_INDEX_COMMENT, key_info->name)) DBUG_RETURN(TRUE); @@ -4141,7 +4018,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (!sql_field->default_value && !sql_field->has_default_function() && (sql_field->flags & NOT_NULL_FLAG) && - !is_timestamp_type(sql_field->sql_type)) + !sql_field->is_timestamp_type()) { sql_field->flags|= NO_DEFAULT_VALUE_FLAG; sql_field->pack_flag|= FIELDFLAG_NO_DEFAULT; @@ -4149,7 +4026,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (thd->variables.sql_mode & MODE_NO_ZERO_DATE && !sql_field->default_value && !sql_field->vcol_info && - is_timestamp_type(sql_field->sql_type) && + sql_field->is_timestamp_type() && (sql_field->flags & NOT_NULL_FLAG) && (type == Field::NONE || type == Field::TIMESTAMP_UN_FIELD)) { @@ -4167,7 +4044,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, 'column_name TIMESTAMP DEFAULT 0'. */ - my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), sql_field->field_name.str); DBUG_RETURN(TRUE); } } @@ -4207,7 +4084,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, my_error(ER_TOO_LONG_IDENT, MYF(0), check->name.str); DBUG_RETURN(TRUE); } - if (check_expression(check, check->name.str, VCOL_CHECK_TABLE)) + if (check_expression(check, &check->name, VCOL_CHECK_TABLE)) DBUG_RETURN(TRUE); } } @@ -4250,7 +4127,7 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, @retval true Error found @retval false On Success */ -bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len, +bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len, uint err_code, const char *name) { DBUG_ENTER("validate_comment_length"); @@ -4286,7 +4163,8 @@ bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len, */ static void set_table_default_charset(THD *thd, - HA_CREATE_INFO *create_info, char *db) + HA_CREATE_INFO *create_info, + const char *db) { /* If the table character set was not given explicitly, @@ -4309,7 +4187,6 @@ static void set_table_default_charset(THD *thd, SYNOPSIS prepare_blob_field() - sql_field Field to check RETURN 0 ok @@ -4317,45 +4194,42 @@ static void set_table_default_charset(THD *thd, In this case the error is given */ -static bool prepare_blob_field(THD *thd, Column_definition *sql_field) +bool Column_definition::prepare_blob_field(THD *thd) { - DBUG_ENTER("prepare_blob_field"); + DBUG_ENTER("Column_definition::prepare_blob_field"); - if (sql_field->length > MAX_FIELD_VARCHARLENGTH && - !(sql_field->flags & BLOB_FLAG)) + if (length > MAX_FIELD_VARCHARLENGTH && !(flags & BLOB_FLAG)) { /* Convert long VARCHAR columns to TEXT or BLOB */ char warn_buff[MYSQL_ERRMSG_SIZE]; if (thd->is_strict_mode()) { - my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), sql_field->field_name, - static_cast<ulong>(MAX_FIELD_VARCHARLENGTH / - sql_field->charset->mbmaxlen)); + my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name.str, + static_cast<ulong>(MAX_FIELD_VARCHARLENGTH / charset->mbmaxlen)); DBUG_RETURN(1); } - sql_field->sql_type= MYSQL_TYPE_BLOB; - sql_field->flags|= BLOB_FLAG; + set_handler(&type_handler_blob); + flags|= BLOB_FLAG; my_snprintf(warn_buff, sizeof(warn_buff), ER_THD(thd, ER_AUTO_CONVERT), - sql_field->field_name, - (sql_field->charset == &my_charset_bin) ? "VARBINARY" : - "VARCHAR", - (sql_field->charset == &my_charset_bin) ? "BLOB" : "TEXT"); + field_name.str, + (charset == &my_charset_bin) ? "VARBINARY" : "VARCHAR", + (charset == &my_charset_bin) ? "BLOB" : "TEXT"); push_warning(thd, Sql_condition::WARN_LEVEL_NOTE, ER_AUTO_CONVERT, warn_buff); } - if ((sql_field->flags & BLOB_FLAG) && sql_field->length) + if ((flags & BLOB_FLAG) && length) { - if (sql_field->sql_type == FIELD_TYPE_BLOB || - sql_field->sql_type == FIELD_TYPE_TINY_BLOB || - sql_field->sql_type == FIELD_TYPE_MEDIUM_BLOB) + if (real_field_type() == FIELD_TYPE_BLOB || + real_field_type() == FIELD_TYPE_TINY_BLOB || + real_field_type() == FIELD_TYPE_MEDIUM_BLOB) { /* The user has given a length to the blob column */ - sql_field->sql_type= get_blob_type_from_length((ulong)sql_field->length); - sql_field->pack_length= calc_pack_length(sql_field->sql_type, 0); + set_handler(Type_handler::blob_type_handler((uint) length)); + pack_length= type_handler()->calc_pack_length(0); } - sql_field->length= 0; + length= 0; } DBUG_RETURN(0); } @@ -4368,47 +4242,18 @@ static bool prepare_blob_field(THD *thd, Column_definition *sql_field) SYNOPSIS sp_prepare_create_field() - thd Thread object - sql_field Field to prepare + thd Thread object + mem_root Memory root to allocate components on (e.g. interval) DESCRIPTION Prepares the field structures for field creation. */ -void sp_prepare_create_field(THD *thd, Column_definition *sql_field) +bool Column_definition::sp_prepare_create_field(THD *thd, MEM_ROOT *mem_root) { - if (sql_field->sql_type == MYSQL_TYPE_SET || - sql_field->sql_type == MYSQL_TYPE_ENUM) - { - uint32 field_length, dummy; - if (sql_field->sql_type == MYSQL_TYPE_SET) - { - calculate_interval_lengths(sql_field->charset, - sql_field->interval, &dummy, - &field_length); - sql_field->length= field_length + - (sql_field->interval->count - 1); - } - else /* MYSQL_TYPE_ENUM */ - { - calculate_interval_lengths(sql_field->charset, - sql_field->interval, - &field_length, &dummy); - sql_field->length= field_length; - } - set_if_smaller(sql_field->length, MAX_FIELD_WIDTH-1); - } - - if (sql_field->sql_type == MYSQL_TYPE_BIT) - { - sql_field->pack_flag= FIELDFLAG_NUMBER | - FIELDFLAG_TREAT_BIT_AS_CHAR; - } - sql_field->create_length_to_internal_length(); - DBUG_ASSERT(sql_field->default_value == 0); - /* Can't go wrong as sql_field->def is not defined */ - (void) prepare_blob_field(thd, sql_field); + return prepare_stage1(thd, mem_root, NULL, HA_CAN_GEOMETRY) || + prepare_stage2(NULL, HA_CAN_GEOMETRY); } @@ -4485,15 +4330,16 @@ handler *mysql_create_frm_image(THD *thd, { if (part_elem->part_comment) { - LEX_STRING comment= { - part_elem->part_comment, strlen(part_elem->part_comment) + LEX_CSTRING comment= { part_elem->part_comment, + strlen(part_elem->part_comment) }; if (validate_comment_length(thd, &comment, TABLE_PARTITION_COMMENT_MAXLEN, ER_TOO_LONG_TABLE_PARTITION_COMMENT, part_elem->partition_name)) DBUG_RETURN(NULL); - part_elem->part_comment[comment.length]= '\0'; + /* cut comment length. Safe to do in all cases */ + ((char*)part_elem->part_comment)[comment.length]= '\0'; } if (part_elem->subpartitions.elements) { @@ -4503,7 +4349,7 @@ handler *mysql_create_frm_image(THD *thd, { if (subpart_elem->part_comment) { - LEX_STRING comment= { + LEX_CSTRING comment= { subpart_elem->part_comment, strlen(subpart_elem->part_comment) }; if (validate_comment_length(thd, &comment, @@ -4511,7 +4357,8 @@ handler *mysql_create_frm_image(THD *thd, ER_TOO_LONG_TABLE_PARTITION_COMMENT, subpart_elem->partition_name)) DBUG_RETURN(NULL); - subpart_elem->part_comment[comment.length]= '\0'; + /* cut comment length. Safe to do in all cases */ + ((char*)subpart_elem->part_comment)[comment.length]= '\0'; } } } @@ -4808,7 +4655,7 @@ int create_table_impl(THD *thd, */ (void) trans_rollback_stmt(thd); /* Remove normal table without logging. Keep tables locked */ - if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 1, 1)) + if (mysql_rm_table_no_locks(thd, &table_list, 0, 0, 0, 0, 1, 1)) goto err; /* @@ -4955,7 +4802,7 @@ int create_table_impl(THD *thd, } } #endif - + error= 0; err: THD_STAGE_INFO(thd, stage_after_create); @@ -4978,10 +4825,11 @@ warn: */ int mysql_create_table_no_lock(THD *thd, - const char *db, const char *table_name, - Table_specification_st *create_info, - Alter_info *alter_info, bool *is_trans, - int create_table_mode) + const char *db, const char *table_name, + Table_specification_st *create_info, + Alter_info *alter_info, bool *is_trans, + int create_table_mode, + TABLE_LIST *table_list) { KEY *not_used_1; uint not_used_2; @@ -5000,7 +4848,7 @@ int mysql_create_table_no_lock(THD *thd, // Check if we hit FN_REFLEN bytes along with file extension. if (length+reg_ext_length > FN_REFLEN) { - my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(path)-1, path); + my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), (int) sizeof(path)-1, path); return true; } } @@ -5010,6 +4858,17 @@ int mysql_create_table_no_lock(THD *thd, alter_info, create_table_mode, is_trans, ¬_used_1, ¬_used_2, &frm); my_free(const_cast<uchar*>(frm.str)); + + if (!res && create_info->sequence) + { + /* Set create_info.table if temporary table */ + if (create_info->tmp_table()) + table_list->table= create_info->table; + else + table_list->table= 0; + res= sequence_insert(thd, thd->lex, table_list); + } + return res; } @@ -5071,7 +4930,8 @@ bool mysql_create_table(THD *thd, TABLE_LIST *create_table, promote_first_timestamp_column(&alter_info->create_list); if (mysql_create_table_no_lock(thd, db, table_name, create_info, alter_info, - &is_trans, create_table_mode) > 0) + &is_trans, create_table_mode, + create_table) > 0) { result= 1; goto err; @@ -5178,7 +5038,7 @@ make_unique_key_name(THD *thd, const char *field_name,KEY *start,KEY *end) Make an unique name for constraints without a name */ -static void make_unique_constraint_name(THD *thd, LEX_STRING *name, +static void make_unique_constraint_name(THD *thd, LEX_CSTRING *name, List<Virtual_column_info> *vcol, uint *nr) { @@ -5264,7 +5124,7 @@ mysql_rename_table(handlerton *base, const char *old_db, // Check if we hit FN_REFLEN bytes along with file extension. if (length+reg_ext_length > FN_REFLEN) { - my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), sizeof(to)-1, to); + my_error(ER_IDENT_CAUSES_TOO_LONG_PATH, MYF(0), (int) sizeof(to)-1, to); DBUG_RETURN(TRUE); } @@ -5461,7 +5321,8 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, res= ((create_res= mysql_create_table_no_lock(thd, table->db, table->table_name, &local_create_info, &local_alter_info, - &is_trans, C_ORDINARY_CREATE)) > 0); + &is_trans, C_ORDINARY_CREATE, + table)) > 0); /* Remember to log if we deleted something */ do_logging= thd->log_current_statement; if (res) @@ -5702,7 +5563,7 @@ int mysql_discard_or_import_tablespace(THD *thd, table_list->mdl_request.set_type(MDL_EXCLUSIVE); table_list->lock_type= TL_WRITE; /* Do not open views. */ - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; if (open_and_lock_tables(thd, table_list, FALSE, 0, &alter_prelocking_strategy)) @@ -5728,9 +5589,8 @@ int mysql_discard_or_import_tablespace(THD *thd, error= trans_commit_stmt(thd); if (trans_commit_implicit(thd)) error=1; - if (error) - goto err; - error= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); + if (!error) + error= write_bin_log(thd, FALSE, thd->query(), thd->query_length()); err: thd->tablespace_op=FALSE; @@ -5799,7 +5659,7 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) while ((sql_field=it++)) { - if (!sql_field->create_if_not_exists || sql_field->change) + if (!sql_field->create_if_not_exists || sql_field->change.str) continue; /* If there is a field with the same name in the table already, @@ -5808,7 +5668,7 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) for (f_ptr=table->field; *f_ptr; f_ptr++) { if (my_strcasecmp(system_charset_info, - sql_field->field_name, (*f_ptr)->field_name) == 0) + sql_field->field_name.str, (*f_ptr)->field_name.str) == 0) goto drop_create_field; } { @@ -5821,7 +5681,7 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) while ((chk_field= chk_it++) && chk_field != sql_field) { if (my_strcasecmp(system_charset_info, - sql_field->field_name, chk_field->field_name) == 0) + sql_field->field_name.str, chk_field->field_name.str) == 0) goto drop_create_field; } } @@ -5829,7 +5689,7 @@ handle_if_exists_options(THD *thd, TABLE *table, Alter_info *alter_info) drop_create_field: push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_DUP_FIELDNAME, ER_THD(thd, ER_DUP_FIELDNAME), - sql_field->field_name); + sql_field->field_name.str); it.remove(); if (alter_info->create_list.is_empty()) { @@ -5848,7 +5708,7 @@ drop_create_field: while ((sql_field=it++)) { - if (!sql_field->create_if_not_exists || !sql_field->change) + if (!sql_field->create_if_not_exists || !sql_field->change.str) continue; /* If there is NO field with the same name in the table already, @@ -5857,7 +5717,7 @@ drop_create_field: for (f_ptr=table->field; *f_ptr; f_ptr++) { if (my_strcasecmp(system_charset_info, - sql_field->change, (*f_ptr)->field_name) == 0) + sql_field->change.str, (*f_ptr)->field_name.str) == 0) { break; } @@ -5867,7 +5727,7 @@ drop_create_field: push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_BAD_FIELD_ERROR, ER_THD(thd, ER_BAD_FIELD_ERROR), - sql_field->change, table->s->table_name.str); + sql_field->change.str, table->s->table_name.str); it.remove(); if (alter_info->create_list.is_empty()) { @@ -5899,7 +5759,7 @@ drop_create_field: for (f_ptr=table->field; *f_ptr; f_ptr++) { if (my_strcasecmp(system_charset_info, - drop->name, (*f_ptr)->field_name) == 0) + drop->name, (*f_ptr)->field_name.str) == 0) { remove_drop= FALSE; break; @@ -6130,8 +5990,8 @@ remove_key: if ((alter_info->flags & Alter_info::ALTER_DROP_PARTITION) && thd->lex->if_exists()) { - List_iterator<char> names_it(alter_info->partition_names); - char *name; + List_iterator<const char> names_it(alter_info->partition_names); + const char *name; while ((name= names_it++)) { @@ -6483,13 +6343,13 @@ static bool fill_alter_inplace_info(THD *thd, } /* Check if field was renamed */ - if (my_strcasecmp(system_charset_info, field->field_name, - new_field->field_name)) + if (my_strcasecmp(system_charset_info, field->field_name.str, + new_field->field_name.str)) { field->flags|= FIELD_IS_RENAMED; ha_alter_info->handler_flags|= Alter_inplace_info::ALTER_COLUMN_NAME; rename_column_in_stat_tables(thd, table, field, - new_field->field_name); + new_field->field_name.str); } /* Check that NULL behavior is same for old and new fields */ @@ -6934,14 +6794,14 @@ bool mysql_compare_tables(TABLE *table, if (create_info->row_type == ROW_TYPE_DYNAMIC || create_info->row_type == ROW_TYPE_PAGE || (tmp_new_field->flags & BLOB_FLAG) || - (tmp_new_field->sql_type == MYSQL_TYPE_VARCHAR && + (tmp_new_field->real_field_type() == MYSQL_TYPE_VARCHAR && create_info->row_type != ROW_TYPE_FIXED)) create_info->table_options|= HA_OPTION_PACK_RECORD; /* Check if field was renamed */ if (my_strcasecmp(system_charset_info, - field->field_name, - tmp_new_field->field_name)) + field->field_name.str, + tmp_new_field->field_name.str)) DBUG_RETURN(false); /* Evaluate changes bitmap and send to check_if_incompatible_data() */ @@ -7486,7 +7346,7 @@ blob_length_by_type(enum_field_types type) case MYSQL_TYPE_MEDIUM_BLOB: return 16777215; case MYSQL_TYPE_LONG_BLOB: - return 4294967295U; + return (uint) UINT_MAX32; default: DBUG_ASSERT(0); // we should never go here return 0; @@ -7606,6 +7466,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (!(used_fields & HA_CREATE_USED_CONNECTION)) create_info->connect_string= table->s->connect_string; + if (!(used_fields & HA_CREATE_USED_SEQUENCE)) + create_info->sequence= table->s->table_type == TABLE_TYPE_SEQUENCE; + restore_record(table, s->default_values); // Empty record for DEFAULT if ((create_info->fields_option_struct= (ha_field_option_struct**) @@ -7631,8 +7494,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, while ((drop=drop_it++)) { if (drop->type == Alter_drop::COLUMN && - !my_strcasecmp(system_charset_info, field->field_name, drop->name)) - break; + !my_strcasecmp(system_charset_info,field->field_name.str, drop->name)) + break; } if (drop) { @@ -7654,8 +7517,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, def_it.rewind(); while ((def=def_it++)) { - if (def->change && - !my_strcasecmp(system_charset_info,field->field_name, def->change)) + if (def->change.str && + !my_strcasecmp(system_charset_info,field->field_name.str, + def->change.str)) break; } if (def) @@ -7672,7 +7536,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, my_error(ER_UNSUPPORTED_ACTION_ON_GENERATED_COLUMN, MYF(0)); goto err; } - if (!def->after) + if (!def->after.str) { /* If this ALTER TABLE doesn't have an AFTER clause for the modified @@ -7696,7 +7560,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Alter_column *alter; while ((alter=alter_it++)) { - if (!my_strcasecmp(system_charset_info,field->field_name, alter->name)) + if (!my_strcasecmp(system_charset_info,field->field_name.str, + alter->name)) break; } if (alter) @@ -7712,7 +7577,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, def_it.rewind(); while ((def=def_it++)) // Add new columns { - if (def->change && ! def->field) + if (def->change.str && ! def->field) { /* Check if there is modify for newly added field. @@ -7721,7 +7586,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, find_it.rewind(); while((find=find_it++)) { - if (!my_strcasecmp(system_charset_info,find->field_name, def->field_name)) + if (!my_strcasecmp(system_charset_info,find->field_name.str, + def->field_name.str)) break; } @@ -7729,7 +7595,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, find_it.remove(); else { - my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change, + my_error(ER_BAD_FIELD_ERROR, MYF(0), def->change.str, table->s->table_name.str); goto err; } @@ -7741,10 +7607,10 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, If the '0000-00-00' value isn't allowed then raise the error_if_not_empty flag to allow ALTER TABLE only if the table to be altered is empty. */ - if ((def->sql_type == MYSQL_TYPE_DATE || - def->sql_type == MYSQL_TYPE_NEWDATE || - def->sql_type == MYSQL_TYPE_DATETIME || - def->sql_type == MYSQL_TYPE_DATETIME2) && + if ((def->real_field_type() == MYSQL_TYPE_DATE || + def->real_field_type() == MYSQL_TYPE_NEWDATE || + def->real_field_type() == MYSQL_TYPE_DATETIME || + def->real_field_type() == MYSQL_TYPE_DATETIME2) && !alter_ctx->datetime_field && !(~def->flags & (NO_DEFAULT_VALUE_FLAG | NOT_NULL_FLAG)) && thd->variables.sql_mode & MODE_NO_ZERO_DATE) @@ -7752,12 +7618,12 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, alter_ctx->datetime_field= def; alter_ctx->error_if_not_empty= TRUE; } - if (!def->after) + if (!def->after.str) new_create_list.push_back(def, thd->mem_root); else { Create_field *find; - if (def->change) + if (def->change.str) { find_it.rewind(); /* @@ -7780,19 +7646,21 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } } } - if (def->after == first_keyword) + if (def->after.str == first_keyword) new_create_list.push_front(def, thd->mem_root); else { find_it.rewind(); while ((find=find_it++)) { - if (!my_strcasecmp(system_charset_info, def->after, find->field_name)) + if (!my_strcasecmp(system_charset_info, def->after.str, + find->field_name.str)) break; } if (!find) { - my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after, table->s->table_name.str); + my_error(ER_BAD_FIELD_ERROR, MYF(0), def->after.str, + table->s->table_name.str); goto err; } find_it.after(def); // Put column after this @@ -7805,12 +7673,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Alter_column *alter; while ((alter=alter_it++)) { - if (!my_strcasecmp(system_charset_info,def->field_name, alter->name)) + if (!my_strcasecmp(system_charset_info,def->field_name.str, + alter->name)) break; } if (alter) { - if (def->sql_type == MYSQL_TYPE_BLOB) + if (def->real_field_type() == MYSQL_TYPE_BLOB) { my_error(ER_BLOB_CANT_HAVE_DEFAULT, MYF(0), def->change); goto err; @@ -7843,7 +7712,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, for (uint i=0 ; i < table->s->keys ; i++,key_info++) { - char *key_name= key_info->name; + const char *key_name= key_info->name; Alter_drop *drop; drop_it.rewind(); while ((drop=drop_it++)) @@ -7881,21 +7750,21 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, { if (!key_part->field) continue; // Wrong field (from UNIREG) - const char *key_part_name=key_part->field->field_name; + const char *key_part_name=key_part->field->field_name.str; Create_field *cfield; uint key_part_length; field_it.rewind(); while ((cfield=field_it++)) { - if (cfield->change) + if (cfield->change.str) { if (!my_strcasecmp(system_charset_info, key_part_name, - cfield->change)) + cfield->change.str)) break; } else if (!my_strcasecmp(system_charset_info, - key_part_name, cfield->field_name)) + key_part_name, cfield->field_name.str)) break; } if (!cfield) @@ -7929,22 +7798,22 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, - data type maximum length is 255. - key_part_length is 1016 (=254*4, where 4 is mbmaxlen) */ - if (!Field::type_can_have_key_part(cfield->field->type()) || - !Field::type_can_have_key_part(cfield->sql_type) || + if (!cfield->field->type_handler()->type_can_have_key_part() || + !cfield->type_handler()->type_can_have_key_part() || /* spatial keys can't have sub-key length */ (key_info->flags & HA_SPATIAL) || (cfield->field->field_length == key_part_length && !f_is_blob(key_part->key_type)) || - (cfield->length && (((cfield->sql_type >= MYSQL_TYPE_TINY_BLOB && - cfield->sql_type <= MYSQL_TYPE_BLOB) ? - blob_length_by_type(cfield->sql_type) : - cfield->length) < + (cfield->length && + (((cfield->real_field_type() >= MYSQL_TYPE_TINY_BLOB && + cfield->real_field_type() <= MYSQL_TYPE_BLOB) ? + blob_length_by_type(cfield->real_field_type()) : + cfield->length) < key_part_length / key_part->field->charset()->mbmaxlen))) key_part_length= 0; // Use whole field } key_part_length /= key_part->field->charset()->mbmaxlen; - key_parts.push_back(new Key_part_spec(cfield->field_name, - strlen(cfield->field_name), + key_parts.push_back(new Key_part_spec(&cfield->field_name, key_part_length), thd->mem_root); } @@ -7962,8 +7831,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, KEY_CREATE_INFO key_create_info; Key *key; enum Key::Keytype key_type; + LEX_CSTRING tmp_name; bzero((char*) &key_create_info, sizeof(key_create_info)); - key_create_info.algorithm= key_info->algorithm; if (key_info->flags & HA_USES_BLOCK_SIZE) key_create_info.block_size= key_info->block_size; @@ -7997,10 +7866,11 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, else key_type= Key::MULTIPLE; - key= new Key(key_type, key_name, strlen(key_name), - &key_create_info, + tmp_name.str= key_name; + tmp_name.length= strlen(key_name); + key= new Key(key_type, &tmp_name, &key_create_info, MY_TEST(key_info->flags & HA_GENERATED_KEY), - key_parts, key_info->option_list, DDL_options()); + &key_parts, key_info->option_list, DDL_options()); new_key_list.push_back(key, thd->mem_root); } } @@ -8054,7 +7924,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, bitmap_intersect(table->read_set, dropped_fields); uint field_nr= bitmap_get_first_set(table->read_set); my_error(ER_BAD_FIELD_ERROR, MYF(0), - table->field[field_nr]->field_name, "CHECK"); + table->field[field_nr]->field_name.str, "CHECK"); goto err; } } @@ -8142,7 +8012,7 @@ static Create_field *get_field_by_old_name(Alter_info *alter_info, { if (new_field->field && (my_strcasecmp(system_charset_info, - new_field->field->field_name, + new_field->field->field_name.str, old_name) == 0)) break; } @@ -8182,11 +8052,11 @@ enum fk_column_change_type static enum fk_column_change_type fk_check_column_changes(THD *thd, Alter_info *alter_info, - List<LEX_STRING> &fk_columns, + List<LEX_CSTRING> &fk_columns, const char **bad_column_name) { - List_iterator_fast<LEX_STRING> column_it(fk_columns); - LEX_STRING *column; + List_iterator_fast<LEX_CSTRING> column_it(fk_columns); + LEX_CSTRING *column; *bad_column_name= NULL; @@ -8198,8 +8068,8 @@ fk_check_column_changes(THD *thd, Alter_info *alter_info, { Field *old_field= new_field->field; - if (my_strcasecmp(system_charset_info, old_field->field_name, - new_field->field_name)) + if (my_strcasecmp(system_charset_info, old_field->field_name.str, + new_field->field_name.str)) { /* Copy algorithm doesn't support proper renaming of columns in @@ -8363,7 +8233,7 @@ static bool fk_prepare_copy_alter_table(THD *thd, TABLE *table, case FK_COLUMN_DROPPED: { StringBuffer<NAME_LEN*2+2> buff(system_charset_info); - LEX_STRING *db= f_key->foreign_db, *tbl= f_key->foreign_table; + LEX_CSTRING *db= f_key->foreign_db, *tbl= f_key->foreign_table; append_identifier(thd, &buff, db->str, db->length); buff.append('.'); @@ -8565,11 +8435,11 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(true); close_all_tables_for_name(thd, table->s, HA_EXTRA_PREPARE_FOR_RENAME, NULL); - LEX_STRING old_db_name= { alter_ctx->db, strlen(alter_ctx->db) }; - LEX_STRING old_table_name= + LEX_CSTRING old_db_name= { alter_ctx->db, strlen(alter_ctx->db) }; + LEX_CSTRING old_table_name= { alter_ctx->table_name, strlen(alter_ctx->table_name) }; - LEX_STRING new_db_name= { alter_ctx->new_db, strlen(alter_ctx->new_db) }; - LEX_STRING new_table_name= + LEX_CSTRING new_db_name= { alter_ctx->new_db, strlen(alter_ctx->new_db) }; + LEX_CSTRING new_table_name= { alter_ctx->new_alias, strlen(alter_ctx->new_alias) }; (void) rename_table_in_stat_tables(thd, &old_db_name, &old_table_name, &new_db_name, &new_table_name); @@ -8657,7 +8527,7 @@ simple_rename_or_index_change(THD *thd, TABLE_LIST *table_list, based on information about the table changes from fill_alter_inplace_info(). */ -bool mysql_alter_table(THD *thd,char *new_db, char *new_name, +bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, Alter_info *alter_info, @@ -8708,7 +8578,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Note that RENAME TABLE the only ALTER clause which is supported for views has been already processed. */ - table_list->required_type= FRMTYPE_TABLE; + table_list->required_type= TABLE_TYPE_NORMAL; Alter_table_prelocking_strategy alter_prelocking_strategy; @@ -8815,7 +8685,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, Table maybe does not exist, but we got an exclusive lock on the name, now we can safely try to find out for sure. */ - if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name, 0)) + if (ha_table_exists(thd, alter_ctx.new_db, alter_ctx.new_name)) { /* Table will be closed in do_command() */ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), alter_ctx.new_alias); @@ -9548,7 +9418,7 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name, */ char backup_name[32]; my_snprintf(backup_name, sizeof(backup_name), "%s2-%lx-%lx", tmp_file_prefix, - current_pid, thd->thread_id); + current_pid, (long) thd->thread_id); if (lower_case_table_names) my_casedn_str(files_charset_info, backup_name); if (mysql_rename_table(old_db_type, alter_ctx.db, alter_ctx.table_name, @@ -9668,7 +9538,7 @@ err_new_table_cleanup: { const char *f_val= 0; enum enum_mysql_timestamp_type t_type= MYSQL_TIMESTAMP_DATE; - switch (alter_ctx.datetime_field->sql_type) + switch (alter_ctx.datetime_field->real_field_type()) { case MYSQL_TYPE_DATE: case MYSQL_TYPE_NEWDATE: @@ -9688,7 +9558,7 @@ err_new_table_cleanup: thd->abort_on_warning= true; make_truncated_value_warning(thd, Sql_condition::WARN_LEVEL_WARN, f_val, strlength(f_val), t_type, - alter_ctx.datetime_field->field_name); + alter_ctx.datetime_field->field_name.str); thd->abort_on_warning= save_abort_on_warning; } @@ -9808,6 +9678,7 @@ copy_data_between_tables(THD *thd, TABLE *from, TABLE *to, thd->abort_on_warning= !ignore && thd->is_strict_mode(); from->file->info(HA_STATUS_VARIABLE); + to->file->extra(HA_EXTRA_PREPARE_FOR_ALTER_TABLE); to->file->ha_start_bulk_insert(from->file->stats.records, ignore ? 0 : HA_CREATE_UNIQUE_INDEX_BY_SORT); @@ -10164,7 +10035,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, table->next_global= NULL; table->lock_type= TL_READ; /* Allow to open real tables only. */ - table->required_type= FRMTYPE_TABLE; + table->required_type= TABLE_TYPE_NORMAL; if (thd->open_temporary_tables(table) || open_and_lock_tables(thd, table, FALSE, 0)) @@ -10350,7 +10221,7 @@ bool check_engine(THD *thd, const char *db_name, if (no_substitution) { const char *engine_name= ha_resolve_storage_engine_name(req_engine); - my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), engine_name, engine_name); + my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), engine_name); DBUG_RETURN(TRUE); } *new_engine= enf_engine; diff --git a/sql/sql_table.h b/sql/sql_table.h index 5abb25d1ea5..2e080462deb 100644 --- a/sql/sql_table.h +++ b/sql/sql_table.h @@ -196,7 +196,7 @@ int mysql_create_table_no_lock(THD *thd, const char *db, const char *table_name, Table_specification_st *create_info, Alter_info *alter_info, bool *is_trans, - int create_table_mode); + int create_table_mode, TABLE_LIST *table); handler *mysql_create_frm_image(THD *thd, const char *db, const char *table_name, @@ -217,7 +217,7 @@ bool mysql_prepare_alter_table(THD *thd, TABLE *table, Alter_table_ctx *alter_ctx); bool mysql_trans_prepare_alter_copy_data(THD *thd); bool mysql_trans_commit_alter_copy_data(THD *thd); -bool mysql_alter_table(THD *thd, char *new_db, char *new_name, +bool mysql_alter_table(THD *thd, const char *new_db, const char *new_name, HA_CREATE_INFO *create_info, TABLE_LIST *table_list, Alter_info *alter_info, @@ -239,10 +239,11 @@ bool mysql_restore_table(THD* thd, TABLE_LIST* table_list); bool mysql_checksum_table(THD* thd, TABLE_LIST* table_list, HA_CHECK_OPT* check_opt); -bool mysql_rm_table(THD *thd,TABLE_LIST *tables, my_bool if_exists, - my_bool drop_temporary); +bool mysql_rm_table(THD *thd,TABLE_LIST *tables, bool if_exists, + bool drop_temporary, bool drop_sequence); int mysql_rm_table_no_locks(THD *thd, TABLE_LIST *tables, bool if_exists, bool drop_temporary, bool drop_view, + bool drop_sequence, bool log_query, bool dont_free_locks); bool log_drop_table(THD *thd, const char *db_name, size_t db_name_length, const char *table_name, size_t table_name_length, @@ -252,10 +253,7 @@ bool quick_rm_table(THD *thd, handlerton *base, const char *db, const char *table_path=0); void close_cached_table(THD *thd, TABLE *table); void sp_prepare_create_field(THD *thd, Column_definition *sql_field); -int prepare_create_field(Column_definition *sql_field, - uint *blob_columns, - ulonglong table_flags); -CHARSET_INFO* get_sql_field_charset(Create_field *sql_field, +CHARSET_INFO* get_sql_field_charset(Column_definition *sql_field, HA_CREATE_INFO *create_info); bool mysql_write_frm(ALTER_PARTITION_PARAM_TYPE *lpt, uint flags); int write_bin_log(THD *thd, bool clear_error, diff --git a/sql/sql_test.cc b/sql/sql_test.cc index feb99861c48..1baa5c3d983 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -135,7 +135,8 @@ void TEST_filesort(SORT_FIELD *sortorder,uint s_length) out.append(*sortorder->field->table_name); out.append('.'); } - out.append(sortorder->field->field_name ? sortorder->field->field_name: + out.append(sortorder->field->field_name.str ? + sortorder->field->field_name.str : "tmp_table_column"); } else @@ -236,11 +237,11 @@ static void print_keyuse(KEYUSE *keyuse) keyuse->val->print(&str, QT_ORDINARY); str.append('\0'); if (keyuse->is_for_hash_join()) - fieldname= keyuse->table->field[keyuse->keypart]->field_name; + fieldname= keyuse->table->field[keyuse->keypart]->field_name.str; else if (keyuse->keypart == FT_KEYPART) fieldname= "FT_KEYPART"; else - fieldname= key_info->key_part[keyuse->keypart].field->field_name; + fieldname= key_info->key_part[keyuse->keypart].field->field_name.str; ll2str(keyuse->used_tables, buf2, 16, 0); fprintf(DBUG_FILE, "KEYUSE: %s.%s=%s optimize: %u used_tables: %s " "ref_table_rows: %lu keypart_map: %0lx\n", @@ -385,10 +386,10 @@ void print_sjm(SJ_MATERIALIZATION_INFO *sjm) /* Debugging help: force List<...>::elem function not be removed as unused. */ -Item* (List<Item>:: *dbug_list_item_elem_ptr)(int)= &List<Item>::elem; -Item_equal* (List<Item_equal>:: *dbug_list_item_equal_elem_ptr)(int)= +Item* (List<Item>:: *dbug_list_item_elem_ptr)(uint)= &List<Item>::elem; +Item_equal* (List<Item_equal>:: *dbug_list_item_equal_elem_ptr)(uint)= &List<Item_equal>::elem; -TABLE_LIST* (List<TABLE_LIST>:: *dbug_list_table_list_elem_ptr)(int) = +TABLE_LIST* (List<TABLE_LIST>:: *dbug_list_table_list_elem_ptr)(uint) = &List<TABLE_LIST>::elem; #endif diff --git a/sql/sql_time.cc b/sql/sql_time.cc index cad4bae03e8..c8ec1fc7f6a 100644 --- a/sql/sql_time.cc +++ b/sql/sql_time.cc @@ -40,27 +40,27 @@ See also interval_type, interval_names */ -LEX_STRING interval_type_to_name[INTERVAL_LAST] = { - { C_STRING_WITH_LEN("YEAR")}, - { C_STRING_WITH_LEN("QUARTER")}, - { C_STRING_WITH_LEN("MONTH")}, - { C_STRING_WITH_LEN("WEEK")}, - { C_STRING_WITH_LEN("DAY")}, - { C_STRING_WITH_LEN("HOUR")}, - { C_STRING_WITH_LEN("MINUTE")}, - { C_STRING_WITH_LEN("SECOND")}, - { C_STRING_WITH_LEN("MICROSECOND")}, - { C_STRING_WITH_LEN("YEAR_MONTH")}, - { C_STRING_WITH_LEN("DAY_HOUR")}, - { C_STRING_WITH_LEN("DAY_MINUTE")}, - { C_STRING_WITH_LEN("DAY_SECOND")}, - { C_STRING_WITH_LEN("HOUR_MINUTE")}, - { C_STRING_WITH_LEN("HOUR_SECOND")}, - { C_STRING_WITH_LEN("MINUTE_SECOND")}, - { C_STRING_WITH_LEN("DAY_MICROSECOND")}, - { C_STRING_WITH_LEN("HOUR_MICROSECOND")}, - { C_STRING_WITH_LEN("MINUTE_MICROSECOND")}, - { C_STRING_WITH_LEN("SECOND_MICROSECOND")} +LEX_CSTRING interval_type_to_name[INTERVAL_LAST] = { + { STRING_WITH_LEN("YEAR")}, + { STRING_WITH_LEN("QUARTER")}, + { STRING_WITH_LEN("MONTH")}, + { STRING_WITH_LEN("WEEK")}, + { STRING_WITH_LEN("DAY")}, + { STRING_WITH_LEN("HOUR")}, + { STRING_WITH_LEN("MINUTE")}, + { STRING_WITH_LEN("SECOND")}, + { STRING_WITH_LEN("MICROSECOND")}, + { STRING_WITH_LEN("YEAR_MONTH")}, + { STRING_WITH_LEN("DAY_HOUR")}, + { STRING_WITH_LEN("DAY_MINUTE")}, + { STRING_WITH_LEN("DAY_SECOND")}, + { STRING_WITH_LEN("HOUR_MINUTE")}, + { STRING_WITH_LEN("HOUR_SECOND")}, + { STRING_WITH_LEN("MINUTE_SECOND")}, + { STRING_WITH_LEN("DAY_MICROSECOND")}, + { STRING_WITH_LEN("HOUR_MICROSECOND")}, + { STRING_WITH_LEN("MINUTE_MICROSECOND")}, + { STRING_WITH_LEN("SECOND_MICROSECOND")} }; /* Calc weekday from daynr */ diff --git a/sql/sql_time.h b/sql/sql_time.h index d560edc296e..4e8f280514f 100644 --- a/sql/sql_time.h +++ b/sql/sql_time.h @@ -106,7 +106,7 @@ inline void datetime_to_date(MYSQL_TIME *ltime) DBUG_ASSERT(ltime->time_type == MYSQL_TIMESTAMP_DATE || ltime->time_type == MYSQL_TIMESTAMP_DATETIME); DBUG_ASSERT(ltime->neg == 0); - ltime->hour= ltime->minute= ltime->second= ltime->second_part= 0; + ltime->second_part= ltime->hour= ltime->minute= ltime->second= 0; ltime->time_type= MYSQL_TIMESTAMP_DATE; } inline void date_to_datetime(MYSQL_TIME *ltime) @@ -203,7 +203,7 @@ extern DATE_TIME_FORMAT global_date_format; extern DATE_TIME_FORMAT global_datetime_format; extern DATE_TIME_FORMAT global_time_format; extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[]; -extern LEX_STRING interval_type_to_name[]; +extern LEX_CSTRING interval_type_to_name[]; static inline bool non_zero_hhmmssuu(const MYSQL_TIME *ltime) diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc index ca9b1431785..291d55d61a2 100644 --- a/sql/sql_trigger.cc +++ b/sql/sql_trigger.cc @@ -34,8 +34,9 @@ #include "sp_cache.h" // sp_invalidate_cache #include <mysys_err.h> -LEX_STRING *make_lex_string(LEX_STRING *lex_str, const char* str, uint length, - MEM_ROOT *mem_root) +LEX_CSTRING *make_lex_string(LEX_CSTRING *lex_str, + const char* str, uint length, + MEM_ROOT *mem_root) { if (!(lex_str->str= strmake_root(mem_root, str, length))) return 0; @@ -56,9 +57,9 @@ public: static Trigger_creation_ctx *create(THD *thd, const char *db_name, const char *table_name, - const LEX_STRING *client_cs_name, - const LEX_STRING *connection_cl_name, - const LEX_STRING *db_cl_name); + const LEX_CSTRING *client_cs_name, + const LEX_CSTRING *connection_cl_name, + const LEX_CSTRING *db_cl_name); Trigger_creation_ctx(CHARSET_INFO *client_cs, CHARSET_INFO *connection_cl, @@ -92,9 +93,9 @@ Trigger_creation_ctx * Trigger_creation_ctx::create(THD *thd, const char *db_name, const char *table_name, - const LEX_STRING *client_cs_name, - const LEX_STRING *connection_cl_name, - const LEX_STRING *db_cl_name) + const LEX_CSTRING *client_cs_name, + const LEX_CSTRING *connection_cl_name, + const LEX_CSTRING *db_cl_name) { CHARSET_INFO *client_cs; CHARSET_INFO *connection_cl; @@ -162,7 +163,7 @@ Trigger_creation_ctx::create(THD *thd, /*************************************************************************/ -static const LEX_STRING triggers_file_type= +static const LEX_CSTRING triggers_file_type= { C_STRING_WITH_LEN("TRIGGERS") }; const char * const TRG_EXT= ".TRG"; @@ -176,37 +177,37 @@ const char * const TRG_EXT= ".TRG"; static File_option triggers_file_parameters[]= { { - { C_STRING_WITH_LEN("triggers") }, + { STRING_WITH_LEN("triggers") }, my_offsetof(class Table_triggers_list, definitions_list), FILE_OPTIONS_STRLIST }, { - { C_STRING_WITH_LEN("sql_modes") }, + { STRING_WITH_LEN("sql_modes") }, my_offsetof(class Table_triggers_list, definition_modes_list), FILE_OPTIONS_ULLLIST }, { - { C_STRING_WITH_LEN("definers") }, + { STRING_WITH_LEN("definers") }, my_offsetof(class Table_triggers_list, definers_list), FILE_OPTIONS_STRLIST }, { - { C_STRING_WITH_LEN("client_cs_names") }, + { STRING_WITH_LEN("client_cs_names") }, my_offsetof(class Table_triggers_list, client_cs_names), FILE_OPTIONS_STRLIST }, { - { C_STRING_WITH_LEN("connection_cl_names") }, + { STRING_WITH_LEN("connection_cl_names") }, my_offsetof(class Table_triggers_list, connection_cl_names), FILE_OPTIONS_STRLIST }, { - { C_STRING_WITH_LEN("db_cl_names") }, + { STRING_WITH_LEN("db_cl_names") }, my_offsetof(class Table_triggers_list, db_cl_names), FILE_OPTIONS_STRLIST }, { - { C_STRING_WITH_LEN("created") }, + { STRING_WITH_LEN("created") }, my_offsetof(class Table_triggers_list, create_times), FILE_OPTIONS_ULLLIST }, @@ -215,7 +216,7 @@ static File_option triggers_file_parameters[]= File_option sql_modes_parameters= { - { C_STRING_WITH_LEN("sql_modes") }, + { STRING_WITH_LEN("sql_modes") }, my_offsetof(class Table_triggers_list, definition_modes_list), FILE_OPTIONS_ULLLIST }; @@ -238,18 +239,18 @@ static const int TRG_NUM_REQUIRED_PARAMETERS= 7; struct st_trigname { - LEX_STRING trigger_table; + LEX_CSTRING trigger_table; }; -static const LEX_STRING trigname_file_type= - { C_STRING_WITH_LEN("TRIGGERNAME") }; +static const LEX_CSTRING trigname_file_type= + { STRING_WITH_LEN("TRIGGERNAME") }; const char * const TRN_EXT= ".TRN"; static File_option trigname_file_parameters[]= { { - { C_STRING_WITH_LEN("trigger_table")}, + { STRING_WITH_LEN("trigger_table")}, offsetof(struct st_trigname, trigger_table), FILE_OPTIONS_ESTRING }, @@ -260,9 +261,9 @@ static File_option trigname_file_parameters[]= class Handle_old_incorrect_sql_modes_hook: public Unknown_key_hook { private: - char *path; + const char *path; public: - Handle_old_incorrect_sql_modes_hook(char *file_path) + Handle_old_incorrect_sql_modes_hook(const char *file_path) :path(file_path) {}; virtual bool process_unknown_string(const char *&unknown_key, uchar* base, @@ -273,15 +274,15 @@ public: class Handle_old_incorrect_trigger_table_hook: public Unknown_key_hook { public: - Handle_old_incorrect_trigger_table_hook(char *file_path, - LEX_STRING *trigger_table_arg) + Handle_old_incorrect_trigger_table_hook(const char *file_path, + LEX_CSTRING *trigger_table_arg) :path(file_path), trigger_table_value(trigger_table_arg) {}; virtual bool process_unknown_string(const char *&unknown_key, uchar* base, MEM_ROOT *mem_root, const char *end); private: - char *path; - LEX_STRING *trigger_table_value; + const char *path; + LEX_CSTRING *trigger_table_value; }; @@ -299,7 +300,7 @@ class Deprecated_trigger_syntax_handler : public Internal_error_handler private: char m_message[MYSQL_ERRMSG_SIZE]; - LEX_STRING *m_trigger_name; + LEX_CSTRING *m_trigger_name; public: @@ -329,7 +330,7 @@ public: return false; } - LEX_STRING *get_trigger_name() { return m_trigger_name; } + LEX_CSTRING *get_trigger_name() { return m_trigger_name; } char *get_error_message() { return m_message; } }; @@ -525,7 +526,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create) } /* We also don't allow creation of triggers on views. */ - tables->required_type= FRMTYPE_TABLE; + tables->required_type= TABLE_TYPE_NORMAL; /* Also prevent DROP TRIGGER from opening temporary table which might shadow the subject table on which trigger to be dropped is defined. @@ -643,10 +644,10 @@ end: static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables, String *stmt_query, String *trigger_def, - LEX_STRING *trg_definer, + LEX_CSTRING *trg_definer, char trg_definer_holder[]) { - LEX_STRING stmt_definition; + LEX_CSTRING stmt_definition; LEX *lex= thd->lex; uint prefix_trimmed, suffix_trimmed; size_t original_length; @@ -662,7 +663,7 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables, if (lex->create_info.or_replace()) stmt_query->append(STRING_WITH_LEN("OR REPLACE ")); - if (lex->sphead->m_chistics->suid != SP_IS_NOT_SUID) + if (lex->sphead->suid() != SP_IS_NOT_SUID) { /* SUID trigger */ lex->definer->set_lex_string(trg_definer, trg_definer_holder); @@ -671,7 +672,7 @@ static void build_trig_stmt_query(THD *thd, TABLE_LIST *tables, } else { - *trg_definer= empty_lex_str; + *trg_definer= empty_clex_str; } @@ -736,7 +737,7 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, LEX *lex= thd->lex; TABLE *table= tables->table; char file_buff[FN_REFLEN], trigname_buff[FN_REFLEN]; - LEX_STRING file, trigname_file; + LEX_CSTRING file, trigname_file; char trg_definer_holder[USER_HOST_BUFF_SIZE]; Item_trigger_field *trg_field; struct st_trigname trigname; @@ -833,11 +834,13 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, } else if (lex->create_info.if_not_exists()) { + strxnmov(trigname_buff, sizeof(trigname_buff) - 1, tables->db, ".", + lex->spname->m_name.str, NullS); push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_TRG_ALREADY_EXISTS, ER_THD(thd, ER_TRG_ALREADY_EXISTS), trigname_buff); - LEX_STRING trg_definer_tmp; + LEX_CSTRING trg_definer_tmp; String trigger_def; /* @@ -850,7 +853,9 @@ bool Table_triggers_list::create_trigger(THD *thd, TABLE_LIST *tables, } else { - my_error(ER_TRG_ALREADY_EXISTS, MYF(0)); + strxnmov(trigname_buff, sizeof(trigname_buff) - 1, tables->db, ".", + lex->spname->m_name.str, NullS); + my_error(ER_TRG_ALREADY_EXISTS, MYF(0), trigname_buff); DBUG_RETURN(true); } } @@ -1046,7 +1051,7 @@ bool Table_triggers_list::save_trigger_file(THD *thd, const char *db, const char *table_name) { char file_buff[FN_REFLEN]; - LEX_STRING file; + LEX_CSTRING file; if (create_lists_needed_for_files(thd->mem_root)) return true; @@ -1066,7 +1071,7 @@ bool Table_triggers_list::save_trigger_file(THD *thd, const char *db, @param remove_from_list If set, remove trigger if found */ -Trigger *Table_triggers_list::find_trigger(const LEX_STRING *name, +Trigger *Table_triggers_list::find_trigger(const LEX_CSTRING *name, bool remove_from_list) { for (uint i= 0; i < (uint)TRG_EVENT_MAX; i++) @@ -1121,7 +1126,7 @@ Trigger *Table_triggers_list::find_trigger(const LEX_STRING *name, bool Table_triggers_list::drop_trigger(THD *thd, TABLE_LIST *tables, String *stmt_query) { - const LEX_STRING *sp_name= &thd->lex->spname->m_name; // alias + const LEX_CSTRING *sp_name= &thd->lex->spname->m_name; // alias char path[FN_REFLEN]; Trigger *trigger; @@ -1294,7 +1299,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, bool names_only) { char path_buff[FN_REFLEN]; - LEX_STRING path; + LEX_CSTRING path; File_parser *parser; LEX_STRING save_db; DBUG_ENTER("Table_triggers_list::check_n_load"); @@ -1314,7 +1319,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (is_equal(&triggers_file_type, parser->type())) { Handle_old_incorrect_sql_modes_hook sql_modes_hook(path.str); - LEX_STRING *trg_create_str; + LEX_CSTRING *trg_create_str; ulonglong *trg_sql_mode, *trg_create_time; Trigger *trigger; Table_triggers_list *trigger_list= @@ -1328,7 +1333,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, &sql_modes_hook)) goto error; - List_iterator_fast<LEX_STRING> it(trigger_list->definitions_list); + List_iterator_fast<LEX_CSTRING> it(trigger_list->definitions_list); if (!trigger_list->definitions_list.is_empty() && (trigger_list->client_cs_names.is_empty() || @@ -1347,10 +1352,10 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, status_var_increment(thd->status_var.feature_trigger); List_iterator_fast<ulonglong> itm(trigger_list->definition_modes_list); - List_iterator_fast<LEX_STRING> it_definer(trigger_list->definers_list); - List_iterator_fast<LEX_STRING> it_client_cs_name(trigger_list->client_cs_names); - List_iterator_fast<LEX_STRING> it_connection_cl_name(trigger_list->connection_cl_names); - List_iterator_fast<LEX_STRING> it_db_cl_name(trigger_list->db_cl_names); + List_iterator_fast<LEX_CSTRING> it_definer(trigger_list->definers_list); + List_iterator_fast<LEX_CSTRING> it_client_cs_name(trigger_list->client_cs_names); + List_iterator_fast<LEX_CSTRING> it_connection_cl_name(trigger_list->connection_cl_names); + List_iterator_fast<LEX_CSTRING> it_db_cl_name(trigger_list->db_cl_names); List_iterator_fast<ulonglong> it_create_times(trigger_list->create_times); LEX *old_lex= thd->lex, lex; sp_rcontext *save_spcont= thd->spcont; @@ -1365,7 +1370,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, { sp_head *sp; sql_mode_t sql_mode; - LEX_STRING *trg_definer; + LEX_CSTRING *trg_definer; Trigger_creation_ctx *creation_ctx; /* @@ -1382,7 +1387,8 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, thd->variables.sql_mode= sql_mode; Parser_state parser_state; - if (parser_state.init(thd, trg_create_str->str, trg_create_str->length)) + if (parser_state.init(thd, (char*) trg_create_str->str, + trg_create_str->length)) goto err_with_lex_cleanup; if (!trigger_list->client_cs_names.is_empty()) @@ -1425,7 +1431,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, lex.set_trg_event_type_for_tables(); if (lex.sphead) - lex.sphead->set_info(0, 0, &lex.sp_chistics, sql_mode); + lex.sphead->m_sql_mode= sql_mode; if (unlikely(!(trigger= (new (&table->mem_root) Trigger(trigger_list, lex.sphead))))) @@ -1437,7 +1443,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, trigger->sql_mode= sql_mode; trigger->definition= *trg_create_str; trigger->create_time= trg_create_time ? *trg_create_time : 0; - trigger->name= sp ? sp->m_name : empty_lex_str; + trigger->name= sp ? sp->m_name : empty_clex_str; trigger->on_table_name.str= (char*) lex.raw_trg_on_table_name_begin; trigger->on_table_name.length= (lex.raw_trg_on_table_name_end - lex.raw_trg_on_table_name_begin); @@ -1460,7 +1466,7 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, if (parse_error) { - LEX_STRING *name; + LEX_CSTRING *name; /* In case of errors, disable all triggers for the table, but keep @@ -1479,11 +1485,11 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, goto err_with_lex_cleanup; } trigger->definer= ((!trg_definer || !trg_definer->length) ? - empty_lex_str : *trg_definer); + empty_clex_str : *trg_definer); continue; } - sp->set_info(0, 0, &lex.sp_chistics, sql_mode); + sp->m_sql_mode= sql_mode; sp->set_creation_ctx(creation_ctx); if (!trg_definer || !trg_definer->length) @@ -1505,15 +1511,15 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, schema. */ - sp->set_definer((char*) "", 0); - trigger->definer= empty_lex_str; + sp->set_definer("", 0); + trigger->definer= empty_clex_str; /* trigger_list without definer information are executed under the authorization of the invoker. */ - sp->m_chistics->suid= SP_IS_NOT_SUID; + sp->set_suid(SP_IS_NOT_SUID); } else { @@ -1618,7 +1624,7 @@ error: void Table_triggers_list::add_trigger(trg_event_type event, trg_action_time_type action_time, trigger_order_type ordering_clause, - LEX_STRING *anchor_trigger_name, + LEX_CSTRING *anchor_trigger_name, Trigger *trigger) { Trigger **parent= &triggers[event][action_time]; @@ -1669,8 +1675,9 @@ void Table_triggers_list::add_trigger(trg_event_type event, True error */ -void Trigger::get_trigger_info(LEX_STRING *trigger_stmt, - LEX_STRING *trigger_body, LEX_STRING *definer) +void Trigger::get_trigger_info(LEX_CSTRING *trigger_stmt, + LEX_CSTRING *trigger_body, + LEX_STRING *definer) { DBUG_ENTER("get_trigger_info"); @@ -1684,14 +1691,14 @@ void Trigger::get_trigger_info(LEX_STRING *trigger_stmt, } *trigger_body= body->m_body_utf8; - if (body->m_chistics->suid == SP_IS_NOT_SUID) + if (body->suid() == SP_IS_NOT_SUID) { *definer= empty_lex_str; } else { - definer->length= strxmov(definer->str, body->m_definer_user.str, "@", - body->m_definer_host.str, NullS) - definer->str; + definer->length= strxmov(definer->str, body->m_definer.user.str, "@", + body->m_definer.host.str, NullS) - definer->str; } DBUG_VOID_RETURN; } @@ -1722,7 +1729,7 @@ bool add_table_for_trigger(THD *thd, LEX *lex= thd->lex; char trn_path_buff[FN_REFLEN]; LEX_STRING trn_path= { trn_path_buff, 0 }; - LEX_STRING tbl_name= null_lex_str; + LEX_CSTRING tbl_name= null_clex_str; DBUG_ENTER("add_table_for_trigger"); @@ -1746,7 +1753,8 @@ bool add_table_for_trigger(THD *thd, DBUG_RETURN(TRUE); } - if (load_table_name_for_trigger(thd, trg_name, &trn_path, &tbl_name)) + if (load_table_name_for_trigger(thd, trg_name, (LEX_CSTRING*) &trn_path, + &tbl_name)) DBUG_RETURN(TRUE); *table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str, @@ -1770,7 +1778,8 @@ bool add_table_for_trigger(THD *thd, True error */ -bool Table_triggers_list::drop_all_triggers(THD *thd, char *db, char *name) +bool Table_triggers_list::drop_all_triggers(THD *thd, const char *db, + const char *name) { TABLE table; char path[FN_REFLEN]; @@ -1844,7 +1853,7 @@ struct change_table_name_param THD *thd; const char *old_db_name; const char *new_db_name; - LEX_STRING *new_table_name; + LEX_CSTRING *new_table_name; Trigger *stopper; }; @@ -1853,8 +1862,8 @@ bool Table_triggers_list::change_table_name_in_triggers(THD *thd, const char *old_db_name, const char *new_db_name, - LEX_STRING *old_table_name, - LEX_STRING *new_table_name) + LEX_CSTRING *old_table_name, + LEX_CSTRING *new_table_name) { struct change_table_name_param param; sql_mode_t save_sql_mode= thd->variables.sql_mode; @@ -1886,9 +1895,8 @@ bool Trigger::change_table_name(void* param_arg) { change_table_name_param *param= (change_table_name_param*) param_arg; THD *thd= param->thd; - LEX_STRING *new_table_name= param->new_table_name; - - LEX_STRING *def= &definition, new_def; + LEX_CSTRING *new_table_name= param->new_table_name; + LEX_CSTRING *def= &definition, new_def; size_t on_q_table_name_len, before_on_len; String buff; @@ -1943,7 +1951,7 @@ bool Trigger::change_table_name(void* param_arg) Trigger * Table_triggers_list::change_table_name_in_trignames(const char *old_db_name, const char *new_db_name, - LEX_STRING *new_table_name, + LEX_CSTRING *new_table_name, Trigger *trigger) { struct change_table_name_param param; @@ -1962,7 +1970,7 @@ bool Trigger::change_on_table_name(void* param_arg) char trigname_buff[FN_REFLEN]; struct st_trigname trigname; - LEX_STRING trigname_file; + LEX_CSTRING trigname_file; if (param->stopper == this) return 0; // Stop processing @@ -2052,8 +2060,8 @@ bool Table_triggers_list::change_table_name(THD *thd, const char *db, result= 1; goto end; } - LEX_STRING old_table_name= { (char *) old_alias, strlen(old_alias) }; - LEX_STRING new_table_name= { (char *) new_table, strlen(new_table) }; + LEX_CSTRING old_table_name= { (char *) old_alias, strlen(old_alias) }; + LEX_CSTRING new_table_name= { (char *) new_table, strlen(new_table) }; /* Since triggers should be in the same schema as their subject tables moving table with them between two schemas raises too many questions. @@ -2464,16 +2472,14 @@ bool check_trn_exists(const LEX_STRING *trn_path) bool load_table_name_for_trigger(THD *thd, const sp_name *trg_name, - const LEX_STRING *trn_path, - LEX_STRING *tbl_name) + const LEX_CSTRING *trn_path, + LEX_CSTRING *tbl_name) { File_parser *parser; struct st_trigname trn_data; - Handle_old_incorrect_trigger_table_hook trigger_table_hook( trn_path->str, &trn_data.trigger_table); - DBUG_ENTER("load_table_name_for_trigger"); /* Parse the TRN-file. */ diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h index 9d1c79cc7cf..8847680c7b2 100644 --- a/sql/sql_trigger.h +++ b/sql/sql_trigger.h @@ -71,7 +71,7 @@ struct st_trg_execution_order Trigger name referenced in the FOLLOWS/PRECEDES clause of the CREATE TRIGGER statement. */ - LEX_STRING anchor_trigger_name; + LEX_CSTRING anchor_trigger_name; }; @@ -99,15 +99,15 @@ public: grouped by event and action_time. */ Item_trigger_field *trigger_fields; - LEX_STRING name; - LEX_STRING on_table_name; /* Raw table name */ - LEX_STRING definition; - LEX_STRING definer; + LEX_CSTRING name; + LEX_CSTRING on_table_name; /* Raw table name */ + LEX_CSTRING definition; + LEX_CSTRING definer; /* Character sets used */ - LEX_STRING client_cs_name; - LEX_STRING connection_cl_name; - LEX_STRING db_cl_name; + LEX_CSTRING client_cs_name; + LEX_CSTRING connection_cl_name; + LEX_CSTRING db_cl_name; GRANT_INFO subject_table_grants; sql_mode_t sql_mode; @@ -118,7 +118,7 @@ public: uint action_order; bool is_fields_updated_in_trigger(MY_BITMAP *used_fields); - void get_trigger_info(LEX_STRING *stmt, LEX_STRING *body, + void get_trigger_info(LEX_CSTRING *stmt, LEX_CSTRING *body, LEX_STRING *definer); /* Functions executed over each active trigger */ bool change_on_table_name(void* param_arg); @@ -189,7 +189,7 @@ public: Field responsible for storing triggers definitions in file. It have to be public because we are using it directly from parser. */ - List<LEX_STRING> definitions_list; + List<LEX_CSTRING> definitions_list; /** List of sql modes for triggers */ @@ -197,13 +197,13 @@ public: /** Create times for triggers */ List<ulonglong> create_times; - List<LEX_STRING> definers_list; + List<LEX_CSTRING> definers_list; /* Character set context, used for parsing and executing triggers. */ - List<LEX_STRING> client_cs_names; - List<LEX_STRING> connection_cl_names; - List<LEX_STRING> db_cl_names; + List<LEX_CSTRING> client_cs_names; + List<LEX_CSTRING> connection_cl_names; + List<LEX_CSTRING> db_cl_names; /* End of character ser context. */ @@ -227,7 +227,8 @@ public: static bool check_n_load(THD *thd, const char *db, const char *table_name, TABLE *table, bool names_only); - static bool drop_all_triggers(THD *thd, char *db, char *table_name); + static bool drop_all_triggers(THD *thd, const char *db, + const char *table_name); static bool change_table_name(THD *thd, const char *db, const char *old_alias, const char *old_table, @@ -236,7 +237,7 @@ public: void add_trigger(trg_event_type event_type, trg_action_time_type action_time, trigger_order_type ordering_clause, - LEX_STRING *anchor_trigger_name, + LEX_CSTRING *anchor_trigger_name, Trigger *trigger); Trigger *get_trigger(trg_event_type event_type, trg_action_time_type action_time) @@ -279,7 +280,7 @@ public: bzero(extra_null_bitmap, null_bytes); } - Trigger *find_trigger(const LEX_STRING *name, bool remove_from_list); + Trigger *find_trigger(const LEX_CSTRING *name, bool remove_from_list); Trigger* for_all_triggers(Triggers_processor func, void *arg); @@ -287,13 +288,13 @@ private: bool prepare_record_accessors(TABLE *table); Trigger *change_table_name_in_trignames(const char *old_db_name, const char *new_db_name, - LEX_STRING *new_table_name, + LEX_CSTRING *new_table_name, Trigger *trigger); bool change_table_name_in_triggers(THD *thd, const char *old_db_name, const char *new_db_name, - LEX_STRING *old_table_name, - LEX_STRING *new_table_name); + LEX_CSTRING *old_table_name, + LEX_CSTRING *new_table_name); bool check_for_broken_triggers() { @@ -324,8 +325,8 @@ bool check_trn_exists(const LEX_STRING *trn_path); bool load_table_name_for_trigger(THD *thd, const sp_name *trg_name, - const LEX_STRING *trn_path, - LEX_STRING *tbl_name); + const LEX_CSTRING *trn_path, + LEX_CSTRING *tbl_name); bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create); extern const char * const TRG_EXT; diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc index c08c75f771a..d4859efc7af 100644 --- a/sql/sql_truncate.cc +++ b/sql/sql_truncate.cc @@ -38,11 +38,11 @@ */ static bool fk_info_append_fields(THD *thd, String *str, - List<LEX_STRING> *fields) + List<LEX_CSTRING> *fields) { bool res= FALSE; - LEX_STRING *field; - List_iterator_fast<LEX_STRING> it(*fields); + LEX_CSTRING *field; + List_iterator_fast<LEX_CSTRING> it(*fields); while ((field= it++)) { @@ -216,7 +216,7 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref, or writing into the table. Yet, to open a write cursor we need a thr_lock lock. Allow to open base tables only. */ - table_ref->required_type= FRMTYPE_TABLE; + table_ref->required_type= TABLE_TYPE_NORMAL; /* Ignore pending FLUSH TABLES since we don't want to release the MDL lock taken above and otherwise there is no way to @@ -248,8 +248,8 @@ Sql_cmd_truncate_table::handler_truncate(THD *thd, TABLE_LIST *table_ref, table_ref->table->file->print_error(error, MYF(0)); /* If truncate method is not implemented then we don't binlog the - statement. If truncation has failed in a transactional engine then also we - donot binlog the statment. Only in non transactional engine we binlog + statement. If truncation has failed in a transactional engine then also + we don't binlog the statment. Only in non transactional engine we binlog inspite of errors. */ if (error == HA_ERR_WRONG_COMMAND || @@ -311,14 +311,17 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, } else { + handlerton *hton; + bool is_sequence; + /* Acquire an exclusive lock. */ DBUG_ASSERT(table_ref->next_global == NULL); if (lock_table_names(thd, table_ref, NULL, thd->variables.lock_wait_timeout, 0)) DBUG_RETURN(TRUE); - handlerton *hton; - if (!ha_table_exists(thd, table_ref->db, table_ref->table_name, &hton) || + if (!ha_table_exists(thd, table_ref->db, table_ref->table_name, + &hton, &is_sequence) || hton == view_pseudo_hton) { my_error(ER_NO_SUCH_TABLE, MYF(0), table_ref->db, table_ref->table_name); @@ -337,7 +340,7 @@ bool Sql_cmd_truncate_table::lock_table(THD *thd, TABLE_LIST *table_ref, *hton_can_recreate= false; } else - *hton_can_recreate= hton->flags & HTON_CAN_RECREATE; + *hton_can_recreate= !is_sequence && hton->flags & HTON_CAN_RECREATE; } /* diff --git a/sql/sql_type.cc b/sql/sql_type.cc index d85664568a1..737d01f7b89 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -17,40 +17,237 @@ #include "sql_type.h" #include "sql_const.h" #include "sql_class.h" +#include "sql_time.h" #include "item.h" #include "log.h" -static Type_handler_tiny type_handler_tiny; -static Type_handler_short type_handler_short; -static Type_handler_long type_handler_long; -static Type_handler_longlong type_handler_longlong; -static Type_handler_int24 type_handler_int24; -static Type_handler_year type_handler_year; -static Type_handler_bit type_handler_bit; -static Type_handler_float type_handler_float; -static Type_handler_double type_handler_double; -static Type_handler_time type_handler_time; -static Type_handler_time2 type_handler_time2; -static Type_handler_date type_handler_date; -static Type_handler_newdate type_handler_newdate; -static Type_handler_datetime type_handler_datetime; -static Type_handler_datetime2 type_handler_datetime2; -static Type_handler_timestamp type_handler_timestamp; -static Type_handler_timestamp2 type_handler_timestamp2; -static Type_handler_olddecimal type_handler_olddecimal; -static Type_handler_newdecimal type_handler_newdecimal; -static Type_handler_null type_handler_null; -static Type_handler_string type_handler_string; -static Type_handler_varchar type_handler_varchar; -static Type_handler_tiny_blob type_handler_tiny_blob; -static Type_handler_medium_blob type_handler_medium_blob; -static Type_handler_long_blob type_handler_long_blob; -static Type_handler_blob type_handler_blob; +Type_handler_row type_handler_row; + +Type_handler_null type_handler_null; + +Type_handler_tiny type_handler_tiny; +Type_handler_short type_handler_short; +Type_handler_long type_handler_long; +Type_handler_int24 type_handler_int24; +Type_handler_longlong type_handler_longlong; +Type_handler_longlong type_handler_ulonglong; // Only used for CAST() for now +Type_handler_float type_handler_float; +Type_handler_double type_handler_double; +Type_handler_bit type_handler_bit; + +Type_handler_olddecimal type_handler_olddecimal; +Type_handler_newdecimal type_handler_newdecimal; + +Type_handler_year type_handler_year; +Type_handler_time type_handler_time; +Type_handler_date type_handler_date; +Type_handler_timestamp type_handler_timestamp; +Type_handler_timestamp2 type_handler_timestamp2; +Type_handler_datetime type_handler_datetime; +Type_handler_time2 type_handler_time2; +Type_handler_newdate type_handler_newdate; +Type_handler_datetime2 type_handler_datetime2; + +Type_handler_enum type_handler_enum; +Type_handler_set type_handler_set; + +Type_handler_string type_handler_string; +Type_handler_var_string type_handler_var_string; +Type_handler_varchar type_handler_varchar; + +Type_handler_tiny_blob type_handler_tiny_blob; +Type_handler_medium_blob type_handler_medium_blob; +Type_handler_long_blob type_handler_long_blob; +Type_handler_blob type_handler_blob; + #ifdef HAVE_SPATIAL -static Type_handler_geometry type_handler_geometry; +Type_handler_geometry type_handler_geometry; +#endif + + +bool Type_handler_data::init() +{ +#ifdef HAVE_SPATIAL + +#ifndef DBUG_OFF + if (m_type_aggregator_non_commutative_test.add(&type_handler_geometry, + &type_handler_geometry, + &type_handler_geometry) || + m_type_aggregator_non_commutative_test.add(&type_handler_geometry, + &type_handler_varchar, + &type_handler_long_blob)) + return true; +#endif + + return + m_type_aggregator_for_result.add(&type_handler_geometry, + &type_handler_null, + &type_handler_geometry) || + m_type_aggregator_for_result.add(&type_handler_geometry, + &type_handler_geometry, + &type_handler_geometry) || + m_type_aggregator_for_result.add(&type_handler_geometry, + &type_handler_tiny_blob, + &type_handler_long_blob) || + m_type_aggregator_for_result.add(&type_handler_geometry, + &type_handler_blob, + &type_handler_long_blob) || + m_type_aggregator_for_result.add(&type_handler_geometry, + &type_handler_medium_blob, + &type_handler_long_blob) || + m_type_aggregator_for_result.add(&type_handler_geometry, + &type_handler_long_blob, + &type_handler_long_blob) || + m_type_aggregator_for_result.add(&type_handler_geometry, + &type_handler_varchar, + &type_handler_long_blob) || + m_type_aggregator_for_result.add(&type_handler_geometry, + &type_handler_string, + &type_handler_long_blob) || + m_type_aggregator_for_comparison.add(&type_handler_geometry, + &type_handler_geometry, + &type_handler_geometry) || + m_type_aggregator_for_comparison.add(&type_handler_geometry, + &type_handler_null, + &type_handler_geometry) || + m_type_aggregator_for_comparison.add(&type_handler_geometry, + &type_handler_long_blob, + &type_handler_long_blob); #endif -static Type_handler_enum type_handler_enum; -static Type_handler_set type_handler_set; + return false; +} + + +Type_handler_data *type_handler_data= NULL; + + +void Type_std_attributes::set(const Field *field) +{ + decimals= field->decimals(); + unsigned_flag= MY_TEST(field->flags & UNSIGNED_FLAG); + collation.set(field->charset(), field->derivation(), field->repertoire()); + fix_char_length(field->char_length()); +} + + +uint Type_std_attributes::count_max_decimals(Item **item, uint nitems) +{ + uint res= 0; + for (uint i= 0; i < nitems; i++) + set_if_bigger(res, item[i]->decimals); + return res; +} + + +/** + Set max_length/decimals of function if function is fixed point and + result length/precision depends on argument ones. +*/ + +void Type_std_attributes::count_decimal_length(Item **item, uint nitems) +{ + int max_int_part= 0; + decimals= 0; + unsigned_flag= 1; + for (uint i=0 ; i < nitems ; i++) + { + set_if_bigger(decimals, item[i]->decimals); + set_if_bigger(max_int_part, item[i]->decimal_int_part()); + set_if_smaller(unsigned_flag, item[i]->unsigned_flag); + } + int precision= MY_MIN(max_int_part + decimals, DECIMAL_MAX_PRECISION); + fix_char_length(my_decimal_precision_to_length_no_truncation(precision, + (uint8) decimals, + unsigned_flag)); +} + + +/** + Set max_length of if it is maximum length of its arguments. +*/ + +void Type_std_attributes::count_only_length(Item **item, uint nitems) +{ + uint32 char_length= 0; + unsigned_flag= 0; + for (uint i= 0; i < nitems ; i++) + { + set_if_bigger(char_length, item[i]->max_char_length()); + set_if_bigger(unsigned_flag, item[i]->unsigned_flag); + } + fix_char_length(char_length); +} + + +void Type_std_attributes::count_octet_length(Item **item, uint nitems) +{ + max_length= 0; + unsigned_flag= 0; + for (uint i= 0; i < nitems ; i++) + { + set_if_bigger(max_length, item[i]->max_length); + set_if_bigger(unsigned_flag, item[i]->unsigned_flag); + } +} + + +/** + Set max_length/decimals of function if function is floating point and + result length/precision depends on argument ones. +*/ + +void Type_std_attributes::count_real_length(Item **items, uint nitems) +{ + uint32 length= 0; + decimals= 0; + max_length= 0; + unsigned_flag= false; + for (uint i=0 ; i < nitems ; i++) + { + if (decimals < FLOATING_POINT_DECIMALS) + { + set_if_bigger(decimals, items[i]->decimals); + /* Will be ignored if items[i]->decimals >= FLOATING_POINT_DECIMALS */ + set_if_bigger(length, (items[i]->max_length - items[i]->decimals)); + } + set_if_bigger(max_length, items[i]->max_length); + } + if (decimals < FLOATING_POINT_DECIMALS) + { + max_length= length; + length+= decimals; + if (length < max_length) // If previous operation gave overflow + max_length= UINT_MAX32; + else + max_length= length; + } + // Corner case: COALESCE(DOUBLE(255,4), DOUBLE(255,3)) -> FLOAT(255, 4) + set_if_smaller(max_length, MAX_FIELD_CHARLENGTH); +} + + +/** + Calculate max_length and decimals for string functions. + + @param field_type Field type. + @param items Argument array. + @param nitems Number of arguments. + + @retval False on success, true on error. +*/ +bool Type_std_attributes::count_string_length(const char *func_name, + Item **items, uint nitems) +{ + if (agg_arg_charsets_for_string_result(collation, func_name, + items, nitems, 1)) + return true; + if (collation.collation == &my_charset_bin) + count_octet_length(items, nitems); + else + count_only_length(items, nitems); + decimals= max_length ? NOT_FIXED_DEC : 0; + return false; +} /** @@ -68,7 +265,7 @@ static Type_handler_set type_handler_set; all around the code. */ const Type_handler * -Type_handler::string_type_handler(uint max_octet_length) const +Type_handler::string_type_handler(uint max_octet_length) { if (max_octet_length >= 16777216) return &type_handler_long_blob; @@ -78,6 +275,36 @@ Type_handler::string_type_handler(uint max_octet_length) const } +const Type_handler * +Type_handler::varstring_type_handler(const Item *item) +{ + if (!item->max_length) + return &type_handler_string; + if (item->too_big_for_varchar()) + return blob_type_handler(item->max_length); + return &type_handler_varchar; +} + + +const Type_handler * +Type_handler::blob_type_handler(uint max_octet_length) +{ + if (max_octet_length <= 255) + return &type_handler_tiny_blob; + if (max_octet_length <= 65535) + return &type_handler_blob; + if (max_octet_length <= 16777215) + return &type_handler_medium_blob; + return &type_handler_long_blob; +} + + +const Type_handler * +Type_handler::blob_type_handler(const Item *item) +{ + return blob_type_handler(item->max_length); +} + /** This method is used by: - Item_sum_hybrid, e.g. MAX(item), MIN(item). @@ -98,19 +325,51 @@ Type_handler_string_result::type_handler_adjusted_to_max_octet_length( } +CHARSET_INFO *Type_handler::charset_for_protocol(const Item *item) const +{ + /* + For backward compatibility, to make numeric + data types return "binary" charset in client-side metadata. + */ + return &my_charset_bin; +} + + +bool +Type_handler::Item_func_or_sum_illegal_param(const char *funcname) const +{ + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION, MYF(0), + name().ptr(), funcname); + return true; +} + + +bool +Type_handler::Item_func_or_sum_illegal_param(const Item_func_or_sum *it) const +{ + return Item_func_or_sum_illegal_param(it->func_name()); +} + + +CHARSET_INFO * +Type_handler_string_result::charset_for_protocol(const Item *item) const +{ + return item->collation.collation; +} + + const Type_handler * -Type_handler_hybrid_field_type::get_handler_by_result_type(Item_result type) - const +Type_handler::get_handler_by_cmp_type(Item_result type) { switch (type) { case REAL_RESULT: return &type_handler_double; case INT_RESULT: return &type_handler_longlong; case DECIMAL_RESULT: return &type_handler_newdecimal; case STRING_RESULT: return &type_handler_long_blob; - case TIME_RESULT: - case ROW_RESULT: - DBUG_ASSERT(0); + case TIME_RESULT: return &type_handler_datetime; + case ROW_RESULT: return &type_handler_row; } + DBUG_ASSERT(0); return &type_handler_string; } @@ -121,6 +380,502 @@ Type_handler_hybrid_field_type::Type_handler_hybrid_field_type() } +/***************************************************************************/ + +/* number of bytes to store second_part part of the TIMESTAMP(N) */ +uint Type_handler_timestamp::m_sec_part_bytes[MAX_DATETIME_PRECISION + 1]= + { 0, 1, 1, 2, 2, 3, 3 }; + +/* number of bytes to store DATETIME(N) */ +uint Type_handler_datetime::m_hires_bytes[MAX_DATETIME_PRECISION + 1]= + { 5, 6, 6, 7, 7, 7, 8 }; + +/* number of bytes to store TIME(N) */ +uint Type_handler_time::m_hires_bytes[MAX_DATETIME_PRECISION + 1]= + { 3, 4, 4, 5, 5, 5, 6 }; + +/***************************************************************************/ +const Name Type_handler_row::m_name_row(C_STRING_WITH_LEN("row")); + +const Name Type_handler_null::m_name_null(C_STRING_WITH_LEN("null")); + +const Name + Type_handler_string::m_name_char(C_STRING_WITH_LEN("char")), + Type_handler_var_string::m_name_var_string(C_STRING_WITH_LEN("varchar")), + Type_handler_varchar::m_name_varchar(C_STRING_WITH_LEN("varchar")), + Type_handler_tiny_blob::m_name_tinyblob(C_STRING_WITH_LEN("tinyblob")), + Type_handler_medium_blob::m_name_mediumblob(C_STRING_WITH_LEN("mediumblob")), + Type_handler_long_blob::m_name_longblob(C_STRING_WITH_LEN("longblob")), + Type_handler_blob::m_name_blob(C_STRING_WITH_LEN("blob")); + +const Name + Type_handler_enum::m_name_enum(C_STRING_WITH_LEN("enum")), + Type_handler_set::m_name_set(C_STRING_WITH_LEN("set")); + +const Name + Type_handler_tiny::m_name_tiny(C_STRING_WITH_LEN("tinyint")), + Type_handler_short::m_name_short(C_STRING_WITH_LEN("smallint")), + Type_handler_long::m_name_int(C_STRING_WITH_LEN("int")), + Type_handler_longlong::m_name_longlong(C_STRING_WITH_LEN("bigint")), + Type_handler_int24::m_name_mediumint(C_STRING_WITH_LEN("mediumint")), + Type_handler_year::m_name_year(C_STRING_WITH_LEN("year")), + Type_handler_bit::m_name_bit(C_STRING_WITH_LEN("bit")); + +const Name + Type_handler_float::m_name_float(C_STRING_WITH_LEN("float")), + Type_handler_double::m_name_double(C_STRING_WITH_LEN("double")); + +const Name + Type_handler_olddecimal::m_name_decimal(C_STRING_WITH_LEN("decimal")), + Type_handler_newdecimal::m_name_decimal(C_STRING_WITH_LEN("decimal")); + +const Name + Type_handler_time_common::m_name_time(C_STRING_WITH_LEN("time")), + Type_handler_date_common::m_name_date(C_STRING_WITH_LEN("date")), + Type_handler_datetime_common::m_name_datetime(C_STRING_WITH_LEN("datetime")), + Type_handler_timestamp_common::m_name_timestamp(C_STRING_WITH_LEN("timestamp")); + +/***************************************************************************/ + +const Type_handler *Type_handler_null::type_handler_for_comparison() const +{ + return &type_handler_null; +} + + +const Type_handler *Type_handler_int_result::type_handler_for_comparison() const +{ + return &type_handler_longlong; +} + + +const Type_handler *Type_handler_string_result::type_handler_for_comparison() const +{ + return &type_handler_long_blob; +} + + +const Type_handler *Type_handler_decimal_result::type_handler_for_comparison() const +{ + return &type_handler_newdecimal; +} + + +const Type_handler *Type_handler_real_result::type_handler_for_comparison() const +{ + return &type_handler_double; +} + + +const Type_handler *Type_handler_time_common::type_handler_for_comparison() const +{ + return &type_handler_time; +} + +const Type_handler *Type_handler_date_common::type_handler_for_comparison() const +{ + return &type_handler_newdate; +} + + +const Type_handler *Type_handler_datetime_common::type_handler_for_comparison() const +{ + return &type_handler_datetime; +} + + +const Type_handler *Type_handler_timestamp_common::type_handler_for_comparison() const +{ + return &type_handler_datetime; +} + + +const Type_handler *Type_handler_row::type_handler_for_comparison() const +{ + return &type_handler_row; +} + +/***************************************************************************/ + +const Type_handler *Type_handler_typelib::type_handler_for_item_field() const +{ + return &type_handler_string; +} + + +const Type_handler *Type_handler_typelib::cast_to_int_type_handler() const +{ + return &type_handler_longlong; +} + + +/***************************************************************************/ + +bool +Type_handler_hybrid_field_type::aggregate_for_result(const Type_handler *other) +{ + if (m_type_handler->is_traditional_type() && other->is_traditional_type()) + { + m_type_handler= + Type_handler::aggregate_for_result_traditional(m_type_handler, other); + return false; + } + other= type_handler_data-> + m_type_aggregator_for_result.find_handler(m_type_handler, other); + if (!other) + return true; + m_type_handler= other; + return false; +} + + +const Type_handler * +Type_handler::type_handler_long_or_longlong(uint max_char_length) +{ + if (max_char_length <= MY_INT32_NUM_DECIMAL_DIGITS - 2) + return &type_handler_long; + return &type_handler_longlong; +} + +/* + This method is called for CASE (and its abbreviations) and LEAST/GREATEST + when data type aggregation returned LONGLONG and there were some BIT + expressions. This helps to adjust the data type from LONGLONG to LONG + if all expressions fit. +*/ +const Type_handler * +Type_handler::bit_and_int_mixture_handler(uint max_char_length) +{ + if (max_char_length <= MY_INT32_NUM_DECIMAL_DIGITS) + return &type_handler_long; + return &type_handler_longlong; +} + + +/** + @brief Aggregates field types from the array of items. + + @param[in] items array of items to aggregate the type from + @param[in] nitems number of items in the array + @param[in] treat_bit_as_number - if BIT should be aggregated to a non-BIT + counterpart as a LONGLONG number or as a VARBINARY string. + + Currently behaviour depends on the function: + - LEAST/GREATEST treat BIT as VARBINARY when + aggregating with a non-BIT counterpart. + Note, UNION also works this way. + + - CASE, COALESCE, IF, IFNULL treat BIT as LONGLONG when + aggregating with a non-BIT counterpart; + + This inconsistency may be changed in the future. See MDEV-8867. + + Note, independently from "treat_bit_as_number": + - a single BIT argument gives BIT as a result + - two BIT couterparts give BIT as a result + + @details This function aggregates field types from the array of items. + Found type is supposed to be used later as the result field type + of a multi-argument function. + Aggregation itself is performed by Type_handler::aggregate_for_result(). + + @note The term "aggregation" is used here in the sense of inferring the + result type of a function from its argument types. + + @retval false - on success + @retval true - on error +*/ + +bool +Type_handler_hybrid_field_type::aggregate_for_result(const char *funcname, + Item **items, uint nitems, + bool treat_bit_as_number) +{ + bool bit_and_non_bit_mixture_found= false; + uint32 max_display_length; + if (!nitems || items[0]->result_type() == ROW_RESULT) + { + DBUG_ASSERT(0); + set_handler(&type_handler_null); + return true; + } + set_handler(items[0]->type_handler()); + max_display_length= items[0]->max_display_length(); + for (uint i= 1 ; i < nitems ; i++) + { + const Type_handler *cur= items[i]->type_handler(); + set_if_bigger(max_display_length, items[i]->max_display_length()); + if (treat_bit_as_number && + ((type_handler() == &type_handler_bit) ^ (cur == &type_handler_bit))) + { + bit_and_non_bit_mixture_found= true; + if (type_handler() == &type_handler_bit) + set_handler(&type_handler_longlong); // BIT + non-BIT + else + cur= &type_handler_longlong; // non-BIT + BIT + } + if (aggregate_for_result(cur)) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + type_handler()->name().ptr(), cur->name().ptr(), funcname); + return true; + } + } + if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_longlong) + set_handler(Type_handler::bit_and_int_mixture_handler(max_display_length)); + return false; +} + +/** + Collect built-in data type handlers for comparison. + This method is very similar to item_cmp_type() defined in item.cc. + Now they coexist. Later item_cmp_type() will be removed. + In addition to item_cmp_type(), this method correctly aggregates + TIME with DATETIME/TIMESTAMP/DATE, so no additional find_date_time_item() + is needed after this call. +*/ + +bool +Type_handler_hybrid_field_type::aggregate_for_comparison(const Type_handler *h) +{ + DBUG_ASSERT(m_type_handler == m_type_handler->type_handler_for_comparison()); + DBUG_ASSERT(h == h->type_handler_for_comparison()); + + if (!m_type_handler->is_traditional_type() || + !h->is_traditional_type()) + { + h= type_handler_data-> + m_type_aggregator_for_comparison.find_handler(m_type_handler, h); + if (!h) + return true; + m_type_handler= h; + DBUG_ASSERT(m_type_handler == m_type_handler->type_handler_for_comparison()); + return false; + } + + Item_result a= cmp_type(); + Item_result b= h->cmp_type(); + if (a == STRING_RESULT && b == STRING_RESULT) + m_type_handler= &type_handler_long_blob; + else if (a == INT_RESULT && b == INT_RESULT) + m_type_handler= &type_handler_longlong; + else if (a == ROW_RESULT || b == ROW_RESULT) + m_type_handler= &type_handler_row; + else if (a == TIME_RESULT || b == TIME_RESULT) + { + if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1) + { + /* + We're here if there's only one temporal data type: + either m_type_handler or h. + */ + if (b == TIME_RESULT) + m_type_handler= h; // Temporal types bit non-temporal types + } + else + { + /* + We're here if both m_type_handler and h are temporal data types. + - If both data types are TIME, we preserve TIME. + - If both data types are DATE, we preserve DATE. + Preserving DATE is needed for EXPLAIN FORMAT=JSON, + to print DATE constants using proper format: + 'YYYY-MM-DD' rather than 'YYYY-MM-DD 00:00:00'. + */ + if (m_type_handler->field_type() != h->field_type()) + m_type_handler= &type_handler_datetime; + } + } + else if ((a == INT_RESULT || a == DECIMAL_RESULT) && + (b == INT_RESULT || b == DECIMAL_RESULT)) + { + m_type_handler= &type_handler_newdecimal; + } + else + m_type_handler= &type_handler_double; + DBUG_ASSERT(m_type_handler == m_type_handler->type_handler_for_comparison()); + return false; +} + + +/** + Aggregate data type handler for LEAST/GRATEST. + aggregate_for_min_max() is close to aggregate_for_comparison(), + but tries to preserve the exact type handler for string, int and temporal + data types (instead of converting to super-types). + FLOAT is not preserved and is converted to its super-type (DOUBLE). + This should probably fixed eventually, for symmetry. +*/ + +bool +Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h) +{ + if (!m_type_handler->is_traditional_type() || + !h->is_traditional_type()) + { + /* + If at least one data type is non-traditional, + do aggregation for result immediately. + For now we suppose that these two expressions: + - LEAST(type1, type2) + - COALESCE(type1, type2) + return the same data type (or both expressions return error) + if type1 and/or type2 are non-traditional. + This may change in the future. + */ + h= type_handler_data-> + m_type_aggregator_for_result.find_handler(m_type_handler, h); + if (!h) + return true; + m_type_handler= h; + return false; + } + + Item_result a= cmp_type(); + Item_result b= h->cmp_type(); + DBUG_ASSERT(a != ROW_RESULT); // Disallowed by check_cols() in fix_fields() + DBUG_ASSERT(b != ROW_RESULT); // Disallowed by check_cols() in fix_fields() + + if (a == STRING_RESULT && b == STRING_RESULT) + m_type_handler= + Type_handler::aggregate_for_result_traditional(m_type_handler, h); + else if (a == INT_RESULT && b == INT_RESULT) + { + // BIT aggregates with non-BIT as BIGINT + if (m_type_handler != h) + { + if (m_type_handler == &type_handler_bit) + m_type_handler= &type_handler_longlong; + else if (h == &type_handler_bit) + h= &type_handler_longlong; + } + m_type_handler= + Type_handler::aggregate_for_result_traditional(m_type_handler, h); + } + else if (a == TIME_RESULT || b == TIME_RESULT) + { + if ((a == TIME_RESULT) + (b == TIME_RESULT) == 1) + { + /* + We're here if there's only one temporal data type: + either m_type_handler or h. + */ + if (b == TIME_RESULT) + m_type_handler= h; // Temporal types bit non-temporal types + } + else + { + /* + We're here if both m_type_handler and h are temporal data types. + */ + m_type_handler= + Type_handler::aggregate_for_result_traditional(m_type_handler, h); + } + } + else if ((a == INT_RESULT || a == DECIMAL_RESULT) && + (b == INT_RESULT || b == DECIMAL_RESULT)) + { + m_type_handler= &type_handler_newdecimal; + } + else + { + // Preserve FLOAT if two FLOATs, set to DOUBLE otherwise. + if (m_type_handler != &type_handler_float || h != &type_handler_float) + m_type_handler= &type_handler_double; + } + return false; +} + + +bool +Type_handler_hybrid_field_type::aggregate_for_min_max(const char *funcname, + Item **items, uint nitems) +{ + bool bit_and_non_bit_mixture_found= false; + uint32 max_display_length; + // LEAST/GREATEST require at least two arguments + DBUG_ASSERT(nitems > 1); + set_handler(items[0]->type_handler()); + max_display_length= items[0]->max_display_length(); + for (uint i= 1; i < nitems; i++) + { + const Type_handler *cur= items[i]->type_handler(); + set_if_bigger(max_display_length, items[i]->max_display_length()); + // Check if BIT + non-BIT, or non-BIT + BIT + bit_and_non_bit_mixture_found|= (m_type_handler == &type_handler_bit) != + (cur == &type_handler_bit); + if (aggregate_for_min_max(cur)) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + type_handler()->name().ptr(), cur->name().ptr(), funcname); + return true; + } + } + if (bit_and_non_bit_mixture_found && type_handler() == &type_handler_longlong) + set_handler(Type_handler::bit_and_int_mixture_handler(max_display_length)); + return false; +} + + +const Type_handler * +Type_handler::aggregate_for_num_op_traditional(const Type_handler *h0, + const Type_handler *h1) +{ + Item_result r0= h0->cmp_type(); + Item_result r1= h1->cmp_type(); + + if (r0 == REAL_RESULT || r1 == REAL_RESULT || + r0 == STRING_RESULT || r1 ==STRING_RESULT) + return &type_handler_double; + + if (r0 == TIME_RESULT || r1 == TIME_RESULT) + return &type_handler_datetime; + + if (r0 == DECIMAL_RESULT || r1 == DECIMAL_RESULT) + return &type_handler_newdecimal; + + DBUG_ASSERT(r0 == INT_RESULT && r1 == INT_RESULT); + return &type_handler_longlong; +} + + +const Type_aggregator::Pair* +Type_aggregator::find_pair(const Type_handler *handler1, + const Type_handler *handler2) const +{ + for (uint i= 0; i < m_array.elements(); i++) + { + const Pair& el= m_array.at(i); + if (el.eq(handler1, handler2) || + (m_is_commutative && el.eq(handler2, handler1))) + return ⪙ + } + return NULL; +} + + +bool +Type_handler_hybrid_field_type::aggregate_for_num_op(const Type_aggregator *agg, + const Type_handler *h0, + const Type_handler *h1) +{ + const Type_handler *hres; + if (h0->is_traditional_type() && h1->is_traditional_type()) + { + set_handler(Type_handler::aggregate_for_num_op_traditional(h0, h1)); + return false; + } + if ((hres= agg->find_handler(h0, h1))) + { + set_handler(hres); + return false; + } + return true; +} + + +/***************************************************************************/ + const Type_handler * Type_handler::get_handler_by_field_type(enum_field_types type) { @@ -235,7 +990,8 @@ Type_handler::make_num_distinct_aggregator_field(MEM_ROOT *mem_root, Field_double(NULL, item->max_length, (uchar *) (item->maybe_null ? "" : 0), item->maybe_null ? 1 : 0, Field::NONE, - item->name, item->decimals, 0, item->unsigned_flag); + &item->name, (uint8) item->decimals, + 0, item->unsigned_flag); } @@ -248,7 +1004,8 @@ Type_handler_float::make_num_distinct_aggregator_field(MEM_ROOT *mem_root, Field_float(NULL, item->max_length, (uchar *) (item->maybe_null ? "" : 0), item->maybe_null ? 1 : 0, Field::NONE, - item->name, item->decimals, 0, item->unsigned_flag); + &item->name, (uint8) item->decimals, + 0, item->unsigned_flag); } @@ -263,7 +1020,8 @@ Type_handler_decimal_result::make_num_distinct_aggregator_field( Field_new_decimal(NULL, item->max_length, (uchar *) (item->maybe_null ? "" : 0), item->maybe_null ? 1 : 0, Field::NONE, - item->name, item->decimals, 0, item->unsigned_flag); + &item->name, (uint8) item->decimals, + 0, item->unsigned_flag); } @@ -280,14 +1038,12 @@ Type_handler_int_result::make_num_distinct_aggregator_field(MEM_ROOT *mem_root, Field_longlong(NULL, item->max_length, (uchar *) (item->maybe_null ? "" : 0), item->maybe_null ? 1 : 0, Field::NONE, - item->name, 0, item->unsigned_flag); + &item->name, 0, item->unsigned_flag); } /***********************************************************************/ -#define TMPNAME "" - Field *Type_handler_tiny::make_conversion_table_field(TABLE *table, uint metadata, const Field *target) @@ -301,7 +1057,7 @@ Field *Type_handler_tiny::make_conversion_table_field(TABLE *table, bool unsigned_flag= ((Field_num*) target)->unsigned_flag; return new (table->in_use->mem_root) Field_tiny(NULL, 4 /*max_length*/, (uchar *) "", 1, Field::NONE, - TMPNAME, 0/*zerofill*/, unsigned_flag); + &empty_clex_str, 0/*zerofill*/, unsigned_flag); } @@ -313,7 +1069,7 @@ Field *Type_handler_short::make_conversion_table_field(TABLE *table, bool unsigned_flag= ((Field_num*) target)->unsigned_flag; return new (table->in_use->mem_root) Field_short(NULL, 6 /*max_length*/, (uchar *) "", 1, Field::NONE, - TMPNAME, 0/*zerofill*/, unsigned_flag); + &empty_clex_str, 0/*zerofill*/, unsigned_flag); } @@ -325,7 +1081,7 @@ Field *Type_handler_int24::make_conversion_table_field(TABLE *table, bool unsigned_flag= ((Field_num*) target)->unsigned_flag; return new (table->in_use->mem_root) Field_medium(NULL, 9 /*max_length*/, (uchar *) "", 1, Field::NONE, - TMPNAME, 0/*zerofill*/, unsigned_flag); + &empty_clex_str, 0/*zerofill*/, unsigned_flag); } @@ -337,7 +1093,7 @@ Field *Type_handler_long::make_conversion_table_field(TABLE *table, bool unsigned_flag= ((Field_num*) target)->unsigned_flag; return new (table->in_use->mem_root) Field_long(NULL, 11 /*max_length*/, (uchar *) "", 1, Field::NONE, - TMPNAME, 0/*zerofill*/, unsigned_flag); + &empty_clex_str, 0/*zerofill*/, unsigned_flag); } @@ -349,7 +1105,7 @@ Field *Type_handler_longlong::make_conversion_table_field(TABLE *table, bool unsigned_flag= ((Field_num*) target)->unsigned_flag; return new (table->in_use->mem_root) Field_longlong(NULL, 20 /*max_length*/,(uchar *) "", 1, Field::NONE, - TMPNAME, 0/*zerofill*/, unsigned_flag); + &empty_clex_str, 0/*zerofill*/, unsigned_flag); } @@ -361,7 +1117,7 @@ Field *Type_handler_float::make_conversion_table_field(TABLE *table, { return new (table->in_use->mem_root) Field_float(NULL, 12 /*max_length*/, (uchar *) "", 1, Field::NONE, - TMPNAME, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/); + &empty_clex_str, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/); } @@ -372,7 +1128,7 @@ Field *Type_handler_double::make_conversion_table_field(TABLE *table, { return new (table->in_use->mem_root) Field_double(NULL, 22 /*max_length*/, (uchar *) "", 1, Field::NONE, - TMPNAME, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/); + &empty_clex_str, 0/*dec*/, 0/*zerofill*/, 0/*unsigned_flag*/); } @@ -382,12 +1138,12 @@ Field *Type_handler_newdecimal::make_conversion_table_field(TABLE *table, const { int precision= metadata >> 8; - uint decimals= metadata & 0x00ff; + uint8 decimals= metadata & 0x00ff; uint32 max_length= my_decimal_precision_to_length(precision, decimals, false); DBUG_ASSERT(decimals <= DECIMAL_MAX_SCALE); return new (table->in_use->mem_root) Field_new_decimal(NULL, max_length, (uchar *) "", 1, Field::NONE, - TMPNAME, decimals, 0/*zerofill*/, 0/*unsigned*/); + &empty_clex_str, decimals, 0/*zerofill*/, 0/*unsigned*/); } @@ -403,7 +1159,7 @@ Field *Type_handler_olddecimal::make_conversion_table_field(TABLE *table, " column Name: %s.%s.%s.", target->table->s->db.str, target->table->s->table_name.str, - target->field_name); + target->field_name.str); return NULL; } @@ -414,7 +1170,7 @@ Field *Type_handler_year::make_conversion_table_field(TABLE *table, const { return new(table->in_use->mem_root) - Field_year(NULL, 4, (uchar *) "", 1, Field::NONE, TMPNAME); + Field_year(NULL, 4, (uchar *) "", 1, Field::NONE, &empty_clex_str); } @@ -424,7 +1180,7 @@ Field *Type_handler_null::make_conversion_table_field(TABLE *table, const { return new(table->in_use->mem_root) - Field_null(NULL, 0, Field::NONE, TMPNAME, target->charset()); + Field_null(NULL, 0, Field::NONE, &empty_clex_str, target->charset()); } @@ -434,7 +1190,7 @@ Field *Type_handler_timestamp::make_conversion_table_field(TABLE *table, const { return new_Field_timestamp(table->in_use->mem_root, NULL, (uchar *) "", 1, - Field::NONE, TMPNAME, table->s, target->decimals()); + Field::NONE, &empty_clex_str, table->s, target->decimals()); } @@ -445,7 +1201,7 @@ Field *Type_handler_timestamp2::make_conversion_table_field(TABLE *table, { return new(table->in_use->mem_root) Field_timestampf(NULL, (uchar *) "", 1, Field::NONE, - TMPNAME, table->s, metadata); + &empty_clex_str, table->s, metadata); } @@ -455,7 +1211,7 @@ Field *Type_handler_newdate::make_conversion_table_field(TABLE *table, const { return new(table->in_use->mem_root) - Field_newdate(NULL, (uchar *) "", 1, Field::NONE, TMPNAME); + Field_newdate(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str); } @@ -465,7 +1221,7 @@ Field *Type_handler_date::make_conversion_table_field(TABLE *table, const { return new(table->in_use->mem_root) - Field_date(NULL, (uchar *) "", 1, Field::NONE, TMPNAME); + Field_date(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str); } @@ -475,7 +1231,7 @@ Field *Type_handler_time::make_conversion_table_field(TABLE *table, const { return new_Field_time(table->in_use->mem_root, NULL, (uchar *) "", 1, - Field::NONE, TMPNAME, target->decimals()); + Field::NONE, &empty_clex_str, target->decimals()); } @@ -485,7 +1241,7 @@ Field *Type_handler_time2::make_conversion_table_field(TABLE *table, const { return new(table->in_use->mem_root) - Field_timef(NULL, (uchar *) "", 1, Field::NONE, TMPNAME, metadata); + Field_timef(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, metadata); } @@ -495,7 +1251,7 @@ Field *Type_handler_datetime::make_conversion_table_field(TABLE *table, const { return new_Field_datetime(table->in_use->mem_root, NULL, (uchar *) "", 1, - Field::NONE, TMPNAME, target->decimals()); + Field::NONE, &empty_clex_str, target->decimals()); } @@ -506,7 +1262,7 @@ Field *Type_handler_datetime2::make_conversion_table_field(TABLE *table, { return new(table->in_use->mem_root) Field_datetimef(NULL, (uchar *) "", 1, - Field::NONE, TMPNAME, metadata); + Field::NONE, &empty_clex_str, metadata); } @@ -519,7 +1275,7 @@ Field *Type_handler_bit::make_conversion_table_field(TABLE *table, uint32 max_length= 8 * (metadata >> 8U) + (metadata & 0x00ff); return new(table->in_use->mem_root) Field_bit_as_char(NULL, max_length, (uchar *) "", 1, - Field::NONE, TMPNAME); + Field::NONE, &empty_clex_str); } @@ -532,7 +1288,7 @@ Field *Type_handler_string::make_conversion_table_field(TABLE *table, uint32 max_length= (((metadata >> 4) & 0x300) ^ 0x300) + (metadata & 0x00ff); return new(table->in_use->mem_root) Field_string(NULL, max_length, (uchar *) "", 1, - Field::NONE, TMPNAME, target->charset()); + Field::NONE, &empty_clex_str, target->charset()); } @@ -543,7 +1299,7 @@ Field *Type_handler_varchar::make_conversion_table_field(TABLE *table, { return new(table->in_use->mem_root) Field_varstring(NULL, metadata, HA_VARCHAR_PACKLENGTH(metadata), - (uchar *) "", 1, Field::NONE, TMPNAME, + (uchar *) "", 1, Field::NONE, &empty_clex_str, table->s, target->charset()); } @@ -554,7 +1310,7 @@ Field *Type_handler_tiny_blob::make_conversion_table_field(TABLE *table, const { return new(table->in_use->mem_root) - Field_blob(NULL, (uchar *) "", 1, Field::NONE, TMPNAME, + Field_blob(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, table->s, 1, target->charset()); } @@ -565,7 +1321,7 @@ Field *Type_handler_blob::make_conversion_table_field(TABLE *table, const { return new(table->in_use->mem_root) - Field_blob(NULL, (uchar *) "", 1, Field::NONE, TMPNAME, + Field_blob(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, table->s, 2, target->charset()); } @@ -576,7 +1332,7 @@ Field *Type_handler_medium_blob::make_conversion_table_field(TABLE *table, const { return new(table->in_use->mem_root) - Field_blob(NULL, (uchar *) "", 1, Field::NONE, TMPNAME, + Field_blob(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, table->s, 3, target->charset()); } @@ -587,12 +1343,21 @@ Field *Type_handler_long_blob::make_conversion_table_field(TABLE *table, const { return new(table->in_use->mem_root) - Field_blob(NULL, (uchar *) "", 1, Field::NONE, TMPNAME, + Field_blob(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, table->s, 4, target->charset()); } #ifdef HAVE_SPATIAL +const Name Type_handler_geometry::m_name_geometry(C_STRING_WITH_LEN("geometry")); + + +const Type_handler *Type_handler_geometry::type_handler_for_comparison() const +{ + return &type_handler_geometry; +} + + Field *Type_handler_geometry::make_conversion_table_field(TABLE *table, uint metadata, const Field *target) @@ -606,7 +1371,7 @@ Field *Type_handler_geometry::make_conversion_table_field(TABLE *table, The statistics was already incremented when "target" was created. */ return new(table->in_use->mem_root) - Field_geom(NULL, (uchar *) "", 1, Field::NONE, TMPNAME, table->s, 4, + Field_geom(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str, table->s, 4, ((const Field_geom*) target)->geom_type, ((const Field_geom*) target)->srid); } @@ -621,7 +1386,7 @@ Field *Type_handler_enum::make_conversion_table_field(TABLE *table, DBUG_ASSERT(target->real_type() == MYSQL_TYPE_ENUM); return new(table->in_use->mem_root) Field_enum(NULL, target->field_length, - (uchar *) "", 1, Field::NONE, TMPNAME, + (uchar *) "", 1, Field::NONE, &empty_clex_str, metadata & 0x00ff/*pack_length()*/, ((const Field_enum*) target)->typelib, target->charset()); } @@ -636,7 +1401,4038 @@ Field *Type_handler_set::make_conversion_table_field(TABLE *table, DBUG_ASSERT(target->real_type() == MYSQL_TYPE_SET); return new(table->in_use->mem_root) Field_set(NULL, target->field_length, - (uchar *) "", 1, Field::NONE, TMPNAME, + (uchar *) "", 1, Field::NONE, &empty_clex_str, metadata & 0x00ff/*pack_length()*/, ((const Field_enum*) target)->typelib, target->charset()); } + +/*************************************************************************/ +bool Type_handler_null:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return false; +} + +bool Type_handler_tiny:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_int(MAX_TINYINT_WIDTH + def->sign_length()); +} + +bool Type_handler_short:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_int(MAX_SMALLINT_WIDTH + def->sign_length()); +} + +bool Type_handler_int24:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_int(MAX_MEDIUMINT_WIDTH + def->sign_length()); +} + +bool Type_handler_long:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_int(MAX_INT_WIDTH + def->sign_length()); +} + +bool Type_handler_longlong:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_int(MAX_BIGINT_WIDTH/*no sign_length() added*/); +} + +bool Type_handler_newdecimal:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_decimal(); +} + +bool Type_handler_olddecimal:: + Column_definition_fix_attributes(Column_definition *def) const +{ + DBUG_ASSERT(0); // Obsolete + return true; +} + +bool Type_handler_var_string:: + Column_definition_fix_attributes(Column_definition *def) const +{ + DBUG_ASSERT(0); // Obsolete + return true; +} + +bool Type_handler_varchar:: + Column_definition_fix_attributes(Column_definition *def) const +{ + /* + Long VARCHAR's are automaticly converted to blobs in mysql_prepare_table + if they don't have a default value + */ + return def->check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_BLOBLENGTH); +} + +bool Type_handler_string:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->check_length(ER_TOO_BIG_FIELDLENGTH, MAX_FIELD_CHARLENGTH); +} + +bool Type_handler_blob_common:: + Column_definition_fix_attributes(Column_definition *def) const +{ + def->flags|= BLOB_FLAG; + return def->check_length(ER_TOO_BIG_DISPLAYWIDTH, MAX_FIELD_BLOBLENGTH); +} + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Column_definition_fix_attributes(Column_definition *def) const +{ + def->flags|= BLOB_FLAG; + return false; +} +#endif + +bool Type_handler_year:: + Column_definition_fix_attributes(Column_definition *def) const +{ + if (!def->length || def->length != 2) + def->length= 4; // Default length + def->flags|= ZEROFILL_FLAG | UNSIGNED_FLAG; + return false; +} + +bool Type_handler_float:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_real(MAX_FLOAT_STR_LENGTH); +} + + +bool Type_handler_double:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_real(DBL_DIG + 7); +} + +bool Type_handler_timestamp_common:: + Column_definition_fix_attributes(Column_definition *def) const +{ + def->flags|= UNSIGNED_FLAG; + return def->fix_attributes_temporal_with_time(MAX_DATETIME_WIDTH); +} + +bool Type_handler_date_common:: + Column_definition_fix_attributes(Column_definition *def) const +{ + // We don't support creation of MYSQL_TYPE_DATE anymore + def->set_handler(&type_handler_newdate); + def->length= MAX_DATE_WIDTH; + return false; +} + +bool Type_handler_time_common:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_temporal_with_time(MIN_TIME_WIDTH); +} + +bool Type_handler_datetime_common:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_temporal_with_time(MAX_DATETIME_WIDTH); +} + +bool Type_handler_set:: + Column_definition_fix_attributes(Column_definition *def) const +{ + def->pack_length= get_set_pack_length(def->interval_list.elements); + return false; +} + +bool Type_handler_enum:: + Column_definition_fix_attributes(Column_definition *def) const +{ + def->pack_length= get_enum_pack_length(def->interval_list.elements); + return false; +} + +bool Type_handler_bit:: + Column_definition_fix_attributes(Column_definition *def) const +{ + return def->fix_attributes_bit(); +} + +/*************************************************************************/ + +bool Type_handler:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + def->create_length_to_internal_length_simple(); + return false; +} + +bool Type_handler_null:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + def->create_length_to_internal_length_null(); + return false; +} + +bool Type_handler_newdecimal:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + def->create_length_to_internal_length_newdecimal(); + return false; +} + +bool Type_handler_bit:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + return def->prepare_stage1_bit(thd, mem_root, file, table_flags); +} + +bool Type_handler_typelib:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + return def->prepare_stage1_typelib(thd, mem_root, file, table_flags); +} + + +bool Type_handler_string_result:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + return def->prepare_stage1_string(thd, mem_root, file, table_flags); +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + def->create_length_to_internal_length_string(); + return def->prepare_blob_field(thd); +} +#endif + + +/*************************************************************************/ + +bool Type_handler:: + Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const +{ + def->redefine_stage1_common(dup, file, schema); + def->create_length_to_internal_length_simple(); + return false; +} + + +bool Type_handler_null:: + Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const +{ + def->redefine_stage1_common(dup, file, schema); + def->create_length_to_internal_length_null(); + return false; +} + + +bool Type_handler_newdecimal:: + Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const +{ + def->redefine_stage1_common(dup, file, schema); + def->create_length_to_internal_length_newdecimal(); + return false; +} + + +bool Type_handler_string_result:: + Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const +{ + def->redefine_stage1_common(dup, file, schema); + def->create_length_to_internal_length_string(); + return false; +} + + +bool Type_handler_typelib:: + Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const +{ + def->redefine_stage1_common(dup, file, schema); + def->create_length_to_internal_length_typelib(); + return false; +} + + +bool Type_handler_bit:: + Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const +{ + def->redefine_stage1_common(dup, file, schema); + /* + If we are replacing a field with a BIT field, we need + to initialize pack_flag. + */ + def->pack_flag= FIELDFLAG_NUMBER; + if (!(file->ha_table_flags() & HA_CAN_BIT_FIELD)) + def->pack_flag|= FIELDFLAG_TREAT_BIT_AS_CHAR; + def->create_length_to_internal_length_bit(); + return false; +} + + +/*************************************************************************/ + +bool Type_handler:: + Column_definition_prepare_stage2_legacy(Column_definition *def, + enum_field_types type) const +{ + def->pack_flag= f_settype((uint) type); + return false; +} + +bool Type_handler:: + Column_definition_prepare_stage2_legacy_num(Column_definition *def, + enum_field_types type) const +{ + def->pack_flag= def->pack_flag_numeric(def->decimals) | + f_settype((uint) type); + return false; +} + +bool Type_handler:: + Column_definition_prepare_stage2_legacy_real(Column_definition *def, + enum_field_types type) const +{ + uint dec= def->decimals; + /* + User specified FLOAT() or DOUBLE() without precision. Change to + FLOATING_POINT_DECIMALS to keep things compatible with earlier MariaDB + versions. + */ + if (dec >= FLOATING_POINT_DECIMALS) + dec= FLOATING_POINT_DECIMALS; + def->pack_flag= def->pack_flag_numeric(dec) | f_settype((uint) type); + return false; +} + +bool Type_handler_newdecimal:: + Column_definition_prepare_stage2(Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + def->pack_flag= def->pack_flag_numeric(def->decimals); + return false; +} + +bool Type_handler_blob_common:: + Column_definition_prepare_stage2(Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + return def->prepare_stage2_blob(file, table_flags, FIELDFLAG_BLOB); +} + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Column_definition_prepare_stage2(Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + if (!(table_flags & HA_CAN_GEOMETRY)) + { + my_error(ER_CHECK_NOT_IMPLEMENTED, MYF(0), "GEOMETRY"); + return true; + } + return def->prepare_stage2_blob(file, table_flags, FIELDFLAG_GEOM); +} +#endif + +bool Type_handler_varchar:: + Column_definition_prepare_stage2(Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + return def->prepare_stage2_varchar(table_flags); +} + +bool Type_handler_string:: + Column_definition_prepare_stage2(Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + def->pack_flag= (def->charset->state & MY_CS_BINSORT) ? FIELDFLAG_BINARY : 0; + return false; +} + +bool Type_handler_enum:: + Column_definition_prepare_stage2(Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + uint dummy; + return def->prepare_stage2_typelib("ENUM", FIELDFLAG_INTERVAL, &dummy); +} + +bool Type_handler_set:: + Column_definition_prepare_stage2(Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + uint dup_count; + if (def->prepare_stage2_typelib("SET", FIELDFLAG_BITFIELD, &dup_count)) + return true; + /* Check that count of unique members is not more then 64 */ + if (def->interval->count - dup_count > sizeof(longlong)*8) + { + my_error(ER_TOO_BIG_SET, MYF(0), def->field_name.str); + return true; + } + return false; +} + +bool Type_handler_bit:: + Column_definition_prepare_stage2(Column_definition *def, + handler *file, + ulonglong table_flags) const +{ + /* + We have sql_field->pack_flag already set here, see + mysql_prepare_create_table(). + */ + return false; +} + +/*************************************************************************/ + +uint32 Type_handler_time::calc_pack_length(uint32 length) const +{ + return length > MIN_TIME_WIDTH ? + hires_bytes(length - 1 - MIN_TIME_WIDTH) : 3; +} + +uint32 Type_handler_time2::calc_pack_length(uint32 length) const +{ + return length > MIN_TIME_WIDTH ? + my_time_binary_length(length - MIN_TIME_WIDTH - 1) : 3; +} + +uint32 Type_handler_timestamp::calc_pack_length(uint32 length) const +{ + return length > MAX_DATETIME_WIDTH ? + 4 + sec_part_bytes(length - 1 - MAX_DATETIME_WIDTH) : 4; +} + +uint32 Type_handler_timestamp2::calc_pack_length(uint32 length) const +{ + return length > MAX_DATETIME_WIDTH ? + my_timestamp_binary_length(length - MAX_DATETIME_WIDTH - 1) : 4; +} + +uint32 Type_handler_datetime::calc_pack_length(uint32 length) const +{ + return length > MAX_DATETIME_WIDTH ? + hires_bytes(length - 1 - MAX_DATETIME_WIDTH) : 8; +} + +uint32 Type_handler_datetime2::calc_pack_length(uint32 length) const +{ + return length > MAX_DATETIME_WIDTH ? + my_datetime_binary_length(length - MAX_DATETIME_WIDTH - 1) : 5; +} + +uint32 Type_handler_tiny_blob::calc_pack_length(uint32 length) const +{ + return 1 + portable_sizeof_char_ptr; +} + +uint32 Type_handler_blob::calc_pack_length(uint32 length) const +{ + return 2 + portable_sizeof_char_ptr; +} + +uint32 Type_handler_medium_blob::calc_pack_length(uint32 length) const +{ + return 3 + portable_sizeof_char_ptr; +} + +uint32 Type_handler_long_blob::calc_pack_length(uint32 length) const +{ + return 4 + portable_sizeof_char_ptr; +} + +#ifdef HAVE_SPATIAL +uint32 Type_handler_geometry::calc_pack_length(uint32 length) const +{ + return 4 + portable_sizeof_char_ptr; +} +#endif + +uint32 Type_handler_newdecimal::calc_pack_length(uint32 length) const +{ + abort(); // This shouldn't happen + return 0; +} + +uint32 Type_handler_set::calc_pack_length(uint32 length) const +{ + abort(); // This shouldn't happen + return 0; +} + +uint32 Type_handler_enum::calc_pack_length(uint32 length) const +{ + abort(); // This shouldn't happen + return 0; +} + + +/*************************************************************************/ +Field *Type_handler::make_and_init_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + Field *field= make_table_field(name, addr, attr, table); + if (field) + field->init(table); + return field; +} + + +Field *Type_handler_tiny::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + return new (table->in_use->mem_root) + Field_tiny(addr.ptr, attr.max_char_length(), + addr.null_ptr, addr.null_bit, + Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag); +} + + +Field *Type_handler_short::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_short(addr.ptr, attr.max_char_length(), + addr.null_ptr, addr.null_bit, + Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag); +} + + +Field *Type_handler_int24::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + return new (table->in_use->mem_root) + Field_medium(addr.ptr, attr.max_char_length(), + addr.null_ptr, addr.null_bit, + Field::NONE, name, + 0/*zerofill*/, attr.unsigned_flag); +} + + +Field *Type_handler_long::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + return new (table->in_use->mem_root) + Field_long(addr.ptr, attr.max_char_length(), + addr.null_ptr, addr.null_bit, + Field::NONE, name, 0/*zerofill*/, attr.unsigned_flag); +} + + +Field *Type_handler_longlong::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + return new (table->in_use->mem_root) + Field_longlong(addr.ptr, attr.max_char_length(), + addr.null_ptr, addr.null_bit, + Field::NONE, name, + 0/*zerofill*/, attr.unsigned_flag); +} + + +Field *Type_handler_float::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + return new (table->in_use->mem_root) + Field_float(addr.ptr, attr.max_char_length(), + addr.null_ptr, addr.null_bit, + Field::NONE, name, + (uint8) attr.decimals, 0/*zerofill*/, attr.unsigned_flag); +} + + +Field *Type_handler_double::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + return new (table->in_use->mem_root) + Field_double(addr.ptr, attr.max_char_length(), + addr.null_ptr, addr.null_bit, + Field::NONE, name, + (uint8) attr.decimals, 0/*zerofill*/, attr.unsigned_flag); +} + + +Field * +Type_handler_olddecimal::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + /* + Currently make_table_field() is used for Item purpose only. + On Item level we have type_handler_newdecimal only. + For now we have DBUG_ASSERT(0). + It will be removed when we reuse Type_handler::make_table_field() + in make_field() in field.cc, to open old tables with old decimal. + */ + DBUG_ASSERT(0); + return new (table->in_use->mem_root) + Field_decimal(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field::NONE, name, (uint8) attr.decimals, + 0/*zerofill*/,attr.unsigned_flag); +} + + +Field * +Type_handler_newdecimal::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + uint8 dec= (uint8) attr.decimals; + uint8 intg= (uint8) (attr.decimal_precision() - dec); + uint32 len= attr.max_char_length(); + + /* + Trying to put too many digits overall in a DECIMAL(prec,dec) + will always throw a warning. We must limit dec to + DECIMAL_MAX_SCALE however to prevent an assert() later. + */ + + if (dec > 0) + { + signed int overflow; + + dec= MY_MIN(dec, DECIMAL_MAX_SCALE); + + /* + If the value still overflows the field with the corrected dec, + we'll throw out decimals rather than integers. This is still + bad and of course throws a truncation warning. + +1: for decimal point + */ + + const int required_length= + my_decimal_precision_to_length(intg + dec, dec, attr.unsigned_flag); + + overflow= required_length - len; + + if (overflow > 0) + dec= MY_MAX(0, dec - overflow); // too long, discard fract + else + /* Corrected value fits. */ + len= required_length; + } + return new (table->in_use->mem_root) + Field_new_decimal(addr.ptr, len, addr.null_ptr, addr.null_bit, + Field::NONE, name, + dec, 0/*zerofill*/, attr.unsigned_flag); +} + + +Field *Type_handler_year::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + return new (table->in_use->mem_root) + Field_year(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field::NONE, name); +} + + +Field *Type_handler_null::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_null(addr.ptr, attr.max_length, + Field::NONE, name, attr.collation.collation); +} + + +Field *Type_handler_timestamp::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new_Field_timestamp(table->in_use->mem_root, + addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, table->s, attr.decimals); +} + + +Field *Type_handler_timestamp2::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + /* + Will be changed to "new Field_timestampf" when we reuse + make_table_field() for make_field() purposes in field.cc. + */ + return new_Field_timestamp(table->in_use->mem_root, + addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, table->s, attr.decimals); +} + + +Field *Type_handler_newdate::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_newdate(addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name); +} + + +Field *Type_handler_date::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + /* + DBUG_ASSERT will be removed when we reuse make_table_field() + for make_field() in field.cc + */ + DBUG_ASSERT(0); + return new (table->in_use->mem_root) + Field_date(addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name); +} + + +Field *Type_handler_time::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new_Field_time(table->in_use->mem_root, + addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, attr.decimals); +} + + +Field *Type_handler_time2::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + + +{ + /* + Will be changed to "new Field_timef" when we reuse + make_table_field() for make_field() purposes in field.cc. + */ + return new_Field_time(table->in_use->mem_root, + addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, attr.decimals); +} + + +Field *Type_handler_datetime::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new_Field_datetime(table->in_use->mem_root, + addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, attr.decimals); +} + + +Field *Type_handler_datetime2::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + /* + Will be changed to "new Field_datetimef" when we reuse + make_table_field() for make_field() purposes in field.cc. + */ + return new_Field_datetime(table->in_use->mem_root, + addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, attr.decimals); +} + + +Field *Type_handler_bit::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_bit_as_char(addr.ptr, attr.max_length, + addr.null_ptr, addr.null_bit, + Field::NONE, name); +} + + +Field *Type_handler_string::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_string(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field::NONE, name, attr.collation); +} + + +Field *Type_handler_varchar::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_varstring(addr.ptr, attr.max_length, + HA_VARCHAR_PACKLENGTH(attr.max_length), + addr.null_ptr, addr.null_bit, + Field::NONE, name, + table->s, attr.collation); +} + + +Field *Type_handler_tiny_blob::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_blob(addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, table->s, + 1, attr.collation); +} + + +Field *Type_handler_blob::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_blob(addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, table->s, + 2, attr.collation); +} + + +Field * +Type_handler_medium_blob::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_blob(addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, table->s, + 3, attr.collation); +} + + +Field *Type_handler_long_blob::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + return new (table->in_use->mem_root) + Field_blob(addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, table->s, + 4, attr.collation); +} + + + +#ifdef HAVE_SPATIAL +Field *Type_handler_geometry::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + return new (table->in_use->mem_root) + Field_geom(addr.ptr, addr.null_ptr, addr.null_bit, + Field::NONE, name, table->s, 4, + (Field::geometry_type) attr.uint_geometry_type(), + 0); +} +#endif + + +Field *Type_handler_enum::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const +{ + TYPELIB *typelib= attr.get_typelib(); + DBUG_ASSERT(typelib); + return new (table->in_use->mem_root) + Field_enum(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field::NONE, name, + get_enum_pack_length(typelib->count), typelib, + attr.collation); +} + + +Field *Type_handler_set::make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + +{ + TYPELIB *typelib= attr.get_typelib(); + DBUG_ASSERT(typelib); + return new (table->in_use->mem_root) + Field_set(addr.ptr, attr.max_length, addr.null_ptr, addr.null_bit, + Field::NONE, name, + get_enum_pack_length(typelib->count), typelib, + attr.collation); +} + +/*************************************************************************/ + +/* + If length is not specified for a varchar parameter, set length to the + maximum length of the actual argument. Goals are: + - avoid to allocate too much unused memory for m_var_table + - allow length check inside the callee rather than during copy of + returned values in output variables. + - allow varchar parameter size greater than 4000 + Default length has been stored in "decimal" member during parse. +*/ +bool Type_handler_varchar::adjust_spparam_type(Spvar_definition *def, + Item *from) const +{ + if (def->decimals) + { + uint def_max_char_length= MAX_FIELD_VARCHARLENGTH / def->charset->mbmaxlen; + uint arg_max_length= from->max_char_length(); + set_if_smaller(arg_max_length, def_max_char_length); + def->length= arg_max_length > 0 ? arg_max_length : def->decimals; + def->create_length_to_internal_length_string(); + } + return false; +} + +/*************************************************************************/ + +uint32 Type_handler_decimal_result::max_display_length(const Item *item) const +{ + return item->max_length; +} + + +uint32 Type_handler_temporal_result::max_display_length(const Item *item) const +{ + return item->max_length; +} + + +uint32 Type_handler_string_result::max_display_length(const Item *item) const +{ + return item->max_length; +} + + +uint32 Type_handler_year::max_display_length(const Item *item) const +{ + return item->max_length; +} + + +uint32 Type_handler_bit::max_display_length(const Item *item) const +{ + return item->max_length; +} + +/*************************************************************************/ + +int Type_handler_time_common::Item_save_in_field(Item *item, Field *field, + bool no_conversions) const +{ + return item->save_time_in_field(field, no_conversions); +} + +int Type_handler_temporal_with_date::Item_save_in_field(Item *item, + Field *field, + bool no_conversions) + const +{ + return item->save_date_in_field(field, no_conversions); +} + + +int Type_handler_string_result::Item_save_in_field(Item *item, Field *field, + bool no_conversions) const +{ + return item->save_str_in_field(field, no_conversions); +} + + +int Type_handler_real_result::Item_save_in_field(Item *item, Field *field, + bool no_conversions) const +{ + return item->save_real_in_field(field, no_conversions); +} + + +int Type_handler_decimal_result::Item_save_in_field(Item *item, Field *field, + bool no_conversions) const +{ + return item->save_decimal_in_field(field, no_conversions); +} + + +int Type_handler_int_result::Item_save_in_field(Item *item, Field *field, + bool no_conversions) const +{ + return item->save_int_in_field(field, no_conversions); +} + + +/***********************************************************************/ + +bool Type_handler_row::set_comparator_func(Arg_comparator *cmp) const +{ + return cmp->set_cmp_func_row(); +} + +bool Type_handler_int_result::set_comparator_func(Arg_comparator *cmp) const +{ + return cmp->set_cmp_func_int(); +} + +bool Type_handler_real_result::set_comparator_func(Arg_comparator *cmp) const +{ + return cmp->set_cmp_func_real(); +} + +bool Type_handler_decimal_result::set_comparator_func(Arg_comparator *cmp) const +{ + return cmp->set_cmp_func_decimal(); +} + +bool Type_handler_string_result::set_comparator_func(Arg_comparator *cmp) const +{ + return cmp->set_cmp_func_string(); +} + +bool Type_handler_time_common::set_comparator_func(Arg_comparator *cmp) const +{ + return cmp->set_cmp_func_time(); +} + +bool +Type_handler_temporal_with_date::set_comparator_func(Arg_comparator *cmp) const +{ + return cmp->set_cmp_func_datetime(); +} + + +/*************************************************************************/ + +bool Type_handler_temporal_result:: + can_change_cond_ref_to_const(Item_bool_func2 *target, + Item *target_expr, Item *target_value, + Item_bool_func2 *source, + Item *source_expr, Item *source_const) + const +{ + if (source->compare_type_handler()->cmp_type() != TIME_RESULT) + return false; + + /* + Can't rewrite: + WHERE COALESCE(time_column)='00:00:00' + AND COALESCE(time_column)=DATE'2015-09-11' + to + WHERE DATE'2015-09-11'='00:00:00' + AND COALESCE(time_column)=DATE'2015-09-11' + because the left part will erroneously try to parse '00:00:00' + as DATE, not as TIME. + + TODO: It could still be rewritten to: + WHERE DATE'2015-09-11'=TIME'00:00:00' + AND COALESCE(time_column)=DATE'2015-09-11' + i.e. we need to replace both target_expr and target_value + at the same time. This is not supported yet. + */ + return target_value->cmp_type() == TIME_RESULT; +} + + +bool Type_handler_string_result:: + can_change_cond_ref_to_const(Item_bool_func2 *target, + Item *target_expr, Item *target_value, + Item_bool_func2 *source, + Item *source_expr, Item *source_const) + const +{ + if (source->compare_type_handler()->cmp_type() != STRING_RESULT) + return false; + /* + In this example: + SET NAMES utf8 COLLATE utf8_german2_ci; + DROP TABLE IF EXISTS t1; + CREATE TABLE t1 (a CHAR(10) CHARACTER SET utf8); + INSERT INTO t1 VALUES ('o-umlaut'),('oe'); + SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci AND a='oe'; + + the query should return only the row with 'oe'. + It should not return 'o-umlaut', because 'o-umlaut' does not match + the right part of the condition: a='oe' + ('o-umlaut' is not equal to 'oe' in utf8_general_ci, + which is the collation of the field "a"). + + If we change the right part from: + ... AND a='oe' + to + ... AND 'oe' COLLATE utf8_german2_ci='oe' + it will be evalulated to TRUE and removed from the condition, + so the overall query will be simplified to: + + SELECT * FROM t1 WHERE a='oe' COLLATE utf8_german2_ci; + + which will erroneously start to return both 'oe' and 'o-umlaut'. + So changing "expr" to "const" is not possible if the effective + collations of "target" and "source" are not exactly the same. + + Note, the code before the fix for MDEV-7152 only checked that + collations of "source_const" and "target_value" are the same. + This was not enough, as the bug report demonstrated. + */ + return + target->compare_collation() == source->compare_collation() && + target_value->collation.collation == source_const->collation.collation; +} + + +bool Type_handler_numeric:: + can_change_cond_ref_to_const(Item_bool_func2 *target, + Item *target_expr, Item *target_value, + Item_bool_func2 *source, + Item *source_expr, Item *source_const) + const +{ + /* + The collations of "target" and "source" do not make sense for numeric + data types. + */ + return target->compare_type_handler() == source->compare_type_handler(); +} + + +/*************************************************************************/ + +Item_cache * +Type_handler_row::Item_get_cache(THD *thd, const Item *item) const +{ + return new (thd->mem_root) Item_cache_row(thd); +} + +Item_cache * +Type_handler_int_result::Item_get_cache(THD *thd, const Item *item) const +{ + return new (thd->mem_root) Item_cache_int(thd, item->type_handler()); +} + +Item_cache * +Type_handler_real_result::Item_get_cache(THD *thd, const Item *item) const +{ + return new (thd->mem_root) Item_cache_real(thd); +} + +Item_cache * +Type_handler_decimal_result::Item_get_cache(THD *thd, const Item *item) const +{ + return new (thd->mem_root) Item_cache_decimal(thd); +} + +Item_cache * +Type_handler_string_result::Item_get_cache(THD *thd, const Item *item) const +{ + return new (thd->mem_root) Item_cache_str(thd, item); +} + +Item_cache * +Type_handler_temporal_result::Item_get_cache(THD *thd, const Item *item) const +{ + return new (thd->mem_root) Item_cache_temporal(thd, item->type_handler()); +} + +/*************************************************************************/ + +bool Type_handler_int_result:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + uint unsigned_flag= items[0]->unsigned_flag; + for (uint i= 1; i < nitems; i++) + { + if (unsigned_flag != items[i]->unsigned_flag) + { + // Convert a mixture of signed and unsigned int to decimal + handler->set_handler(&type_handler_newdecimal); + func->aggregate_attributes_decimal(items, nitems); + return false; + } + } + func->aggregate_attributes_int(items, nitems); + return false; +} + + +bool Type_handler_real_result:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + func->aggregate_attributes_real(items, nitems); + return false; +} + + +bool Type_handler_decimal_result:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + func->aggregate_attributes_decimal(items, nitems); + return false; +} + + +bool Type_handler_string_result:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + return func->aggregate_attributes_string(func_name, items, nitems); +} + + + +/* + We can have enum/set type after merging only if we have one enum|set + field (or MIN|MAX(enum|set field)) and number of NULL fields +*/ +bool Type_handler_typelib:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + TYPELIB *typelib= NULL; + for (uint i= 0; i < nitems; i++) + { + if ((typelib= items[i]->get_typelib())) + break; + } + DBUG_ASSERT(typelib); // There must be at least one typelib + func->set_typelib(typelib); + return func->aggregate_attributes_string(func_name, items, nitems); +} + + +bool Type_handler_blob_common:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + if (func->aggregate_attributes_string(func_name, items, nitems)) + return true; + handler->set_handler(blob_type_handler(func->max_length)); + return false; +} + + +bool Type_handler_date_common:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + func->fix_attributes_date(); + return false; +} + + +bool Type_handler_time_common:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + func->aggregate_attributes_temporal(MIN_TIME_WIDTH, items, nitems); + return false; +} + + +bool Type_handler_datetime_common:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems); + return false; +} + + +bool Type_handler_timestamp_common:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + func->aggregate_attributes_temporal(MAX_DATETIME_WIDTH, items, nitems); + return false; +} + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Item_hybrid_func_fix_attributes(THD *thd, + const char *func_name, + Type_handler_hybrid_field_type *handler, + Type_all_attributes *func, + Item **items, uint nitems) const +{ + DBUG_ASSERT(nitems > 0); + Type_geometry_attributes gattr(items[0]->type_handler(), items[0]); + for (uint i= 1; i < nitems; i++) + gattr.join(items[i]); + func->set_geometry_type(gattr.get_geometry_type()); + func->collation.set(&my_charset_bin); + func->unsigned_flag= false; + func->decimals= 0; + func->max_length= (uint32) UINT_MAX32; + func->set_maybe_null(true); + return false; +} +#endif + + +/*************************************************************************/ + +bool Type_handler:: + Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const +{ + /* + Aggregating attributes for LEAST/GREATES is exactly the same + with aggregating for CASE-alike functions (e.g. COALESCE) + for the majority of data type handlers. + */ + return Item_hybrid_func_fix_attributes(thd, func->func_name(), + func, func, items, nitems); +} + + +bool Type_handler_real_result:: + Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const +{ + /* + DOUBLE is an exception and aggregates attributes differently + for LEAST/GREATEST vs CASE-alike functions. See the comment in + Item_func_min_max::aggregate_attributes_real(). + */ + func->aggregate_attributes_real(items, nitems); + return false; +} + +/*************************************************************************/ + +/** + MAX/MIN for the traditional numeric types preserve the exact data type + from Fields, but do not preserve the exact type from Items: + MAX(float_field) -> FLOAT + MAX(smallint_field) -> LONGLONG + MAX(COALESCE(float_field)) -> DOUBLE + MAX(COALESCE(smallint_field)) -> LONGLONG + QQ: Items should probably be fixed to preserve the exact type. +*/ +bool Type_handler_numeric:: + Item_sum_hybrid_fix_length_and_dec_numeric(Item_sum_hybrid *func, + const Type_handler *handler) + const +{ + Item *item= func->arguments()[0]; + Item *item2= item->real_item(); + func->Type_std_attributes::set(item); + /* MIN/MAX can return NULL for empty set indepedent of the used column */ + func->maybe_null= func->null_value= true; + if (item2->type() == Item::FIELD_ITEM) + func->set_handler(item2->type_handler()); + else + func->set_handler(handler); + return false; +} + + +bool Type_handler_int_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + return Item_sum_hybrid_fix_length_and_dec_numeric(func, + &type_handler_longlong); +} + + +bool Type_handler_real_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + (void) Item_sum_hybrid_fix_length_and_dec_numeric(func, + &type_handler_double); + func->max_length= func->float_length(func->decimals); + return false; +} + + +bool Type_handler_decimal_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + return Item_sum_hybrid_fix_length_and_dec_numeric(func, + &type_handler_newdecimal); +} + + +/** + MAX(str_field) converts ENUM/SET to CHAR, and preserve all other types + for Fields. + QQ: This works differently from UNION, which preserve the exact data + type for ENUM/SET if the joined ENUM/SET fields are equally defined. + Perhaps should be fixed. + MAX(str_item) chooses the best suitable string type. +*/ +bool Type_handler_string_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + Item *item= func->arguments()[0]; + Item *item2= item->real_item(); + func->Type_std_attributes::set(item); + func->maybe_null= func->null_value= true; + if (item2->type() == Item::FIELD_ITEM) + { + // Fields: convert ENUM/SET to CHAR, preserve the type otherwise. + func->set_handler(item->type_handler()); + } + else + { + // Items: choose VARCHAR/BLOB/MEDIUMBLOB/LONGBLOB, depending on length. + func->set_handler(type_handler_varchar. + type_handler_adjusted_to_max_octet_length(func->max_length, + func->collation.collation)); + } + return false; +} + + +/** + Traditional temporal types always preserve the type of the argument. +*/ +bool Type_handler_temporal_result:: + Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const +{ + Item *item= func->arguments()[0]; + func->Type_std_attributes::set(item); + func->maybe_null= func->null_value= true; + func->set_handler(item->type_handler()); + return false; +} + + +/*************************************************************************/ + +bool Type_handler_int_result:: + Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_real_result:: + Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_string_result:: + Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Item_sum_sum_fix_length_and_dec(Item_sum_sum *item) const +{ + return Item_func_or_sum_illegal_param("sum"); +} +#endif + + +/*************************************************************************/ + +bool Type_handler_int_result:: + Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_real_result:: + Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_string_result:: + Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Item_sum_avg_fix_length_and_dec(Item_sum_avg *item) const +{ + return Item_func_or_sum_illegal_param("avg"); +} +#endif + + +/*************************************************************************/ + +bool Type_handler_int_result:: + Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_real_result:: + Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_string_result:: + Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Item_sum_variance_fix_length_and_dec(Item_sum_variance *item) const +{ + return Item_func_or_sum_illegal_param(item); +} +#endif + + +/*************************************************************************/ + +bool Type_handler_real_result::Item_val_bool(Item *item) const +{ + return item->val_real() != 0.0; +} + +bool Type_handler_int_result::Item_val_bool(Item *item) const +{ + return item->val_int() != 0; +} + +bool Type_handler_decimal_result::Item_val_bool(Item *item) const +{ + my_decimal decimal_value; + my_decimal *val= item->val_decimal(&decimal_value); + if (val) + return !my_decimal_is_zero(val); + return false; +} + +bool Type_handler_temporal_result::Item_val_bool(Item *item) const +{ + return item->val_real() != 0.0; +} + +bool Type_handler_string_result::Item_val_bool(Item *item) const +{ + return item->val_real() != 0.0; +} + + +/*************************************************************************/ + +longlong Type_handler_real_result:: + Item_val_int_signed_typecast(Item *item) const +{ + return item->val_int(); +} + +longlong Type_handler_int_result:: + Item_val_int_signed_typecast(Item *item) const +{ + return item->val_int(); +} + +longlong Type_handler_decimal_result:: + Item_val_int_signed_typecast(Item *item) const +{ + return item->val_int(); +} + +longlong Type_handler_temporal_result:: + Item_val_int_signed_typecast(Item *item) const +{ + return item->val_int(); +} + +longlong Type_handler_string_result:: + Item_val_int_signed_typecast(Item *item) const +{ + return item->val_int_signed_typecast_from_str(); +} + +/*************************************************************************/ + +longlong Type_handler_real_result:: + Item_val_int_unsigned_typecast(Item *item) const +{ + return item->val_int_unsigned_typecast_from_int(); +} + +longlong Type_handler_int_result:: + Item_val_int_unsigned_typecast(Item *item) const +{ + return item->val_int_unsigned_typecast_from_int(); +} + +longlong Type_handler_decimal_result:: + Item_val_int_unsigned_typecast(Item *item) const +{ + return item->val_int_unsigned_typecast_from_decimal(); +} + +longlong Type_handler_temporal_result:: + Item_val_int_unsigned_typecast(Item *item) const +{ + return item->val_int_unsigned_typecast_from_int(); +} + +longlong Type_handler_string_result:: + Item_val_int_unsigned_typecast(Item *item) const +{ + return item->val_int_unsigned_typecast_from_str(); +} + +/*************************************************************************/ + +String * +Type_handler_real_result::Item_func_hex_val_str_ascii(Item_func_hex *item, + String *str) const +{ + return item->val_str_ascii_from_val_real(str); +} + + +String * +Type_handler_decimal_result::Item_func_hex_val_str_ascii(Item_func_hex *item, + String *str) const +{ + return item->val_str_ascii_from_val_real(str); +} + + +String * +Type_handler_int_result::Item_func_hex_val_str_ascii(Item_func_hex *item, + String *str) const +{ + return item->val_str_ascii_from_val_int(str); +} + + +String * +Type_handler_temporal_result::Item_func_hex_val_str_ascii(Item_func_hex *item, + String *str) const +{ + return item->val_str_ascii_from_val_str(str); +} + + +String * +Type_handler_string_result::Item_func_hex_val_str_ascii(Item_func_hex *item, + String *str) const +{ + return item->val_str_ascii_from_val_str(str); +} + +/***************************************************************************/ + +String * +Type_handler_decimal_result::Item_func_hybrid_field_type_val_str( + Item_func_hybrid_field_type *item, + String *str) const +{ + return item->val_str_from_decimal_op(str); +} + + +double +Type_handler_decimal_result::Item_func_hybrid_field_type_val_real( + Item_func_hybrid_field_type *item) + const +{ + return item->val_real_from_decimal_op(); +} + + +longlong +Type_handler_decimal_result::Item_func_hybrid_field_type_val_int( + Item_func_hybrid_field_type *item) + const +{ + return item->val_int_from_decimal_op(); +} + + +my_decimal * +Type_handler_decimal_result::Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *item, + my_decimal *dec) const +{ + return item->val_decimal_from_decimal_op(dec); +} + + +bool +Type_handler_decimal_result::Item_func_hybrid_field_type_get_date( + Item_func_hybrid_field_type *item, + MYSQL_TIME *ltime, + ulonglong fuzzydate) const +{ + return item->get_date_from_decimal_op(ltime, fuzzydate); +} + + +/***************************************************************************/ + + +String * +Type_handler_int_result::Item_func_hybrid_field_type_val_str( + Item_func_hybrid_field_type *item, + String *str) const +{ + return item->val_str_from_int_op(str); +} + + +double +Type_handler_int_result::Item_func_hybrid_field_type_val_real( + Item_func_hybrid_field_type *item) + const +{ + return item->val_real_from_int_op(); +} + + +longlong +Type_handler_int_result::Item_func_hybrid_field_type_val_int( + Item_func_hybrid_field_type *item) + const +{ + return item->val_int_from_int_op(); +} + + +my_decimal * +Type_handler_int_result::Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *item, + my_decimal *dec) const +{ + return item->val_decimal_from_int_op(dec); +} + + +bool +Type_handler_int_result::Item_func_hybrid_field_type_get_date( + Item_func_hybrid_field_type *item, + MYSQL_TIME *ltime, + ulonglong fuzzydate) const +{ + return item->get_date_from_int_op(ltime, fuzzydate); +} + + + +/***************************************************************************/ + +String * +Type_handler_real_result::Item_func_hybrid_field_type_val_str( + Item_func_hybrid_field_type *item, + String *str) const +{ + return item->val_str_from_real_op(str); +} + + +double +Type_handler_real_result::Item_func_hybrid_field_type_val_real( + Item_func_hybrid_field_type *item) + const +{ + return item->val_real_from_real_op(); +} + + +longlong +Type_handler_real_result::Item_func_hybrid_field_type_val_int( + Item_func_hybrid_field_type *item) + const +{ + return item->val_int_from_real_op(); +} + + +my_decimal * +Type_handler_real_result::Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *item, + my_decimal *dec) const +{ + return item->val_decimal_from_real_op(dec); +} + + +bool +Type_handler_real_result::Item_func_hybrid_field_type_get_date( + Item_func_hybrid_field_type *item, + MYSQL_TIME *ltime, + ulonglong fuzzydate) const +{ + return item->get_date_from_real_op(ltime, fuzzydate); +} + + +/***************************************************************************/ + +String * +Type_handler_temporal_result::Item_func_hybrid_field_type_val_str( + Item_func_hybrid_field_type *item, + String *str) const +{ + return item->val_str_from_date_op(str); +} + + +double +Type_handler_temporal_result::Item_func_hybrid_field_type_val_real( + Item_func_hybrid_field_type *item) + const +{ + return item->val_real_from_date_op(); +} + + +longlong +Type_handler_temporal_result::Item_func_hybrid_field_type_val_int( + Item_func_hybrid_field_type *item) + const +{ + return item->val_int_from_date_op(); +} + + +my_decimal * +Type_handler_temporal_result::Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *item, + my_decimal *dec) const +{ + return item->val_decimal_from_date_op(dec); +} + + +bool +Type_handler_temporal_result::Item_func_hybrid_field_type_get_date( + Item_func_hybrid_field_type *item, + MYSQL_TIME *ltime, + ulonglong fuzzydate) const +{ + return item->get_date_from_date_op(ltime, fuzzydate); +} + + +/***************************************************************************/ + +String * +Type_handler_string_result::Item_func_hybrid_field_type_val_str( + Item_func_hybrid_field_type *item, + String *str) const +{ + return item->val_str_from_str_op(str); +} + + +double +Type_handler_string_result::Item_func_hybrid_field_type_val_real( + Item_func_hybrid_field_type *item) + const +{ + return item->val_real_from_str_op(); +} + + +longlong +Type_handler_string_result::Item_func_hybrid_field_type_val_int( + Item_func_hybrid_field_type *item) + const +{ + return item->val_int_from_str_op(); +} + + +my_decimal * +Type_handler_string_result::Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *item, + my_decimal *dec) const +{ + return item->val_decimal_from_str_op(dec); +} + + +bool +Type_handler_string_result::Item_func_hybrid_field_type_get_date( + Item_func_hybrid_field_type *item, + MYSQL_TIME *ltime, + ulonglong fuzzydate) const +{ + return item->get_date_from_str_op(ltime, fuzzydate); +} + +/***************************************************************************/ + +bool Type_handler_numeric:: + Item_func_between_fix_length_and_dec(Item_func_between *func) const +{ + return func->fix_length_and_dec_numeric(current_thd); +} + +bool Type_handler_temporal_result:: + Item_func_between_fix_length_and_dec(Item_func_between *func) const +{ + return func->fix_length_and_dec_numeric(current_thd); +} + +bool Type_handler_string_result:: + Item_func_between_fix_length_and_dec(Item_func_between *func) const +{ + return func->fix_length_and_dec_string(current_thd); +} + + +longlong Type_handler_row:: + Item_func_between_val_int(Item_func_between *func) const +{ + DBUG_ASSERT(0); + func->null_value= true; + return 0; +} + +longlong Type_handler_string_result:: + Item_func_between_val_int(Item_func_between *func) const +{ + return func->val_int_cmp_string(); +} + +longlong Type_handler_temporal_result:: + Item_func_between_val_int(Item_func_between *func) const +{ + return func->val_int_cmp_temporal(); +} + +longlong Type_handler_int_result:: + Item_func_between_val_int(Item_func_between *func) const +{ + return func->val_int_cmp_int(); +} + +longlong Type_handler_real_result:: + Item_func_between_val_int(Item_func_between *func) const +{ + return func->val_int_cmp_real(); +} + +longlong Type_handler_decimal_result:: + Item_func_between_val_int(Item_func_between *func) const +{ + return func->val_int_cmp_decimal(); +} + +/***************************************************************************/ + +cmp_item *Type_handler_int_result::make_cmp_item(THD *thd, + CHARSET_INFO *cs) const +{ + return new (thd->mem_root) cmp_item_int; +} + +cmp_item *Type_handler_real_result::make_cmp_item(THD *thd, + CHARSET_INFO *cs) const +{ + return new (thd->mem_root) cmp_item_real; +} + +cmp_item *Type_handler_decimal_result::make_cmp_item(THD *thd, + CHARSET_INFO *cs) const +{ + return new (thd->mem_root) cmp_item_decimal; +} + + +cmp_item *Type_handler_string_result::make_cmp_item(THD *thd, + CHARSET_INFO *cs) const +{ + return new (thd->mem_root) cmp_item_sort_string(cs); +} + +cmp_item *Type_handler_row::make_cmp_item(THD *thd, + CHARSET_INFO *cs) const +{ + return new (thd->mem_root) cmp_item_row; +} + +cmp_item *Type_handler_time_common::make_cmp_item(THD *thd, + CHARSET_INFO *cs) const +{ + return new (thd->mem_root) cmp_item_time; +} + +cmp_item *Type_handler_temporal_with_date::make_cmp_item(THD *thd, + CHARSET_INFO *cs) const +{ + return new (thd->mem_root) cmp_item_datetime; +} + +/***************************************************************************/ + +static int srtcmp_in(CHARSET_INFO *cs, const String *x,const String *y) +{ + return cs->coll->strnncollsp(cs, + (uchar *) x->ptr(),x->length(), + (uchar *) y->ptr(),y->length()); +} + +in_vector *Type_handler_string_result::make_in_vector(THD *thd, + const Item_func_in *func, + uint nargs) const +{ + return new (thd->mem_root) in_string(thd, nargs, (qsort2_cmp) srtcmp_in, + func->compare_collation()); + +} + + +in_vector *Type_handler_int_result::make_in_vector(THD *thd, + const Item_func_in *func, + uint nargs) const +{ + return new (thd->mem_root) in_longlong(thd, nargs); +} + + +in_vector *Type_handler_real_result::make_in_vector(THD *thd, + const Item_func_in *func, + uint nargs) const +{ + return new (thd->mem_root) in_double(thd, nargs); +} + + +in_vector *Type_handler_decimal_result::make_in_vector(THD *thd, + const Item_func_in *func, + uint nargs) const +{ + return new (thd->mem_root) in_decimal(thd, nargs); +} + + +in_vector *Type_handler_time_common::make_in_vector(THD *thd, + const Item_func_in *func, + uint nargs) const +{ + return new (thd->mem_root) in_time(thd, nargs); +} + + +in_vector * +Type_handler_temporal_with_date::make_in_vector(THD *thd, + const Item_func_in *func, + uint nargs) const +{ + return new (thd->mem_root) in_datetime(thd, nargs); +} + + +in_vector *Type_handler_row::make_in_vector(THD *thd, + const Item_func_in *func, + uint nargs) const +{ + return new (thd->mem_root) in_row(thd, nargs, 0); +} + +/***************************************************************************/ + +bool Type_handler_string_result:: + Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *func) const +{ + if (func->agg_all_arg_charsets_for_comparison()) + return true; + if (func->compatible_types_scalar_bisection_possible()) + { + return func->value_list_convert_const_to_int(thd) || + func->fix_for_scalar_comparison_using_bisection(thd); + } + return + func->fix_for_scalar_comparison_using_cmp_items(thd, + 1U << (uint) STRING_RESULT); +} + + +bool Type_handler_int_result:: + Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *func) const +{ + /* + Does not need to call value_list_convert_const_to_int() + as already handled by int handler. + */ + return func->compatible_types_scalar_bisection_possible() ? + func->fix_for_scalar_comparison_using_bisection(thd) : + func->fix_for_scalar_comparison_using_cmp_items(thd, + 1U << (uint) INT_RESULT); +} + + +bool Type_handler_real_result:: + Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *func) const +{ + return func->compatible_types_scalar_bisection_possible() ? + (func->value_list_convert_const_to_int(thd) || + func->fix_for_scalar_comparison_using_bisection(thd)) : + func->fix_for_scalar_comparison_using_cmp_items(thd, + 1U << (uint) REAL_RESULT); +} + + +bool Type_handler_decimal_result:: + Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *func) const +{ + return func->compatible_types_scalar_bisection_possible() ? + (func->value_list_convert_const_to_int(thd) || + func->fix_for_scalar_comparison_using_bisection(thd)) : + func->fix_for_scalar_comparison_using_cmp_items(thd, + 1U << (uint) DECIMAL_RESULT); +} + + +bool Type_handler_temporal_result:: + Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *func) const +{ + return func->compatible_types_scalar_bisection_possible() ? + (func->value_list_convert_const_to_int(thd) || + func->fix_for_scalar_comparison_using_bisection(thd)) : + func->fix_for_scalar_comparison_using_cmp_items(thd, + 1U << (uint) TIME_RESULT); +} + + +bool Type_handler_row::Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *func) const +{ + return func->compatible_types_row_bisection_possible() ? + func->fix_for_row_comparison_using_bisection(thd) : + func->fix_for_row_comparison_using_cmp_items(thd); +} + +/***************************************************************************/ + +String *Type_handler_string_result:: + Item_func_min_max_val_str(Item_func_min_max *func, String *str) const +{ + return func->val_str_native(str); +} + + +String *Type_handler_temporal_result:: + Item_func_min_max_val_str(Item_func_min_max *func, String *str) const +{ + return func->val_string_from_date(str); +} + + +String *Type_handler_int_result:: + Item_func_min_max_val_str(Item_func_min_max *func, String *str) const +{ + return func->val_string_from_int(str); +} + + +String *Type_handler_decimal_result:: + Item_func_min_max_val_str(Item_func_min_max *func, String *str) const +{ + return func->val_string_from_decimal(str); +} + + +String *Type_handler_real_result:: + Item_func_min_max_val_str(Item_func_min_max *func, String *str) const +{ + return func->val_string_from_real(str); +} + + +double Type_handler_string_result:: + Item_func_min_max_val_real(Item_func_min_max *func) const +{ + return func->val_real_native(); +} + + +double Type_handler_temporal_result:: + Item_func_min_max_val_real(Item_func_min_max *func) const +{ + MYSQL_TIME ltime; + if (func->get_date(<ime, 0)) + return 0; + return TIME_to_double(<ime); +} + + +double Type_handler_numeric:: + Item_func_min_max_val_real(Item_func_min_max *func) const +{ + return func->val_real_native(); +} + + +longlong Type_handler_string_result:: + Item_func_min_max_val_int(Item_func_min_max *func) const +{ + return func->val_int_native(); +} + + +longlong Type_handler_temporal_result:: + Item_func_min_max_val_int(Item_func_min_max *func) const +{ + MYSQL_TIME ltime; + if (func->get_date(<ime, 0)) + return 0; + return TIME_to_ulonglong(<ime); +} + + +longlong Type_handler_numeric:: + Item_func_min_max_val_int(Item_func_min_max *func) const +{ + return func->val_int_native(); +} + + +my_decimal *Type_handler_string_result:: + Item_func_min_max_val_decimal(Item_func_min_max *func, + my_decimal *dec) const +{ + return func->val_decimal_native(dec); +} + + +my_decimal *Type_handler_numeric:: + Item_func_min_max_val_decimal(Item_func_min_max *func, + my_decimal *dec) const +{ + return func->val_decimal_native(dec); +} + + +my_decimal *Type_handler_temporal_result:: + Item_func_min_max_val_decimal(Item_func_min_max *func, + my_decimal *dec) const +{ + MYSQL_TIME ltime; + if (func->get_date(<ime, 0)) + return 0; + return date2my_decimal(<ime, dec); +} + + +bool Type_handler_string_result:: + Item_func_min_max_get_date(Item_func_min_max *func, + MYSQL_TIME *ltime, ulonglong fuzzydate) const +{ + /* + just like ::val_int() method of a string item can be called, + for example, SELECT CONCAT("10", "12") + 1, + ::get_date() can be called for non-temporal values, + for example, SELECT MONTH(GREATEST("2011-11-21", "2010-10-09")) + */ + return func->Item::get_date(ltime, fuzzydate); +} + + +bool Type_handler_numeric:: + Item_func_min_max_get_date(Item_func_min_max *func, + MYSQL_TIME *ltime, ulonglong fuzzydate) const +{ + return func->Item::get_date(ltime, fuzzydate); +} + + +bool Type_handler_temporal_result:: + Item_func_min_max_get_date(Item_func_min_max *func, + MYSQL_TIME *ltime, ulonglong fuzzydate) const +{ + return func->get_date_native(ltime, fuzzydate); +} + +/***************************************************************************/ + +/** + Get a string representation of the Item value. + See sql_type.h for details. +*/ +String *Type_handler_row:: + print_item_value(THD *thd, Item *item, String *str) const +{ + CHARSET_INFO *cs= thd->variables.character_set_client; + StringBuffer<STRING_BUFFER_USUAL_SIZE> val(cs); + str->append(C_STRING_WITH_LEN("ROW(")); + for (uint i= 0 ; i < item->cols(); i++) + { + if (i > 0) + str->append(','); + Item *elem= item->element_index(i); + String *tmp= elem->type_handler()->print_item_value(thd, elem, &val); + if (tmp) + str->append(*tmp); + else + str->append(STRING_WITH_LEN("NULL")); + } + str->append(C_STRING_WITH_LEN(")")); + return str; +} + + +/** + Get a string representation of the Item value, + using the character string format with its charset and collation, e.g. + latin1 'string' COLLATE latin1_german2_ci +*/ +String *Type_handler:: + print_item_value_csstr(THD *thd, Item *item, String *str) const +{ + String *result= item->val_str(str); + + if (!result) + return NULL; + + StringBuffer<STRING_BUFFER_USUAL_SIZE> buf(result->charset()); + CHARSET_INFO *cs= thd->variables.character_set_client; + + buf.append('_'); + buf.append(result->charset()->csname); + if (cs->escape_with_backslash_is_dangerous) + buf.append(' '); + append_query_string(cs, &buf, result->ptr(), result->length(), + thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES); + buf.append(" COLLATE '"); + buf.append(item->collation.collation->name); + buf.append('\''); + str->copy(buf); + + return str; +} + + +String *Type_handler_numeric:: + print_item_value(THD *thd, Item *item, String *str) const +{ + return item->val_str(str); +} + + +String *Type_handler:: + print_item_value_temporal(THD *thd, Item *item, String *str, + const Name &type_name, String *buf) const +{ + String *result= item->val_str(buf); + return !result || + str->realloc(type_name.length() + result->length() + 2) || + str->copy(type_name.ptr(), type_name.length(), &my_charset_latin1) || + str->append('\'') || + str->append(result->ptr(), result->length()) || + str->append('\'') ? + NULL : + str; +} + + +String *Type_handler_time_common:: + print_item_value(THD *thd, Item *item, String *str) const +{ + StringBuffer<MAX_TIME_FULL_WIDTH+1> buf; + return print_item_value_temporal(thd, item, str, + Name(C_STRING_WITH_LEN("TIME")), &buf); +} + + +String *Type_handler_date_common:: + print_item_value(THD *thd, Item *item, String *str) const +{ + StringBuffer<MAX_DATE_WIDTH+1> buf; + return print_item_value_temporal(thd, item, str, + Name(C_STRING_WITH_LEN("DATE")), &buf); +} + + +String *Type_handler_datetime_common:: + print_item_value(THD *thd, Item *item, String *str) const +{ + StringBuffer<MAX_DATETIME_FULL_WIDTH+1> buf; + return print_item_value_temporal(thd, item, str, + Name(C_STRING_WITH_LEN("TIMESTAMP")), &buf); +} + + +String *Type_handler_timestamp_common:: + print_item_value(THD *thd, Item *item, String *str) const +{ + StringBuffer<MAX_DATETIME_FULL_WIDTH+1> buf; + return print_item_value_temporal(thd, item, str, + Name(C_STRING_WITH_LEN("TIMESTAMP")), &buf); +} + + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + DBUG_ASSERT(0); + return false; +} + + +bool Type_handler_int_result:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + item->fix_arg_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + item->fix_arg_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + item->fix_arg_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + item->fix_arg_double(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + item->fix_arg_double(); + return false; +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Item_func_round_fix_length_and_dec(Item_func_round *item) const +{ + return Item_func_or_sum_illegal_param(item); +} +#endif + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const +{ + DBUG_ASSERT(0); + return false; +} + + +bool Type_handler_int_result:: + Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const +{ + item->fix_length_and_dec_int_or_decimal(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const +{ + item->fix_length_and_dec_int_or_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const +{ + item->fix_length_and_dec_int_or_decimal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Item_func_int_val_fix_length_and_dec(Item_func_int_val *item) const +{ + return Item_func_or_sum_illegal_param(item); +} +#endif + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_abs_fix_length_and_dec(Item_func_abs *item) const +{ + DBUG_ASSERT(0); + return false; +} + + +bool Type_handler_int_result:: + Item_func_abs_fix_length_and_dec(Item_func_abs *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_abs_fix_length_and_dec(Item_func_abs *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_abs_fix_length_and_dec(Item_func_abs *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_abs_fix_length_and_dec(Item_func_abs *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_abs_fix_length_and_dec(Item_func_abs *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Item_func_abs_fix_length_and_dec(Item_func_abs *item) const +{ + return Item_func_or_sum_illegal_param(item); +} +#endif + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_neg_fix_length_and_dec(Item_func_neg *item) const +{ + DBUG_ASSERT(0); + return false; +} + + +bool Type_handler_int_result:: + Item_func_neg_fix_length_and_dec(Item_func_neg *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_neg_fix_length_and_dec(Item_func_neg *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_neg_fix_length_and_dec(Item_func_neg *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_neg_fix_length_and_dec(Item_func_neg *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_neg_fix_length_and_dec(Item_func_neg *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Item_func_neg_fix_length_and_dec(Item_func_neg *item) const +{ + return Item_func_or_sum_illegal_param(item); +} +#endif + + +/***************************************************************************/ + +bool Type_handler:: + Item_func_signed_fix_length_and_dec(Item_func_signed *item) const +{ + item->fix_length_and_dec_generic(); + return false; +} + + +bool Type_handler:: + Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const +{ + const Item *arg= item->arguments()[0]; + if (!arg->unsigned_flag && arg->val_int_min() < 0) + { + /* + Negative arguments produce long results: + CAST(1-2 AS UNSIGNED) -> 18446744073709551615 + */ + item->max_length= MAX_BIGINT_WIDTH; + return false; + } + item->fix_length_and_dec_generic(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_signed_fix_length_and_dec(Item_func_signed *item) const +{ + item->fix_length_and_dec_string(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const +{ + const Item *arg= item->arguments()[0]; + if (!arg->unsigned_flag && // Not HEX hybrid + arg->max_char_length() > 1) // Can be negative + { + // String arguments can give long results: '-1' -> 18446744073709551614 + item->max_length= MAX_BIGINT_WIDTH; + return false; + } + item->fix_length_and_dec_string(); + return false; +} + +bool Type_handler_real_result:: + Item_func_signed_fix_length_and_dec(Item_func_signed *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler:: + Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) const +{ + item->fix_length_and_dec_generic(); + return false; +} + + +bool Type_handler:: + Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) const +{ + item->fix_length_and_dec_generic(); + return false; +} + + +bool Type_handler:: + Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const +{ + item->fix_length_and_dec_str(); + return false; +} + + +bool Type_handler_numeric:: + Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const +{ + item->fix_length_and_dec_numeric(); + return false; +} + + +bool Type_handler:: + Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const +{ + uint dec= item->decimals == NOT_FIXED_DEC ? + item->arguments()[0]->time_precision() : + item->decimals; + item->fix_attributes_temporal(MIN_TIME_WIDTH, dec); + item->maybe_null= true; + return false; +} + + +bool Type_handler:: + Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const +{ + item->fix_attributes_temporal(MAX_DATE_WIDTH, 0); + item->maybe_null= true; + return false; +} + + +bool Type_handler:: + Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item) + const +{ + uint dec= item->decimals == NOT_FIXED_DEC ? + item->arguments()[0]->datetime_precision() : + item->decimals; + item->fix_attributes_temporal(MAX_DATETIME_WIDTH, dec); + item->maybe_null= true; + return false; +} + + +#ifdef HAVE_SPATIAL + +bool Type_handler_geometry:: + Item_func_signed_fix_length_and_dec(Item_func_signed *item) const +{ + return Item_func_or_sum_illegal_param(item); +} + + +bool Type_handler_geometry:: + Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const +{ + return Item_func_or_sum_illegal_param(item); +} + + +bool Type_handler_geometry:: + Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) const +{ + return Item_func_or_sum_illegal_param(item); +} + + +bool Type_handler_geometry:: + Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) const +{ + return Item_func_or_sum_illegal_param(item); +} + + +bool Type_handler_geometry:: + Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const +{ + if (item->cast_charset() != &my_charset_bin) + return Item_func_or_sum_illegal_param(item); // CAST(geom AS CHAR) + item->fix_length_and_dec_str(); + return false; // CAST(geom AS BINARY) +} + + +bool Type_handler_geometry:: + Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const +{ + return Item_func_or_sum_illegal_param(item); +} + + + +bool Type_handler_geometry:: + Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const +{ + return Item_func_or_sum_illegal_param(item); +} + + +bool Type_handler_geometry:: + Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item) + const +{ + return Item_func_or_sum_illegal_param(item); + +} + +#endif /* HAVE_SPATIAL */ + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_plus_fix_length_and_dec(Item_func_plus *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_minus_fix_length_and_dec(Item_func_minus *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_mul_fix_length_and_dec(Item_func_mul *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_div_fix_length_and_dec(Item_func_div *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ + +bool Type_handler_row:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + DBUG_ASSERT(0); + return true; +} + + +bool Type_handler_int_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_int(); + return false; +} + + +bool Type_handler_real_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + + +bool Type_handler_decimal_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_decimal(); + return false; +} + + +bool Type_handler_temporal_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_temporal(); + return false; +} + + +bool Type_handler_string_result:: + Item_func_mod_fix_length_and_dec(Item_func_mod *item) const +{ + item->fix_length_and_dec_double(); + return false; +} + +/***************************************************************************/ + +uint Type_handler::Item_time_precision(Item *item) const +{ + return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); +} + + +uint Type_handler::Item_datetime_precision(Item *item) const +{ + return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); +} + + +uint Type_handler_string_result::Item_temporal_precision(Item *item, + bool is_time) const +{ + MYSQL_TIME ltime; + StringBuffer<64> buf; + String *tmp; + MYSQL_TIME_STATUS status; + DBUG_ASSERT(item->fixed); + if ((tmp= item->val_str(&buf)) && + !(is_time ? + str_to_time(tmp->charset(), tmp->ptr(), tmp->length(), + <ime, TIME_TIME_ONLY, &status) : + str_to_datetime(tmp->charset(), tmp->ptr(), tmp->length(), + <ime, TIME_FUZZY_DATES, &status))) + return MY_MIN(status.precision, TIME_SECOND_PART_DIGITS); + return MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); +} + +/***************************************************************************/ + +uint Type_handler::Item_decimal_scale(const Item *item) const +{ + return item->decimals < NOT_FIXED_DEC ? + item->decimals : + MY_MIN(item->max_length, DECIMAL_MAX_SCALE); +} + +uint Type_handler_temporal_result:: + Item_decimal_scale_with_seconds(const Item *item) const +{ + return item->decimals < NOT_FIXED_DEC ? + item->decimals : + TIME_SECOND_PART_DIGITS; +} + +uint Type_handler::Item_divisor_precision_increment(const Item *item) const +{ + return item->decimals; +} + +uint Type_handler_temporal_result:: + Item_divisor_precision_increment_with_seconds(const Item *item) const +{ + return item->decimals < NOT_FIXED_DEC ? + item->decimals : + TIME_SECOND_PART_DIGITS; +} + +/***************************************************************************/ + +uint Type_handler_string_result::Item_decimal_precision(const Item *item) const +{ + uint res= item->max_char_length(); + /* + Return at least one decimal digit, even if Item::max_char_length() + returned 0. This is important to avoid attempts to create fields of types + INT(0) or DECIMAL(0,0) when converting NULL or empty strings to INT/DECIMAL: + CREATE TABLE t1 AS SELECT CONVERT(NULL,SIGNED) AS a; + */ + return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1; +} + +uint Type_handler_real_result::Item_decimal_precision(const Item *item) const +{ + uint res= item->max_char_length(); + return res ? MY_MIN(res, DECIMAL_MAX_PRECISION) : 1; +} + +uint Type_handler_decimal_result::Item_decimal_precision(const Item *item) const +{ + uint prec= my_decimal_length_to_precision(item->max_char_length(), + item->decimals, + item->unsigned_flag); + return MY_MIN(prec, DECIMAL_MAX_PRECISION); +} + +uint Type_handler_int_result::Item_decimal_precision(const Item *item) const +{ + uint prec= my_decimal_length_to_precision(item->max_char_length(), + item->decimals, + item->unsigned_flag); + return MY_MIN(prec, DECIMAL_MAX_PRECISION); +} + +uint Type_handler_time_common::Item_decimal_precision(const Item *item) const +{ + return 7 + MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); +} + +uint Type_handler_date_common::Item_decimal_precision(const Item *item) const +{ + return 8; +} + +uint Type_handler_datetime_common::Item_decimal_precision(const Item *item) const +{ + return 14 + MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); +} + +uint Type_handler_timestamp_common::Item_decimal_precision(const Item *item) const +{ + return 14 + MY_MIN(item->decimals, TIME_SECOND_PART_DIGITS); +} + +/***************************************************************************/ + +bool Type_handler_real_result:: + subquery_type_allows_materialization(const Item *inner, + const Item *outer) const +{ + DBUG_ASSERT(inner->cmp_type() == REAL_RESULT); + return outer->cmp_type() == REAL_RESULT; +} + + +bool Type_handler_int_result:: + subquery_type_allows_materialization(const Item *inner, + const Item *outer) const +{ + DBUG_ASSERT(inner->cmp_type() == INT_RESULT); + return outer->cmp_type() == INT_RESULT; +} + + +bool Type_handler_decimal_result:: + subquery_type_allows_materialization(const Item *inner, + const Item *outer) const +{ + DBUG_ASSERT(inner->cmp_type() == DECIMAL_RESULT); + return outer->cmp_type() == DECIMAL_RESULT; +} + + +bool Type_handler_string_result:: + subquery_type_allows_materialization(const Item *inner, + const Item *outer) const +{ + DBUG_ASSERT(inner->cmp_type() == STRING_RESULT); + return outer->cmp_type() == STRING_RESULT && + outer->collation.collation == inner->collation.collation && + /* + Materialization also is unable to work when create_tmp_table() will + create a blob column because item->max_length is too big. + The following test is copied from varstring_type_handler(). + */ + !inner->too_big_for_varchar(); +} + + +bool Type_handler_temporal_result:: + subquery_type_allows_materialization(const Item *inner, + const Item *outer) const +{ + DBUG_ASSERT(inner->cmp_type() == TIME_RESULT); + return mysql_timestamp_type() == + outer->type_handler()->mysql_timestamp_type(); +} + +/***************************************************************************/ + + +const Type_handler * +Type_handler_null::type_handler_for_tmp_table(const Item *item) const +{ + return &type_handler_string; +} + + +const Type_handler * +Type_handler_null::type_handler_for_union(const Item *item) const +{ + return &type_handler_string; +} + + +const Type_handler * +Type_handler_olddecimal::type_handler_for_tmp_table(const Item *item) const +{ + return &type_handler_newdecimal; +} + +const Type_handler * +Type_handler_olddecimal::type_handler_for_union(const Item *item) const +{ + return &type_handler_newdecimal; +} + + +/***************************************************************************/ + +bool Type_handler::check_null(const Item *item, st_value *value) const +{ + if (item->null_value) + { + value->m_type= DYN_COL_NULL; + return true; + } + return false; +} + + +bool Type_handler_null:: + Item_save_in_value(Item *item, st_value *value) const +{ + value->m_type= DYN_COL_NULL; + return true; +} + + +bool Type_handler_row:: + Item_save_in_value(Item *item, st_value *value) const +{ + DBUG_ASSERT(0); + value->m_type= DYN_COL_NULL; + return true; +} + + +bool Type_handler_int_result:: + Item_save_in_value(Item *item, st_value *value) const +{ + value->m_type= item->unsigned_flag ? DYN_COL_UINT : DYN_COL_INT; + value->value.m_longlong= item->val_int(); + return check_null(item, value); +} + + +bool Type_handler_real_result:: + Item_save_in_value(Item *item, st_value *value) const +{ + value->m_type= DYN_COL_DOUBLE; + value->value.m_double= item->val_real(); + return check_null(item, value); +} + + +bool Type_handler_decimal_result:: + Item_save_in_value(Item *item, st_value *value) const +{ + value->m_type= DYN_COL_DECIMAL; + my_decimal *dec= item->val_decimal(&value->m_decimal); + if (dec != &value->m_decimal && !item->null_value) + my_decimal2decimal(dec, &value->m_decimal); + return check_null(item, value); +} + + +bool Type_handler_string_result:: + Item_save_in_value(Item *item, st_value *value) const +{ + value->m_type= DYN_COL_STRING; + String *str= item->val_str(&value->m_string); + if (str != &value->m_string && !item->null_value) + value->m_string.set(str->ptr(), str->length(), str->charset()); + return check_null(item, value); +} + + +bool Type_handler_temporal_with_date:: + Item_save_in_value(Item *item, st_value *value) const +{ + value->m_type= DYN_COL_DATETIME; + item->get_date(&value->value.m_time, sql_mode_for_dates(current_thd)); + return check_null(item, value); +} + + +bool Type_handler_time_common:: + Item_save_in_value(Item *item, st_value *value) const +{ + value->m_type= DYN_COL_DATETIME; + item->get_time(&value->value.m_time); + return check_null(item, value); +} + +/***************************************************************************/ + +bool Type_handler_row:: + Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *val) const +{ + DBUG_ASSERT(0); + param->set_null(); + return true; +} + + +bool Type_handler_real_result:: + Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *val) const +{ + param->unsigned_flag= attr->unsigned_flag; + param->set_double(val->value.m_double); + param->set_handler(&type_handler_double); + return false; +} + + +bool Type_handler_int_result:: + Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *val) const +{ + param->unsigned_flag= attr->unsigned_flag; + param->set_int(val->value.m_longlong, MY_INT64_NUM_DECIMAL_DIGITS); + param->set_handler(&type_handler_longlong); + return false; +} + + +bool Type_handler_decimal_result:: + Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *val) const +{ + param->unsigned_flag= attr->unsigned_flag; + param->set_decimal(&val->m_decimal, attr->unsigned_flag); + param->set_handler(&type_handler_newdecimal); + return false; +} + + +bool Type_handler_string_result:: + Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *val) const +{ + param->unsigned_flag= false; + param->value.cs_info.set(thd, attr->collation.collation); + /* + Exact value of max_length is not known unless data is converted to + charset of connection, so we have to set it later. + */ + param->set_handler(&type_handler_varchar); + return param->set_str(val->m_string.ptr(), val->m_string.length()); +} + + +bool Type_handler_temporal_result:: + Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *val) const +{ + param->unsigned_flag= attr->unsigned_flag; + param->set_time(&val->value.m_time, attr->max_length, attr->decimals); + param->set_handler(this); + return false; +} + + +#ifdef HAVE_SPATIAL +bool Type_handler_geometry:: + Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *val) const +{ + param->unsigned_flag= false; + param->value.cs_info.set(thd, &my_charset_bin); + param->set_handler(&type_handler_geometry); + param->set_geometry_type(attr->uint_geometry_type()); + return param->set_str(val->m_string.ptr(), val->m_string.length()); +} +#endif + +/***************************************************************************/ + +bool Type_handler_null:: + Item_send(Item *item, Protocol *protocol, st_value *buf) const +{ + return protocol->store_null(); +} + + +bool Type_handler:: + Item_send_str(Item *item, Protocol *protocol, st_value *buf) const +{ + String *res; + if ((res= item->val_str(&buf->m_string))) + { + DBUG_ASSERT(!item->null_value); + return protocol->store(res->ptr(), res->length(), res->charset()); + } + DBUG_ASSERT(item->null_value); + return protocol->store_null(); +} + + +bool Type_handler:: + Item_send_tiny(Item *item, Protocol *protocol, st_value *buf) const +{ + longlong nr= item->val_int(); + if (!item->null_value) + return protocol->store_tiny(nr); + return protocol->store_null(); +} + + +bool Type_handler:: + Item_send_short(Item *item, Protocol *protocol, st_value *buf) const +{ + longlong nr= item->val_int(); + if (!item->null_value) + return protocol->store_short(nr); + return protocol->store_null(); +} + + +bool Type_handler:: + Item_send_long(Item *item, Protocol *protocol, st_value *buf) const +{ + longlong nr= item->val_int(); + if (!item->null_value) + return protocol->store_long(nr); + return protocol->store_null(); +} + +bool Type_handler:: + Item_send_longlong(Item *item, Protocol *protocol, st_value *buf) const +{ + longlong nr= item->val_int(); + if (!item->null_value) + return protocol->store_longlong(nr, item->unsigned_flag); + return protocol->store_null(); +} + + +bool Type_handler:: + Item_send_float(Item *item, Protocol *protocol, st_value *buf) const +{ + float nr= (float) item->val_real(); + if (!item->null_value) + return protocol->store(nr, item->decimals, &buf->m_string); + return protocol->store_null(); +} + + +bool Type_handler:: + Item_send_double(Item *item, Protocol *protocol, st_value *buf) const +{ + double nr= item->val_real(); + if (!item->null_value) + return protocol->store(nr, item->decimals, &buf->m_string); + return protocol->store_null(); +} + + +bool Type_handler:: + Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const +{ + item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd)); + if (!item->null_value) + return protocol->store(&buf->value.m_time, item->decimals); + return protocol->store_null(); +} + + +bool Type_handler:: + Item_send_date(Item *item, Protocol *protocol, st_value *buf) const +{ + item->get_date(&buf->value.m_time, sql_mode_for_dates(current_thd)); + if (!item->null_value) + return protocol->store_date(&buf->value.m_time); + return protocol->store_null(); +} + + +bool Type_handler:: + Item_send_time(Item *item, Protocol *protocol, st_value *buf) const +{ + item->get_time(&buf->value.m_time); + if (!item->null_value) + return protocol->store_time(&buf->value.m_time, item->decimals); + return protocol->store_null(); +} + +/***************************************************************************/ + +Item *Type_handler_int_result:: + make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const +{ + longlong result= item->val_int(); + if (item->null_value) + return new (thd->mem_root) Item_null(thd, item->name.str); + return new (thd->mem_root) Item_int(thd, item->name.str, result, + item->max_length); +} + + +Item *Type_handler_real_result:: + make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const +{ + double result= item->val_real(); + if (item->null_value) + return new (thd->mem_root) Item_null(thd, item->name.str); + return new (thd->mem_root) Item_float(thd, item->name.str, result, + item->decimals, item->max_length); +} + + +Item *Type_handler_decimal_result:: + make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const +{ + my_decimal decimal_value; + my_decimal *result= item->val_decimal(&decimal_value); + if (item->null_value) + return new (thd->mem_root) Item_null(thd, item->name.str); + return new (thd->mem_root) Item_decimal(thd, item->name.str, result, + item->max_length, item->decimals); +} + + +Item *Type_handler_string_result:: + make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const +{ + StringBuffer<MAX_FIELD_WIDTH> tmp; + String *result= item->val_str(&tmp); + if (item->null_value) + return new (thd->mem_root) Item_null(thd, item->name.str); + uint length= result->length(); + char *tmp_str= thd->strmake(result->ptr(), length); + return new (thd->mem_root) Item_string(thd, item->name.str, + tmp_str, length, result->charset()); +} + + +Item *Type_handler_time_common:: + make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const +{ + Item_cache_temporal *cache; + longlong value= item->val_time_packed(); + if (item->null_value) + return new (thd->mem_root) Item_null(thd, item->name.str); + cache= new (thd->mem_root) Item_cache_temporal(thd, this); + if (cache) + cache->store_packed(value, item); + return cache; +} + + +Item *Type_handler_temporal_with_date:: + make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const +{ + Item_cache_temporal *cache; + longlong value= item->val_datetime_packed(); + if (item->null_value) + return new (thd->mem_root) Item_null(thd, item->name.str); + cache= new (thd->mem_root) Item_cache_temporal(thd, this); + if (cache) + cache->store_packed(value, item); + return cache; +} + + +Item *Type_handler_row:: + make_const_item_for_comparison(THD *thd, Item *item, const Item *cmp) const +{ + if (item->type() == Item::ROW_ITEM && cmp->type() == Item::ROW_ITEM) + { + /* + Substitute constants only in Item_row's. Don't affect other Items + with ROW_RESULT (eg Item_singlerow_subselect). + + For such Items more optimal is to detect if it is constant and replace + it with Item_row. This would optimize queries like this: + SELECT * FROM t1 WHERE (a,b) = (SELECT a,b FROM t2 LIMIT 1); + */ + Item_row *item_row= (Item_row*) item; + Item_row *comp_item_row= (Item_row*) cmp; + uint col; + /* + If item and comp_item are both Item_row's and have same number of cols + then process items in Item_row one by one. + We can't ignore NULL values here as this item may be used with <=>, in + which case NULL's are significant. + */ + DBUG_ASSERT(item->result_type() == cmp->result_type()); + DBUG_ASSERT(item_row->cols() == comp_item_row->cols()); + col= item_row->cols(); + while (col-- > 0) + resolve_const_item(thd, item_row->addr(col), + comp_item_row->element_index(col)); + } + return NULL; +} + +/***************************************************************************/ + +static const char* item_name(Item *a, String *str) +{ + if (a->name.str) + return a->name.str; + str->length(0); + a->print(str, QT_ORDINARY); + return str->c_ptr_safe(); +} + + +static void wrong_precision_error(uint errcode, Item *a, + ulonglong number, uint maximum) +{ + StringBuffer<1024> buf(system_charset_info); + my_error(errcode, MYF(0), number, item_name(a, &buf), maximum); +} + + +/** + Get precision and scale for a declaration + + return + 0 ok + 1 error +*/ + +bool get_length_and_scale(ulonglong length, ulonglong decimals, + uint *out_length, uint *out_decimals, + uint max_precision, uint max_scale, + Item *a) +{ + if (length > (ulonglong) max_precision) + { + wrong_precision_error(ER_TOO_BIG_PRECISION, a, length, max_precision); + return 1; + } + if (decimals > (ulonglong) max_scale) + { + wrong_precision_error(ER_TOO_BIG_SCALE, a, decimals, max_scale); + return 1; + } + + *out_decimals= (uint) decimals; + my_decimal_trim(&length, out_decimals); + *out_length= (uint) length; + + if (*out_length < *out_decimals) + { + my_error(ER_M_BIGGER_THAN_D, MYF(0), ""); + return 1; + } + return 0; +} + + +Item *Type_handler_longlong:: + create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const +{ + if (this != &type_handler_ulonglong) + return new (thd->mem_root) Item_func_signed(thd, item); + return new (thd->mem_root) Item_func_unsigned(thd, item); + +} + + +Item *Type_handler_date_common:: + create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const +{ + return new (thd->mem_root) Item_date_typecast(thd, item); +} + + + +Item *Type_handler_time_common:: + create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const +{ + if (attr.decimals() > MAX_DATETIME_PRECISION) + { + wrong_precision_error(ER_TOO_BIG_PRECISION, item, attr.decimals(), + MAX_DATETIME_PRECISION); + return 0; + } + return new (thd->mem_root) + Item_time_typecast(thd, item, (uint) attr.decimals()); +} + + +Item *Type_handler_datetime_common:: + create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const +{ + if (attr.decimals() > MAX_DATETIME_PRECISION) + { + wrong_precision_error(ER_TOO_BIG_PRECISION, item, attr.decimals(), + MAX_DATETIME_PRECISION); + return 0; + } + return new (thd->mem_root) + Item_datetime_typecast(thd, item, (uint) attr.decimals()); + +} + + +Item *Type_handler_decimal_result:: + create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const +{ + uint len, dec; + if (get_length_and_scale(attr.length(), attr.decimals(), &len, &dec, + DECIMAL_MAX_PRECISION, DECIMAL_MAX_SCALE, item)) + return NULL; + return new (thd->mem_root) Item_decimal_typecast(thd, item, len, dec); +} + + +Item *Type_handler_double:: + create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const +{ + uint len, dec; + if (!attr.length_specified()) + return new (thd->mem_root) Item_double_typecast(thd, item, + DBL_DIG + 7, + NOT_FIXED_DEC); + + if (get_length_and_scale(attr.length(), attr.decimals(), &len, &dec, + DECIMAL_MAX_PRECISION, NOT_FIXED_DEC - 1, item)) + return NULL; + return new (thd->mem_root) Item_double_typecast(thd, item, len, dec); +} + + +Item *Type_handler_long_blob:: + create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const +{ + int len= -1; + CHARSET_INFO *real_cs= attr.charset() ? + attr.charset() : + thd->variables.collation_connection; + if (attr.length_specified()) + { + if (attr.length() > MAX_FIELD_BLOBLENGTH) + { + char buff[1024]; + String buf(buff, sizeof(buff), system_charset_info); + my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), item_name(item, &buf), + MAX_FIELD_BLOBLENGTH); + return NULL; + } + len= (int) attr.length(); + } + return new (thd->mem_root) Item_char_typecast(thd, item, len, real_cs); +} + +/***************************************************************************/ diff --git a/sql/sql_type.h b/sql/sql_type.h index 596c338720e..910172134fd 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -22,35 +22,705 @@ #include "mysqld.h" +#include "sql_array.h" +#include "sql_const.h" +#include "my_time.h" class Field; +class Column_definition; class Item; -class Type_std_attributes; +class Item_param; +class Item_cache; +class Item_func_or_sum; +class Item_sum_hybrid; +class Item_sum_sum; +class Item_sum_avg; +class Item_sum_variance; +class Item_func_hex; +class Item_hybrid_func; +class Item_func_min_max; +class Item_func_hybrid_field_type; +class Item_bool_func2; +class Item_func_between; +class Item_func_in; +class Item_func_round; +class Item_func_int_val; +class Item_func_abs; +class Item_func_neg; +class Item_func_signed; +class Item_func_unsigned; +class Item_double_typecast; +class Item_decimal_typecast; +class Item_char_typecast; +class Item_time_typecast; +class Item_date_typecast; +class Item_datetime_typecast; +class Item_func_plus; +class Item_func_minus; +class Item_func_mul; +class Item_func_div; +class Item_func_mod; +class cmp_item; +class in_vector; +class Type_handler_hybrid_field_type; class Sort_param; +class Arg_comparator; +class Spvar_definition; +struct st_value; +class Protocol; +class handler; +struct Schema_specification_st; struct TABLE; struct SORT_FIELD_ATTR; + +/* + Flags for collation aggregation modes, used in TDCollation::agg(): + + MY_COLL_ALLOW_SUPERSET_CONV - allow conversion to a superset + MY_COLL_ALLOW_COERCIBLE_CONV - allow conversion of a coercible value + (i.e. constant). + MY_COLL_ALLOW_CONV - allow any kind of conversion + (combination of the above two) + MY_COLL_ALLOW_NUMERIC_CONV - if all items were numbers, convert to + @@character_set_connection + MY_COLL_DISALLOW_NONE - don't allow return DERIVATION_NONE + (e.g. when aggregating for comparison) + MY_COLL_CMP_CONV - combination of MY_COLL_ALLOW_CONV + and MY_COLL_DISALLOW_NONE +*/ + +#define MY_COLL_ALLOW_SUPERSET_CONV 1 +#define MY_COLL_ALLOW_COERCIBLE_CONV 2 +#define MY_COLL_DISALLOW_NONE 4 +#define MY_COLL_ALLOW_NUMERIC_CONV 8 + +#define MY_COLL_ALLOW_CONV (MY_COLL_ALLOW_SUPERSET_CONV | MY_COLL_ALLOW_COERCIBLE_CONV) +#define MY_COLL_CMP_CONV (MY_COLL_ALLOW_CONV | MY_COLL_DISALLOW_NONE) + + +#define my_charset_numeric my_charset_latin1 +#define MY_REPERTOIRE_NUMERIC MY_REPERTOIRE_ASCII + + +enum Derivation +{ + DERIVATION_IGNORABLE= 6, + DERIVATION_NUMERIC= 5, + DERIVATION_COERCIBLE= 4, + DERIVATION_SYSCONST= 3, + DERIVATION_IMPLICIT= 2, + DERIVATION_NONE= 1, + DERIVATION_EXPLICIT= 0 +}; + + +/** + "Declared Type Collation" + A combination of collation and its derivation. +*/ + +class DTCollation { +public: + CHARSET_INFO *collation; + enum Derivation derivation; + uint repertoire; + + void set_repertoire_from_charset(CHARSET_INFO *cs) + { + repertoire= cs->state & MY_CS_PUREASCII ? + MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; + } + DTCollation() + { + collation= &my_charset_bin; + derivation= DERIVATION_NONE; + repertoire= MY_REPERTOIRE_UNICODE30; + } + DTCollation(CHARSET_INFO *collation_arg) + { + /* + This constructor version is used in combination with Field constructors, + to pass "CHARSET_INFO" instead of the full DTCollation. + Therefore, derivation is set to DERIVATION_IMPLICIT, which is the + proper derivation for table fields. + We should eventually remove all code pieces that pass "CHARSET_INFO" + (e.g. in storage engine sources) and fix to pass the full DTCollation + instead. Then, this constructor can be removed. + */ + collation= collation_arg; + derivation= DERIVATION_IMPLICIT; + repertoire= my_charset_repertoire(collation_arg); + } + DTCollation(CHARSET_INFO *collation_arg, Derivation derivation_arg) + { + collation= collation_arg; + derivation= derivation_arg; + set_repertoire_from_charset(collation_arg); + } + DTCollation(CHARSET_INFO *collation_arg, + Derivation derivation_arg, + uint repertoire_arg) + :collation(collation_arg), + derivation(derivation_arg), + repertoire(repertoire_arg) + { } + void set(const DTCollation &dt) + { + collation= dt.collation; + derivation= dt.derivation; + repertoire= dt.repertoire; + } + void set(CHARSET_INFO *collation_arg, Derivation derivation_arg) + { + collation= collation_arg; + derivation= derivation_arg; + set_repertoire_from_charset(collation_arg); + } + void set(CHARSET_INFO *collation_arg, + Derivation derivation_arg, + uint repertoire_arg) + { + collation= collation_arg; + derivation= derivation_arg; + repertoire= repertoire_arg; + } + void set_numeric() + { + collation= &my_charset_numeric; + derivation= DERIVATION_NUMERIC; + repertoire= MY_REPERTOIRE_NUMERIC; + } + void set(CHARSET_INFO *collation_arg) + { + collation= collation_arg; + set_repertoire_from_charset(collation_arg); + } + void set(Derivation derivation_arg) + { derivation= derivation_arg; } + bool aggregate(const DTCollation &dt, uint flags= 0); + bool set(DTCollation &dt1, DTCollation &dt2, uint flags= 0) + { set(dt1); return aggregate(dt2, flags); } + const char *derivation_name() const + { + switch(derivation) + { + case DERIVATION_NUMERIC: return "NUMERIC"; + case DERIVATION_IGNORABLE: return "IGNORABLE"; + case DERIVATION_COERCIBLE: return "COERCIBLE"; + case DERIVATION_IMPLICIT: return "IMPLICIT"; + case DERIVATION_SYSCONST: return "SYSCONST"; + case DERIVATION_EXPLICIT: return "EXPLICIT"; + case DERIVATION_NONE: return "NONE"; + default: return "UNKNOWN"; + } + } + int sortcmp(const String *s, const String *t) const + { + return collation->coll->strnncollsp(collation, + (uchar *) s->ptr(), s->length(), + (uchar *) t->ptr(), t->length()); + } +}; + + +static inline uint32 +char_to_byte_length_safe(size_t char_length_arg, uint32 mbmaxlen_arg) +{ + ulonglong tmp= ((ulonglong) char_length_arg) * mbmaxlen_arg; + return tmp > UINT_MAX32 ? (uint32) UINT_MAX32 : static_cast<uint32>(tmp); +} + +/** + A class to store type attributes for the standard data types. + Does not include attributes for the extended data types + such as ENUM, SET, GEOMETRY. +*/ +class Type_std_attributes +{ +public: + DTCollation collation; + uint decimals; + /* + The maximum value length in characters multiplied by collation->mbmaxlen. + Almost always it's the maximum value length in bytes. + */ + uint32 max_length; + bool unsigned_flag; + Type_std_attributes() + :collation(&my_charset_bin, DERIVATION_COERCIBLE), + decimals(0), max_length(0), unsigned_flag(false) + { } + Type_std_attributes(const Type_std_attributes *other) + :collation(other->collation), + decimals(other->decimals), + max_length(other->max_length), + unsigned_flag(other->unsigned_flag) + { } + Type_std_attributes(uint32 max_length_arg, uint decimals_arg, + bool unsigned_flag_arg, const DTCollation &dtc) + :collation(dtc), + decimals(decimals_arg), + max_length(max_length_arg), + unsigned_flag(unsigned_flag_arg) + { } + void set(const Type_std_attributes *other) + { + *this= *other; + } + void set(const Type_std_attributes &other) + { + *this= other; + } + void set(const Field *field); + uint32 max_char_length() const + { return max_length / collation.collation->mbmaxlen; } + void fix_length_and_charset(uint32 max_char_length_arg, CHARSET_INFO *cs) + { + max_length= char_to_byte_length_safe(max_char_length_arg, cs->mbmaxlen); + collation.collation= cs; + } + void fix_char_length(uint32 max_char_length_arg) + { + max_length= char_to_byte_length_safe(max_char_length_arg, + collation.collation->mbmaxlen); + } + void fix_char_length_temporal_not_fixed_dec(uint int_part_length, uint dec) + { + uint char_length= int_part_length; + if ((decimals= dec)) + { + if (decimals == NOT_FIXED_DEC) + char_length+= TIME_SECOND_PART_DIGITS + 1; + else + { + set_if_smaller(decimals, TIME_SECOND_PART_DIGITS); + char_length+= decimals + 1; + } + } + fix_char_length(char_length); + } + void fix_attributes_temporal_not_fixed_dec(uint int_part_length, uint dec) + { + collation.set_numeric(); + unsigned_flag= 0; + fix_char_length_temporal_not_fixed_dec(int_part_length, dec); + } + void fix_attributes_time_not_fixed_dec(uint dec) + { + fix_attributes_temporal_not_fixed_dec(MIN_TIME_WIDTH, dec); + } + void fix_attributes_datetime_not_fixed_dec(uint dec) + { + fix_attributes_temporal_not_fixed_dec(MAX_DATETIME_WIDTH, dec); + } + void fix_attributes_temporal(uint int_part_length, uint dec) + { + collation.set_numeric(); + unsigned_flag= 0; + decimals= MY_MIN(dec, TIME_SECOND_PART_DIGITS); + max_length= decimals + int_part_length + (dec ? 1 : 0); + } + void fix_attributes_date() + { + fix_attributes_temporal(MAX_DATE_WIDTH, 0); + } + void fix_attributes_time(uint dec) + { + fix_attributes_temporal(MIN_TIME_WIDTH, dec); + } + void fix_attributes_datetime(uint dec) + { + fix_attributes_temporal(MAX_DATETIME_WIDTH, dec); + } + + void count_only_length(Item **item, uint nitems); + void count_octet_length(Item **item, uint nitems); + void count_real_length(Item **item, uint nitems); + void count_decimal_length(Item **item, uint nitems); + bool count_string_length(const char *func_name, Item **item, uint nitems); + uint count_max_decimals(Item **item, uint nitems); + + void aggregate_attributes_int(Item **items, uint nitems) + { + collation.set_numeric(); + count_only_length(items, nitems); + decimals= 0; + } + void aggregate_attributes_real(Item **items, uint nitems) + { + collation.set_numeric(); + count_real_length(items, nitems); + } + void aggregate_attributes_decimal(Item **items, uint nitems) + { + collation.set_numeric(); + count_decimal_length(items, nitems); + } + bool aggregate_attributes_string(const char *func_name, + Item **item, uint nitems) + { + return count_string_length(func_name, item, nitems); + } + void aggregate_attributes_temporal(uint int_part_length, + Item **item, uint nitems) + { + fix_attributes_temporal(int_part_length, count_max_decimals(item, nitems)); + } + + bool agg_item_collations(DTCollation &c, const char *name, + Item **items, uint nitems, + uint flags, int item_sep); + bool agg_item_set_converter(const DTCollation &coll, const char *fname, + Item **args, uint nargs, + uint flags, int item_sep); + + /* + Collect arguments' character sets together. + We allow to apply automatic character set conversion in some cases. + The conditions when conversion is possible are: + - arguments A and B have different charsets + - A wins according to coercibility rules + (i.e. a column is stronger than a string constant, + an explicit COLLATE clause is stronger than a column) + - character set of A is either superset for character set of B, + or B is a string constant which can be converted into the + character set of A without data loss. + + If all of the above is true, then it's possible to convert + B into the character set of A, and then compare according + to the collation of A. + + For functions with more than two arguments: + + collect(A,B,C) ::= collect(collect(A,B),C) + + Since this function calls THD::change_item_tree() on the passed Item ** + pointers, it is necessary to pass the original Item **'s, not copies. + Otherwise their values will not be properly restored (see BUG#20769). + If the items are not consecutive (eg. args[2] and args[5]), use the + item_sep argument, ie. + + agg_item_charsets(coll, fname, &args[2], 2, flags, 3) + */ + bool agg_arg_charsets(DTCollation &c, const char *func_name, + Item **items, uint nitems, + uint flags, int item_sep) + { + if (agg_item_collations(c, func_name, items, nitems, flags, item_sep)) + return true; + return agg_item_set_converter(c, func_name, items, nitems, flags, item_sep); + } + /* + Aggregate arguments for string result, e.g: CONCAT(a,b) + - convert to @@character_set_connection if all arguments are numbers + - allow DERIVATION_NONE + */ + bool agg_arg_charsets_for_string_result(DTCollation &c, const char *func_name, + Item **items, uint nitems, + int item_sep) + { + uint flags= MY_COLL_ALLOW_SUPERSET_CONV | + MY_COLL_ALLOW_COERCIBLE_CONV | + MY_COLL_ALLOW_NUMERIC_CONV; + return agg_arg_charsets(c, func_name, items, nitems, flags, item_sep); + } + /* + Aggregate arguments for string result, when some comparison + is involved internally, e.g: REPLACE(a,b,c) + - convert to @@character_set_connection if all arguments are numbers + - disallow DERIVATION_NONE + */ + bool agg_arg_charsets_for_string_result_with_comparison(DTCollation &c, + const char *func_name, + Item **items, + uint nitems, + int item_sep) + { + uint flags= MY_COLL_ALLOW_SUPERSET_CONV | + MY_COLL_ALLOW_COERCIBLE_CONV | + MY_COLL_ALLOW_NUMERIC_CONV | + MY_COLL_DISALLOW_NONE; + return agg_arg_charsets(c, func_name, items, nitems, flags, item_sep); + } + + /* + Aggregate arguments for comparison, e.g: a=b, a LIKE b, a RLIKE b + - don't convert to @@character_set_connection if all arguments are numbers + - don't allow DERIVATION_NONE + */ + bool agg_arg_charsets_for_comparison(DTCollation &c, + const char *func_name, + Item **items, uint nitems, + int item_sep) + { + uint flags= MY_COLL_ALLOW_SUPERSET_CONV | + MY_COLL_ALLOW_COERCIBLE_CONV | + MY_COLL_DISALLOW_NONE; + return agg_arg_charsets(c, func_name, items, nitems, flags, item_sep); + } + +}; + + +class Type_all_attributes: public Type_std_attributes +{ +public: + Type_all_attributes() + :Type_std_attributes() + { } + Type_all_attributes(const Type_all_attributes *other) + :Type_std_attributes(other) + { } + virtual ~Type_all_attributes() {} + virtual void set_maybe_null(bool maybe_null_arg)= 0; + // Returns total number of decimal digits + virtual uint decimal_precision() const= 0; + /* + Field::geometry_type is not visible here. + Let's use an "uint" wrapper for now. Later when we move Field_geom + into a plugin, this method will be replaced to some generic + datatype indepented method. + */ + virtual uint uint_geometry_type() const= 0; + virtual void set_geometry_type(uint type)= 0; + virtual TYPELIB *get_typelib() const= 0; + virtual void set_typelib(TYPELIB *typelib)= 0; +}; + + +class Type_cast_attributes +{ + CHARSET_INFO *m_charset; + ulonglong m_length; + ulonglong m_decimals; + bool m_length_specified; + bool m_decimals_specified; +public: + Type_cast_attributes(const char *c_len, const char *c_dec, CHARSET_INFO *cs) + :m_charset(cs), m_length(0), m_decimals(0), + m_length_specified(false), m_decimals_specified(false) + { + set_length_and_dec(c_len, c_dec); + } + Type_cast_attributes(CHARSET_INFO *cs) + :m_charset(cs), m_length(0), m_decimals(0), + m_length_specified(false), m_decimals_specified(false) + { } + void set_length_and_dec(const char *c_len, const char *c_dec) + { + int error; + /* + We don't have to check for error here as sql_yacc.yy has guaranteed + that the values are in range of ulonglong + */ + if ((m_length_specified= (c_len != NULL))) + m_length= (ulonglong) my_strtoll10(c_len, NULL, &error); + if ((m_decimals_specified= (c_dec != NULL))) + m_decimals= (ulonglong) my_strtoll10(c_dec, NULL, &error); + } + CHARSET_INFO *charset() const { return m_charset; } + bool length_specified() const { return m_length_specified; } + bool decimals_specified() const { return m_decimals_specified; } + ulonglong length() const { return m_length; } + ulonglong decimals() const { return m_decimals; } +}; + + +class Name: private LEX_CSTRING +{ +public: + Name(const char *str_arg, uint length_arg) + { + DBUG_ASSERT(length_arg < UINT_MAX32); + LEX_CSTRING::str= str_arg; + LEX_CSTRING::length= length_arg; + } + const char *ptr() const { return LEX_CSTRING::str; } + uint length() const { return (uint) LEX_CSTRING::length; } +}; + + +class Record_addr +{ +public: + uchar *ptr; // Position to field in record + /** + Byte where the @c NULL bit is stored inside a record. If this Field is a + @c NOT @c NULL field, this member is @c NULL. + */ + uchar *null_ptr; + uchar null_bit; // Bit used to test null bit + Record_addr(uchar *ptr_arg, + uchar *null_ptr_arg, + uchar null_bit_arg) + :ptr(ptr_arg), + null_ptr(null_ptr_arg), + null_bit(null_bit_arg) + { } + Record_addr(bool maybe_null) + :ptr(NULL), + null_ptr(maybe_null ? (uchar*) "" : 0), + null_bit(0) + { } +}; + + class Type_handler { protected: - const Type_handler *string_type_handler(uint max_octet_length) const; + String *print_item_value_csstr(THD *thd, Item *item, String *str) const; + String *print_item_value_temporal(THD *thd, Item *item, String *str, + const Name &type_name, String *buf) const; void make_sort_key_longlong(uchar *to, bool maybe_null, bool null_value, bool unsigned_flag, longlong value) const; + bool + Item_func_or_sum_illegal_param(const char *name) const; + bool + Item_func_or_sum_illegal_param(const Item_func_or_sum *) const; + bool check_null(const Item *item, st_value *value) const; + bool Item_send_str(Item *item, Protocol *protocol, st_value *buf) const; + bool Item_send_tiny(Item *item, Protocol *protocol, st_value *buf) const; + bool Item_send_short(Item *item, Protocol *protocol, st_value *buf) const; + bool Item_send_long(Item *item, Protocol *protocol, st_value *buf) const; + bool Item_send_longlong(Item *item, Protocol *protocol, st_value *buf) const; + bool Item_send_float(Item *item, Protocol *protocol, st_value *buf) const; + bool Item_send_double(Item *item, Protocol *protocol, st_value *buf) const; + bool Item_send_time(Item *item, Protocol *protocol, st_value *buf) const; + bool Item_send_date(Item *item, Protocol *protocol, st_value *buf) const; + bool Item_send_datetime(Item *item, Protocol *protocol, st_value *buf) const; + bool Column_definition_prepare_stage2_legacy(Column_definition *c, + enum_field_types type) + const; + bool Column_definition_prepare_stage2_legacy_num(Column_definition *c, + enum_field_types type) + const; + bool Column_definition_prepare_stage2_legacy_real(Column_definition *c, + enum_field_types type) + const; public: + static const Type_handler *blob_type_handler(uint max_octet_length); + static const Type_handler *string_type_handler(uint max_octet_length); + static const Type_handler *bit_and_int_mixture_handler(uint max_char_len); + static const Type_handler *type_handler_long_or_longlong(uint max_char_len); + /** + Return a string type handler for Item + If too_big_for_varchar() returns a BLOB variant, according to length. + If max_length > 0 create a VARCHAR(n) + If max_length == 0 create a CHAR(0) + @param item - the Item to get the handler to. + */ + static const Type_handler *varstring_type_handler(const Item *item); + static const Type_handler *blob_type_handler(const Item *item); static const Type_handler *get_handler_by_field_type(enum_field_types type); static const Type_handler *get_handler_by_real_type(enum_field_types type); + static const Type_handler *get_handler_by_cmp_type(Item_result type); + static const Type_handler *get_handler_by_result_type(Item_result type) + { + /* + As result_type() returns STRING_RESULT for temporal Items, + type should never be equal to TIME_RESULT here. + */ + DBUG_ASSERT(type != TIME_RESULT); + return get_handler_by_cmp_type(type); + } + static const + Type_handler *aggregate_for_result_traditional(const Type_handler *h1, + const Type_handler *h2); + static const + Type_handler *aggregate_for_num_op_traditional(const Type_handler *h1, + const Type_handler *h2); + + virtual const Name name() const= 0; virtual enum_field_types field_type() const= 0; virtual enum_field_types real_field_type() const { return field_type(); } virtual Item_result result_type() const= 0; virtual Item_result cmp_type() const= 0; + virtual enum_mysql_timestamp_type mysql_timestamp_type() const + { + return MYSQL_TIMESTAMP_ERROR; + } + virtual bool is_timestamp_type() const + { + return false; + } + /** + Check whether a field type can be partially indexed by a key. + @param type field type + @retval true Type can have a prefixed key + @retval false Type can not have a prefixed key + */ + virtual bool type_can_have_key_part() const + { + return false; + } + virtual bool type_can_have_auto_increment_attribute() const + { + return false; + } + /** + Prepared statement long data: + Check whether this parameter data type is compatible with long data. + Used to detect whether a long data stream has been supplied to a + incompatible data type. + */ + virtual bool is_param_long_data_type() const { return false; } + virtual const Type_handler *type_handler_for_comparison() const= 0; + virtual const Type_handler *type_handler_for_item_field() const + { + return this; + } + virtual const Type_handler *type_handler_for_tmp_table(const Item *) const + { + return this; + } + virtual const Type_handler *type_handler_for_union(const Item *) const + { + return this; + } + virtual const Type_handler *cast_to_int_type_handler() const + { + return this; + } + virtual CHARSET_INFO *charset_for_protocol(const Item *item) const; virtual const Type_handler* type_handler_adjusted_to_max_octet_length(uint max_octet_length, CHARSET_INFO *cs) const { return this; } + virtual bool adjust_spparam_type(Spvar_definition *def, Item *from) const + { + return false; + } virtual ~Type_handler() {} /** + Determines MariaDB traditional data types that always present + in the server. + */ + virtual bool is_traditional_type() const + { + return true; + } + virtual bool is_scalar_type() const { return true; } + virtual bool can_return_int() const { return true; } + virtual bool can_return_decimal() const { return true; } + virtual bool can_return_real() const { return true; } + virtual bool can_return_str() const { return true; } + virtual bool can_return_text() const { return true; } + virtual bool can_return_date() const { return true; } + virtual bool can_return_time() const { return true; } + virtual bool is_general_purpose_string_type() const { return false; } + virtual uint Item_time_precision(Item *item) const; + virtual uint Item_datetime_precision(Item *item) const; + virtual uint Item_decimal_scale(const Item *item) const; + virtual uint Item_decimal_precision(const Item *item) const= 0; + /* + Returns how many digits a divisor adds into a division result. + See Item::divisor_precision_increment() in item.h for more comments. + */ + virtual uint Item_divisor_precision_increment(const Item *) const; + /** Makes a temporary table Field to handle numeric aggregate functions, e.g. SUM(DISTINCT expr), AVG(DISTINCT expr), etc. */ @@ -86,63 +756,814 @@ public: virtual Field *make_conversion_table_field(TABLE *TABLE, uint metadata, const Field *target) const= 0; + virtual bool Column_definition_fix_attributes(Column_definition *c) const= 0; + virtual bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags) const; + /* + This method is called on queries like: + CREATE TABLE t2 (a INT) AS SELECT a FROM t1; + I.e. column "a" is queried from another table, + but its data type is redefined. + @param OUT def - The column definition to be redefined + @param IN dup - The column definition to take the data type from + (i.e. "a INT" in the above example). + @param IN file - Table owner handler. If it does not support certain + data types, some conversion can be applied. + I.g. true BIT to BIT-AS-CHAR. + @param IN schema - the owner schema definition, e.g. for the default + character set and collation. + @retval true - on error + @retval false - on success + */ + virtual bool Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st * + schema) + const; + virtual bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const= 0; + virtual Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const= 0; + Field *make_and_init_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; virtual void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, Sort_param *param) const= 0; virtual void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const= 0; + + virtual uint32 max_display_length(const Item *item) const= 0; + virtual uint32 calc_pack_length(uint32 length) const= 0; + virtual bool Item_save_in_value(Item *item, st_value *value) const= 0; + virtual bool Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *value) const= 0; + virtual bool Item_send(Item *item, Protocol *p, st_value *buf) const= 0; + virtual int Item_save_in_field(Item *item, Field *field, + bool no_conversions) const= 0; + + /** + Return a string representation of the Item value. + + @param thd thread handle + @param str string buffer for representation of the value + + @note + If the item has a string result type, the string is escaped + according to its character set. + + @retval + NULL on error + @retval + non-NULL a pointer to a a valid string on success + */ + virtual String *print_item_value(THD *thd, Item *item, String *str) const= 0; + + /** + Check if + WHERE expr=value AND expr=const + can be rewritten as: + WHERE const=value AND expr=const + + "this" is the comparison handler that is used by "target". + + @param target - the predicate expr=value, + whose "expr" argument will be replaced to "const". + @param target_expr - the target's "expr" which will be replaced to "const". + @param target_value - the target's second argument, it will remain unchanged. + @param source - the equality predicate expr=const (or expr<=>const) + that can be used to rewrite the "target" part + (under certain conditions, see the code). + @param source_expr - the source's "expr". It should be exactly equal to + the target's "expr" to make condition rewrite possible. + @param source_const - the source's "const" argument, it will be inserted + into "target" instead of "expr". + */ + virtual bool + can_change_cond_ref_to_const(Item_bool_func2 *target, + Item *target_expr, Item *target_value, + Item_bool_func2 *source, + Item *source_expr, Item *source_const) const= 0; + virtual bool + subquery_type_allows_materialization(const Item *inner, + const Item *outer) const= 0; + /** + Make a simple constant replacement item for a constant "src", + so the new item can futher be used for comparison with "cmp", e.g.: + src = cmp -> replacement = cmp + + "this" is the type handler that is used to compare "src" and "cmp". + + @param thd - current thread, for mem_root + @param src - The item that we want to replace. It's a const item, + but it can be complex enough to calculate on every row. + @param cmp - The src's comparand. + @retval - a pointer to the created replacement Item + @retval - NULL, if could not create a replacement (e.g. on EOM). + NULL is also returned for ROWs, because instead of replacing + a Item_row to a new Item_row, Type_handler_row just replaces + its elements. + */ + virtual Item *make_const_item_for_comparison(THD *thd, + Item *src, + const Item *cmp) const= 0; + virtual Item_cache *Item_get_cache(THD *thd, const Item *item) const= 0; + virtual Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const + { + DBUG_ASSERT(0); + return NULL; + } + virtual bool set_comparator_func(Arg_comparator *cmp) const= 0; + virtual bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, + uint nitems) const= 0; + virtual bool Item_func_min_max_fix_attributes(THD *thd, + Item_func_min_max *func, + Item **items, + uint nitems) const; + virtual bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *) const= 0; + virtual bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const= 0; + virtual bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const= 0; + virtual + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const= 0; + + virtual bool Item_val_bool(Item *item) const= 0; + virtual longlong Item_val_int_signed_typecast(Item *item) const= 0; + virtual longlong Item_val_int_unsigned_typecast(Item *item) const= 0; + + virtual String *Item_func_hex_val_str_ascii(Item_func_hex *item, + String *str) const= 0; + + virtual + String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *, + String *) const= 0; + virtual + double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) + const= 0; + virtual + longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) + const= 0; + virtual + my_decimal *Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *, + my_decimal *) const= 0; + virtual + bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + MYSQL_TIME *, + ulonglong fuzzydate) const= 0; + virtual + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const= 0; + virtual + double Item_func_min_max_val_real(Item_func_min_max *) const= 0; + virtual + longlong Item_func_min_max_val_int(Item_func_min_max *) const= 0; + virtual + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const= 0; + virtual + bool Item_func_min_max_get_date(Item_func_min_max*, + MYSQL_TIME *, ulonglong fuzzydate) const= 0; + virtual bool + Item_func_between_fix_length_and_dec(Item_func_between *func) const= 0; + virtual longlong + Item_func_between_val_int(Item_func_between *func) const= 0; + + virtual cmp_item * + make_cmp_item(THD *thd, CHARSET_INFO *cs) const= 0; + + virtual in_vector * + make_in_vector(THD *thd, const Item_func_in *func, uint nargs) const= 0; + + virtual bool + Item_func_in_fix_comparator_compatible_types(THD *thd, Item_func_in *) + const= 0; + + virtual bool + Item_func_round_fix_length_and_dec(Item_func_round *round) const= 0; + + virtual bool + Item_func_int_val_fix_length_and_dec(Item_func_int_val *func) const= 0; + + virtual bool + Item_func_abs_fix_length_and_dec(Item_func_abs *func) const= 0; + + virtual bool + Item_func_neg_fix_length_and_dec(Item_func_neg *func) const= 0; + + virtual bool + Item_func_signed_fix_length_and_dec(Item_func_signed *item) const; + virtual bool + Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const; + virtual bool + Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) const; + virtual bool + Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) const; + virtual bool + Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const; + virtual bool + Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const; + virtual bool + Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const; + virtual bool + Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item) const; + + virtual bool + Item_func_plus_fix_length_and_dec(Item_func_plus *func) const= 0; + virtual bool + Item_func_minus_fix_length_and_dec(Item_func_minus *func) const= 0; + virtual bool + Item_func_mul_fix_length_and_dec(Item_func_mul *func) const= 0; + virtual bool + Item_func_div_fix_length_and_dec(Item_func_div *func) const= 0; + virtual bool + Item_func_mod_fix_length_and_dec(Item_func_mod *func) const= 0; +}; + + +/* + Special handler for ROW +*/ +class Type_handler_row: public Type_handler +{ + static const Name m_name_row; +public: + virtual ~Type_handler_row() {} + const Name name() const { return m_name_row; } + bool is_scalar_type() const { return false; } + bool can_return_int() const { return false; } + bool can_return_decimal() const { return false; } + bool can_return_real() const { return false; } + bool can_return_str() const { return false; } + bool can_return_text() const { return false; } + bool can_return_date() const { return false; } + bool can_return_time() const { return false; } + enum_field_types field_type() const + { + DBUG_ASSERT(0); + return MYSQL_TYPE_NULL; + }; + Item_result result_type() const + { + return ROW_RESULT; + } + Item_result cmp_type() const + { + return ROW_RESULT; + } + const Type_handler *type_handler_for_comparison() const; + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer) const + { + DBUG_ASSERT(0); + return false; + } + Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const + { + DBUG_ASSERT(0); + return NULL; + } + Field *make_conversion_table_field(TABLE *TABLE, + uint metadata, + const Field *target) const + { + DBUG_ASSERT(0); + return NULL; + } + bool Column_definition_fix_attributes(Column_definition *c) const + { + DBUG_ASSERT(0); + return true; + } + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags) const + { + DBUG_ASSERT(0); + return true; + } + bool Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const + { + DBUG_ASSERT(0); + return true; + } + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { + DBUG_ASSERT(0); + return true; + } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const + { + DBUG_ASSERT(0); + return NULL; + } + void make_sort_key(uchar *to, Item *item, + const SORT_FIELD_ATTR *sort_field, + Sort_param *param) const + { + DBUG_ASSERT(0); + } + void sortlength(THD *thd, const Type_std_attributes *item, + SORT_FIELD_ATTR *attr) const + { + DBUG_ASSERT(0); + } + uint32 max_display_length(const Item *item) const + { + DBUG_ASSERT(0); + return 0; + } + uint32 calc_pack_length(uint32 length) const + { + DBUG_ASSERT(0); + return 0; + } + uint Item_decimal_precision(const Item *item) const + { + DBUG_ASSERT(0); + return DECIMAL_MAX_PRECISION; + } + bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *value) const; + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + DBUG_ASSERT(0); + return true; + } + int Item_save_in_field(Item *item, Field *field, bool no_conversions) const + { + DBUG_ASSERT(0); + return 1; + } + String *print_item_value(THD *thd, Item *item, String *str) const; + bool can_change_cond_ref_to_const(Item_bool_func2 *target, + Item *target_expr, Item *target_value, + Item_bool_func2 *source, + Item *source_expr, Item *source_const) const + { + DBUG_ASSERT(0); + return false; + } + Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; + Item_cache *Item_get_cache(THD *thd, const Item *item) const; + bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_val_bool(Item *item) const + { + DBUG_ASSERT(0); + return false; + } + longlong Item_val_int_signed_typecast(Item *item) const + { + DBUG_ASSERT(0); + return 0; + } + longlong Item_val_int_unsigned_typecast(Item *item) const + { + DBUG_ASSERT(0); + return 0; + } + String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const + { + DBUG_ASSERT(0); + return NULL; + } + String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *, + String *) const + { + DBUG_ASSERT(0); + return NULL; + } + double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) + const + { + DBUG_ASSERT(0); + return 0.0; + } + longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) + const + { + DBUG_ASSERT(0); + return 0; + } + my_decimal *Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *, + my_decimal *) const + { + DBUG_ASSERT(0); + return NULL; + } + bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + MYSQL_TIME *, + ulonglong fuzzydate) const + { + DBUG_ASSERT(0); + return true; + } + + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const + { + DBUG_ASSERT(0); + return NULL; + } + double Item_func_min_max_val_real(Item_func_min_max *) const + { + DBUG_ASSERT(0); + return 0; + } + longlong Item_func_min_max_val_int(Item_func_min_max *) const + { + DBUG_ASSERT(0); + return 0; + } + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const + { + DBUG_ASSERT(0); + return NULL; + } + bool Item_func_min_max_get_date(Item_func_min_max*, + MYSQL_TIME *, ulonglong fuzzydate) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_func_between_fix_length_and_dec(Item_func_between *func) const + { + DBUG_ASSERT(0); + return true; + } + longlong Item_func_between_val_int(Item_func_between *func) const; + cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; + in_vector *make_in_vector(THD *thd, const Item_func_in *f, uint nargs) const; + bool Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *) const; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const; + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; + bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; + bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + + bool Item_func_signed_fix_length_and_dec(Item_func_signed *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *) const + { + DBUG_ASSERT(0); + return true; + } + bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *) const + { + DBUG_ASSERT(0); + return true; + } + + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; +}; + + +/* + A common parent class for numeric data type handlers +*/ +class Type_handler_numeric: public Type_handler +{ +protected: + bool Item_sum_hybrid_fix_length_and_dec_numeric(Item_sum_hybrid *func, + const Type_handler *handler) + const; +public: + String *print_item_value(THD *thd, Item *item, String *str) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; + bool Item_func_min_max_get_date(Item_func_min_max*, + MYSQL_TIME *, ulonglong fuzzydate) const; + virtual ~Type_handler_numeric() { } + bool can_change_cond_ref_to_const(Item_bool_func2 *target, + Item *target_expr, Item *target_value, + Item_bool_func2 *source, + Item *source_expr, Item *source_const) const; + bool Item_func_between_fix_length_and_dec(Item_func_between *func) const; + bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const; }; /*** Abstract classes for every XXX_RESULT */ -class Type_handler_real_result: public Type_handler +class Type_handler_real_result: public Type_handler_numeric { public: Item_result result_type() const { return REAL_RESULT; } Item_result cmp_type() const { return REAL_RESULT; } virtual ~Type_handler_real_result() {} + const Type_handler *type_handler_for_comparison() const; + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer) const; void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, Sort_param *param) const; void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + uint Item_decimal_precision(const Item *item) const; + bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *value) const; + int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; + Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; + Item_cache *Item_get_cache(THD *thd, const Item *item) const; + bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; + bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; + bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const; + bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const; + bool Item_val_bool(Item *item) const; + longlong Item_val_int_signed_typecast(Item *item) const; + longlong Item_val_int_unsigned_typecast(Item *item) const; + String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; + String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *, + String *) const; + double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) + const; + longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) + const; + my_decimal *Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *, + my_decimal *) const; + bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + MYSQL_TIME *, + ulonglong fuzzydate) const; + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + longlong Item_func_between_val_int(Item_func_between *func) const; + cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; + in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const; + bool Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *) const; + + bool Item_func_round_fix_length_and_dec(Item_func_round *) const; + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; + bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; + bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; }; -class Type_handler_decimal_result: public Type_handler +class Type_handler_decimal_result: public Type_handler_numeric { public: Item_result result_type() const { return DECIMAL_RESULT; } Item_result cmp_type() const { return DECIMAL_RESULT; } virtual ~Type_handler_decimal_result() {}; + const Type_handler *type_handler_for_comparison() const; + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer) const; Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const; void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, Sort_param *param) const; void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + uint32 max_display_length(const Item *item) const; + Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const; + uint Item_decimal_precision(const Item *item) const; + bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *value) const; + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_str(item, protocol, buf); + } + int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; + Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; + Item_cache *Item_get_cache(THD *thd, const Item *item) const; + bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; + bool Item_val_bool(Item *item) const; + longlong Item_val_int_signed_typecast(Item *item) const; + longlong Item_val_int_unsigned_typecast(Item *item) const; + String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; + String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *, + String *) const; + double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) + const; + longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) + const; + my_decimal *Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *, + my_decimal *) const; + bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + MYSQL_TIME *, + ulonglong fuzzydate) const; + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + longlong Item_func_between_val_int(Item_func_between *func) const; + cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; + in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const; + bool Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *) const; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const; + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; + bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; + bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; }; -class Type_handler_int_result: public Type_handler +class Type_handler_int_result: public Type_handler_numeric { public: Item_result result_type() const { return INT_RESULT; } Item_result cmp_type() const { return INT_RESULT; } virtual ~Type_handler_int_result() {} + const Type_handler *type_handler_for_comparison() const; + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer) const; Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const; void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, Sort_param *param) const; void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + uint Item_decimal_precision(const Item *item) const; + bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *value) const; + int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; + Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; + Item_cache *Item_get_cache(THD *thd, const Item *item) const; + bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; + bool Item_val_bool(Item *item) const; + longlong Item_val_int_signed_typecast(Item *item) const; + longlong Item_val_int_unsigned_typecast(Item *item) const; + String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; + String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *, + String *) const; + double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) + const; + longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) + const; + my_decimal *Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *, + my_decimal *) const; + bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + MYSQL_TIME *, + ulonglong fuzzydate) const; + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + longlong Item_func_between_val_int(Item_func_between *func) const; + cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; + in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const; + bool Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *) const; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const; + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; + bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; + bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; +}; + + +class Type_handler_general_purpose_int: public Type_handler_int_result +{ +public: + bool type_can_have_auto_increment_attribute() const { return true; } }; class Type_handler_temporal_result: public Type_handler { +protected: + uint Item_decimal_scale_with_seconds(const Item *item) const; + uint Item_divisor_precision_increment_with_seconds(const Item *) const; public: Item_result result_type() const { return STRING_RESULT; } Item_result cmp_type() const { return TIME_RESULT; } @@ -152,15 +1573,70 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + bool Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *value) const; + uint32 max_display_length(const Item *item) const; + bool can_change_cond_ref_to_const(Item_bool_func2 *target, + Item *target_expr, Item *target_value, + Item_bool_func2 *source, + Item *source_expr, Item *source_const) const; + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer) const; + Item_cache *Item_get_cache(THD *thd, const Item *item) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; + bool Item_val_bool(Item *item) const; + longlong Item_val_int_signed_typecast(Item *item) const; + longlong Item_val_int_unsigned_typecast(Item *item) const; + String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; + String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *, + String *) const; + double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) + const; + longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) + const; + my_decimal *Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *, + my_decimal *) const; + bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + MYSQL_TIME *, + ulonglong fuzzydate) const; + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; + bool Item_func_min_max_get_date(Item_func_min_max*, + MYSQL_TIME *, ulonglong fuzzydate) const; + bool Item_func_between_fix_length_and_dec(Item_func_between *func) const; + longlong Item_func_between_val_int(Item_func_between *func) const; + bool Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *) const; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const; + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; + bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; + bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; }; class Type_handler_string_result: public Type_handler { + uint Item_temporal_precision(Item *item, bool is_time) const; public: Item_result result_type() const { return STRING_RESULT; } Item_result cmp_type() const { return STRING_RESULT; } + CHARSET_INFO *charset_for_protocol(const Item *item) const; virtual ~Type_handler_string_result() {} + const Type_handler *type_handler_for_comparison() const; const Type_handler * type_handler_adjusted_to_max_octet_length(uint max_octet_length, CHARSET_INFO *cs) const; @@ -169,6 +1645,105 @@ public: void sortlength(THD *thd, const Type_std_attributes *item, SORT_FIELD_ATTR *attr) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags) const; + bool Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const; + uint32 max_display_length(const Item *item) const; + uint Item_time_precision(Item *item) const + { + return Item_temporal_precision(item, true); + } + uint Item_datetime_precision(Item *item) const + { + return Item_temporal_precision(item, false); + } + uint Item_decimal_precision(const Item *item) const; + bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *value) const; + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_str(item, protocol, buf); + } + int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; + String *print_item_value(THD *thd, Item *item, String *str) const + { + return print_item_value_csstr(thd, item, str); + } + bool can_change_cond_ref_to_const(Item_bool_func2 *target, + Item *target_expr, Item *target_value, + Item_bool_func2 *source, + Item *source_expr, Item *source_const) const; + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer) const; + Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; + Item_cache *Item_get_cache(THD *thd, const Item *item) const; + bool set_comparator_func(Arg_comparator *cmp) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const; + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; + bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const; + bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const; + bool Item_val_bool(Item *item) const; + longlong Item_val_int_signed_typecast(Item *item) const; + longlong Item_val_int_unsigned_typecast(Item *item) const; + String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) const; + String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *, + String *) const; + double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) + const; + longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) + const; + my_decimal *Item_func_hybrid_field_type_val_decimal( + Item_func_hybrid_field_type *, + my_decimal *) const; + bool Item_func_hybrid_field_type_get_date(Item_func_hybrid_field_type *, + MYSQL_TIME *, + ulonglong fuzzydate) const; + String *Item_func_min_max_val_str(Item_func_min_max *, String *) const; + double Item_func_min_max_val_real(Item_func_min_max *) const; + longlong Item_func_min_max_val_int(Item_func_min_max *) const; + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *) const; + bool Item_func_min_max_get_date(Item_func_min_max*, + MYSQL_TIME *, ulonglong fuzzydate) const; + bool Item_func_between_fix_length_and_dec(Item_func_between *func) const; + longlong Item_func_between_val_int(Item_func_between *func) const; + cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; + in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const; + bool Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *) const; + bool Item_func_round_fix_length_and_dec(Item_func_round *) const; + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; + bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; + bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_func_plus_fix_length_and_dec(Item_func_plus *) const; + bool Item_func_minus_fix_length_and_dec(Item_func_minus *) const; + bool Item_func_mul_fix_length_and_dec(Item_func_mul *) const; + bool Item_func_div_fix_length_and_dec(Item_func_div *) const; + bool Item_func_mod_fix_length_and_dec(Item_func_mod *) const; +}; + + +class Type_handler_general_purpose_string: public Type_handler_string_result +{ +public: + bool is_general_purpose_string_type() const { return true; } }; @@ -192,301 +1767,1022 @@ public: */ -class Type_handler_tiny: public Type_handler_int_result +class Type_handler_tiny: public Type_handler_general_purpose_int { + static const Name m_name_tiny; public: virtual ~Type_handler_tiny() {} + const Name name() const { return m_name_tiny; } enum_field_types field_type() const { return MYSQL_TYPE_TINY; } + uint32 max_display_length(const Item *item) const { return 4; } + uint32 calc_pack_length(uint32 length) const { return 1; } + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_tiny(item, protocol, buf); + } Field *make_conversion_table_field(TABLE *TABLE, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_TINY); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_short: public Type_handler_int_result +class Type_handler_short: public Type_handler_general_purpose_int { + static const Name m_name_short; public: virtual ~Type_handler_short() {} + const Name name() const { return m_name_short; } enum_field_types field_type() const { return MYSQL_TYPE_SHORT; } + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_short(item, protocol, buf); + } + uint32 max_display_length(const Item *item) const { return 6; } + uint32 calc_pack_length(uint32 length) const { return 2; } Field *make_conversion_table_field(TABLE *TABLE, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_SHORT); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_long: public Type_handler_int_result +class Type_handler_long: public Type_handler_general_purpose_int { + static const Name m_name_int; public: virtual ~Type_handler_long() {} + const Name name() const { return m_name_int; } enum_field_types field_type() const { return MYSQL_TYPE_LONG; } + uint32 max_display_length(const Item *item) const + { + return MY_INT32_NUM_DECIMAL_DIGITS; + } + uint32 calc_pack_length(uint32 length) const { return 4; } + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_long(item, protocol, buf); + } Field *make_conversion_table_field(TABLE *TABLE, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_LONG); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_longlong: public Type_handler_int_result +class Type_handler_longlong: public Type_handler_general_purpose_int { + static const Name m_name_longlong; public: virtual ~Type_handler_longlong() {} + const Name name() const { return m_name_longlong; } enum_field_types field_type() const { return MYSQL_TYPE_LONGLONG; } + uint32 max_display_length(const Item *item) const { return 20; } + uint32 calc_pack_length(uint32 length) const { return 8; } + Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const; + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_longlong(item, protocol, buf); + } Field *make_conversion_table_field(TABLE *TABLE, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { + return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_LONGLONG); + } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_int24: public Type_handler_int_result +class Type_handler_int24: public Type_handler_general_purpose_int { + static const Name m_name_mediumint; public: virtual ~Type_handler_int24() {} + const Name name() const { return m_name_mediumint; } enum_field_types field_type() const { return MYSQL_TYPE_INT24; } + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_long(item, protocol, buf); + } + uint32 max_display_length(const Item *item) const { return 8; } + uint32 calc_pack_length(uint32 length) const { return 3; } Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_INT24); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; class Type_handler_year: public Type_handler_int_result { + static const Name m_name_year; public: virtual ~Type_handler_year() {} + const Name name() const { return m_name_year; } enum_field_types field_type() const { return MYSQL_TYPE_YEAR; } + uint32 max_display_length(const Item *item) const; + uint32 calc_pack_length(uint32 length) const { return 1; } + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_short(item, protocol, buf); + } Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_YEAR); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; class Type_handler_bit: public Type_handler_int_result { + static const Name m_name_bit; public: virtual ~Type_handler_bit() {} + const Name name() const { return m_name_bit; } enum_field_types field_type() const { return MYSQL_TYPE_BIT; } + uint32 max_display_length(const Item *item) const; + uint32 calc_pack_length(uint32 length) const { return length / 8; } + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_str(item, protocol, buf); + } + String *print_item_value(THD *thd, Item *item, String *str) const + { + return print_item_value_csstr(thd, item, str); + } Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags) const; + bool Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; class Type_handler_float: public Type_handler_real_result { + static const Name m_name_float; public: virtual ~Type_handler_float() {} + const Name name() const { return m_name_float; } enum_field_types field_type() const { return MYSQL_TYPE_FLOAT; } + bool type_can_have_auto_increment_attribute() const { return true; } + uint32 max_display_length(const Item *item) const { return 25; } + uint32 calc_pack_length(uint32 length) const { return sizeof(float); } + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_float(item, protocol, buf); + } Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_real(c, MYSQL_TYPE_FLOAT); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; class Type_handler_double: public Type_handler_real_result { + static const Name m_name_double; public: virtual ~Type_handler_double() {} + const Name name() const { return m_name_double; } enum_field_types field_type() const { return MYSQL_TYPE_DOUBLE; } + bool type_can_have_auto_increment_attribute() const { return true; } + uint32 max_display_length(const Item *item) const { return 53; } + uint32 calc_pack_length(uint32 length) const { return sizeof(double); } + Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const; + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_double(item, protocol, buf); + } Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_real(c, MYSQL_TYPE_DOUBLE); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_time: public Type_handler_temporal_result +class Type_handler_time_common: public Type_handler_temporal_result { + static const Name m_name_time; public: - virtual ~Type_handler_time() {} + virtual ~Type_handler_time_common() { } + const Name name() const { return m_name_time; } enum_field_types field_type() const { return MYSQL_TYPE_TIME; } + enum_mysql_timestamp_type mysql_timestamp_type() const + { + return MYSQL_TIMESTAMP_TIME; + } + Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const; + uint Item_decimal_scale(const Item *item) const + { + return Item_decimal_scale_with_seconds(item); + } + uint Item_decimal_precision(const Item *item) const; + uint Item_divisor_precision_increment(const Item *item) const + { + return Item_divisor_precision_increment_with_seconds(item); + } + const Type_handler *type_handler_for_comparison() const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_time(item, protocol, buf); + } + int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; + String *print_item_value(THD *thd, Item *item, String *str) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; + Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; + bool set_comparator_func(Arg_comparator *cmp) const; + cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; + in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const; +}; + + +class Type_handler_time: public Type_handler_time_common +{ + /* number of bytes to store TIME(N) */ + static uint m_hires_bytes[MAX_DATETIME_PRECISION+1]; +public: + static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; } + virtual ~Type_handler_time() {} + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_TIME); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_time2: public Type_handler_temporal_result +class Type_handler_time2: public Type_handler_time_common { public: virtual ~Type_handler_time2() {} - enum_field_types field_type() const { return MYSQL_TYPE_TIME; } enum_field_types real_field_type() const { return MYSQL_TYPE_TIME2; } + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_TIME2); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_date: public Type_handler_temporal_result +class Type_handler_temporal_with_date: public Type_handler_temporal_result { public: - virtual ~Type_handler_date() {} + virtual ~Type_handler_temporal_with_date() {} + bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_date(item, protocol, buf); + } + int Item_save_in_field(Item *item, Field *field, bool no_conversions) const; + Item *make_const_item_for_comparison(THD *, Item *src, const Item *cmp) const; + bool set_comparator_func(Arg_comparator *cmp) const; + cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const; + in_vector *make_in_vector(THD *, const Item_func_in *, uint nargs) const; +}; + + +class Type_handler_date_common: public Type_handler_temporal_with_date +{ + static const Name m_name_date; +public: + virtual ~Type_handler_date_common() {} + const Name name() const { return m_name_date; } + const Type_handler *type_handler_for_comparison() const; enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + enum_mysql_timestamp_type mysql_timestamp_type() const + { + return MYSQL_TIMESTAMP_DATE; + } + Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + uint Item_decimal_precision(const Item *item) const; + String *print_item_value(THD *thd, Item *item, String *str) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; +}; + +class Type_handler_date: public Type_handler_date_common +{ +public: + virtual ~Type_handler_date() {} + uint32 calc_pack_length(uint32 length) const { return 4; } Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_DATE); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_newdate: public Type_handler_temporal_result +class Type_handler_newdate: public Type_handler_date_common { public: virtual ~Type_handler_newdate() {} - enum_field_types field_type() const { return MYSQL_TYPE_DATE; } + enum_field_types real_field_type() const { return MYSQL_TYPE_NEWDATE; } + uint32 calc_pack_length(uint32 length) const { return 3; } Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_NEWDATE); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_datetime: public Type_handler_temporal_result +class Type_handler_datetime_common: public Type_handler_temporal_with_date { + static const Name m_name_datetime; public: - virtual ~Type_handler_datetime() {} + virtual ~Type_handler_datetime_common() {} + const Name name() const { return m_name_datetime; } + const Type_handler *type_handler_for_comparison() const; enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } + enum_mysql_timestamp_type mysql_timestamp_type() const + { + return MYSQL_TIMESTAMP_DATETIME; + } + Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + uint Item_decimal_scale(const Item *item) const + { + return Item_decimal_scale_with_seconds(item); + } + uint Item_decimal_precision(const Item *item) const; + uint Item_divisor_precision_increment(const Item *item) const + { + return Item_divisor_precision_increment_with_seconds(item); + } + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_datetime(item, protocol, buf); + } + String *print_item_value(THD *thd, Item *item, String *str) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; +}; + + +class Type_handler_datetime: public Type_handler_datetime_common +{ + /* number of bytes to store DATETIME(N) */ + static uint m_hires_bytes[MAX_DATETIME_PRECISION + 1]; +public: + static uint hires_bytes(uint dec) { return m_hires_bytes[dec]; } + virtual ~Type_handler_datetime() {} + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_DATETIME); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_datetime2: public Type_handler_temporal_result +class Type_handler_datetime2: public Type_handler_datetime_common { public: virtual ~Type_handler_datetime2() {} - enum_field_types field_type() const { return MYSQL_TYPE_DATETIME; } enum_field_types real_field_type() const { return MYSQL_TYPE_DATETIME2; } + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_DATETIME2); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_timestamp: public Type_handler_temporal_result +class Type_handler_timestamp_common: public Type_handler_temporal_with_date { + static const Name m_name_timestamp; public: - virtual ~Type_handler_timestamp() {} + virtual ~Type_handler_timestamp_common() {} + const Name name() const { return m_name_timestamp; } + const Type_handler *type_handler_for_comparison() const; enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; } + enum_mysql_timestamp_type mysql_timestamp_type() const + { + return MYSQL_TIMESTAMP_DATETIME; + } + bool is_timestamp_type() const + { + return true; + } + bool Column_definition_fix_attributes(Column_definition *c) const; + uint Item_decimal_scale(const Item *item) const + { + return Item_decimal_scale_with_seconds(item); + } + uint Item_decimal_precision(const Item *item) const; + uint Item_divisor_precision_increment(const Item *item) const + { + return Item_divisor_precision_increment_with_seconds(item); + } + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const + { + return Item_send_datetime(item, protocol, buf); + } + String *print_item_value(THD *thd, Item *item, String *str) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; +}; + + +class Type_handler_timestamp: public Type_handler_timestamp_common +{ + /* number of bytes to store second_part part of the TIMESTAMP(N) */ + static uint m_sec_part_bytes[MAX_DATETIME_PRECISION + 1]; +public: + static uint sec_part_bytes(uint dec) { return m_sec_part_bytes[dec]; } + virtual ~Type_handler_timestamp() {} + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_TIMESTAMP); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_timestamp2: public Type_handler_temporal_result +class Type_handler_timestamp2: public Type_handler_timestamp_common { public: virtual ~Type_handler_timestamp2() {} - enum_field_types field_type() const { return MYSQL_TYPE_TIMESTAMP; } enum_field_types real_field_type() const { return MYSQL_TYPE_TIMESTAMP2; } + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { + return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_TIMESTAMP2); + } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; class Type_handler_olddecimal: public Type_handler_decimal_result { + static const Name m_name_decimal; public: virtual ~Type_handler_olddecimal() {} + const Name name() const { return m_name_decimal; } enum_field_types field_type() const { return MYSQL_TYPE_DECIMAL; } + uint32 calc_pack_length(uint32 length) const { return length; } + const Type_handler *type_handler_for_tmp_table(const Item *item) const; + const Type_handler *type_handler_for_union(const Item *item) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_DECIMAL); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; class Type_handler_newdecimal: public Type_handler_decimal_result { + static const Name m_name_decimal; public: virtual ~Type_handler_newdecimal() {} + const Name name() const { return m_name_decimal; } enum_field_types field_type() const { return MYSQL_TYPE_NEWDECIMAL; } + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags) const; + bool Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_null: public Type_handler_string_result +class Type_handler_null: public Type_handler_general_purpose_string { + static const Name m_name_null; public: virtual ~Type_handler_null() {} + const Name name() const { return m_name_null; } enum_field_types field_type() const { return MYSQL_TYPE_NULL; } + const Type_handler *type_handler_for_comparison() const; + const Type_handler *type_handler_for_tmp_table(const Item *item) const; + const Type_handler *type_handler_for_union(const Item *) const; + uint32 max_display_length(const Item *item) const { return 0; } + uint32 calc_pack_length(uint32 length) const { return 0; } + bool Item_save_in_value(Item *item, st_value *value) const; + bool Item_send(Item *item, Protocol *protocol, st_value *buf) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags) const; + bool Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy(c, MYSQL_TYPE_NULL); } + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; +}; + + +class Type_handler_longstr: public Type_handler_general_purpose_string +{ +public: + bool type_can_have_key_part() const + { + return true; + } }; -class Type_handler_string: public Type_handler_string_result +class Type_handler_string: public Type_handler_longstr { + static const Name m_name_char; public: virtual ~Type_handler_string() {} + const Name name() const { return m_name_char; } enum_field_types field_type() const { return MYSQL_TYPE_STRING; } + bool is_param_long_data_type() const { return true; } + uint32 calc_pack_length(uint32 length) const { return length; } + const Type_handler *type_handler_for_tmp_table(const Item *item) const + { + return varstring_type_handler(item); + } Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; +}; + + +/* Old varchar */ +class Type_handler_var_string: public Type_handler_string +{ + static const Name m_name_var_string; +public: + virtual ~Type_handler_var_string() {} + const Name name() const { return m_name_var_string; } + enum_field_types field_type() const { return MYSQL_TYPE_VAR_STRING; } + enum_field_types real_field_type() const { return MYSQL_TYPE_STRING; } + const Type_handler *type_handler_for_tmp_table(const Item *item) const + { + return varstring_type_handler(item); + } + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const + { return Column_definition_prepare_stage2_legacy_num(c, MYSQL_TYPE_STRING); } + const Type_handler *type_handler_for_union(const Item *item) const + { + return varstring_type_handler(item); + } }; -class Type_handler_varchar: public Type_handler_string_result +class Type_handler_varchar: public Type_handler_longstr { + static const Name m_name_varchar; public: virtual ~Type_handler_varchar() {} + const Name name() const { return m_name_varchar; } enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } + uint32 calc_pack_length(uint32 length) const + { + return (length + (length < 256 ? 1: 2)); + } + const Type_handler *type_handler_for_tmp_table(const Item *item) const + { + return varstring_type_handler(item); + } + const Type_handler *type_handler_for_union(const Item *item) const + { + return varstring_type_handler(item); + } + bool is_param_long_data_type() const { return true; } Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; + bool adjust_spparam_type(Spvar_definition *def, Item *from) const; +}; + + +class Type_handler_blob_common: public Type_handler_longstr +{ +public: + virtual ~Type_handler_blob_common() { } + const Type_handler *type_handler_for_tmp_table(const Item *item) const + { + return blob_type_handler(item); + } + const Type_handler *type_handler_for_union(const Item *item) const + { + return blob_type_handler(item); + } + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer) const + { + return false; // Materialization does not work with BLOB columns + } + bool is_param_long_data_type() const { return true; } + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; }; -class Type_handler_tiny_blob: public Type_handler_string_result +class Type_handler_tiny_blob: public Type_handler_blob_common { + static const Name m_name_tinyblob; public: virtual ~Type_handler_tiny_blob() {} + const Name name() const { return m_name_tinyblob; } enum_field_types field_type() const { return MYSQL_TYPE_TINY_BLOB; } + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_medium_blob: public Type_handler_string_result +class Type_handler_medium_blob: public Type_handler_blob_common { + static const Name m_name_mediumblob; public: virtual ~Type_handler_medium_blob() {} + const Name name() const { return m_name_mediumblob; } enum_field_types field_type() const { return MYSQL_TYPE_MEDIUM_BLOB; } + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_long_blob: public Type_handler_string_result +class Type_handler_long_blob: public Type_handler_blob_common { + static const Name m_name_longblob; public: virtual ~Type_handler_long_blob() {} + const Name name() const { return m_name_longblob; } enum_field_types field_type() const { return MYSQL_TYPE_LONG_BLOB; } + uint32 calc_pack_length(uint32 length) const; + Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_blob: public Type_handler_string_result +class Type_handler_blob: public Type_handler_blob_common { + static const Name m_name_blob; public: virtual ~Type_handler_blob() {} + const Name name() const { return m_name_blob; } enum_field_types field_type() const { return MYSQL_TYPE_BLOB; } + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; #ifdef HAVE_SPATIAL class Type_handler_geometry: public Type_handler_string_result { + static const Name m_name_geometry; public: virtual ~Type_handler_geometry() {} + const Name name() const { return m_name_geometry; } enum_field_types field_type() const { return MYSQL_TYPE_GEOMETRY; } + bool is_param_long_data_type() const { return true; } + uint32 calc_pack_length(uint32 length) const; + const Type_handler *type_handler_for_comparison() const; + bool type_can_have_key_part() const + { + return true; + } + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer) const + { + return false; // Materialization does not work with GEOMETRY columns + } + bool Item_param_set_from_value(THD *thd, + Item_param *param, + const Type_all_attributes *attr, + const st_value *value) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; + + bool can_return_int() const { return false; } + bool can_return_decimal() const { return false; } + bool can_return_real() const { return false; } + bool can_return_text() const { return false; } + bool can_return_date() const { return false; } + bool can_return_time() const { return false; } + bool is_traditional_type() const + { + return false; + } + bool Item_func_round_fix_length_and_dec(Item_func_round *) const; + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *) const; + bool Item_func_abs_fix_length_and_dec(Item_func_abs *) const; + bool Item_func_neg_fix_length_and_dec(Item_func_neg *) const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *h, + Type_all_attributes *attr, + Item **items, uint nitems) const; + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *) const; + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *) const; + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *) const; + + bool Item_func_signed_fix_length_and_dec(Item_func_signed *) const; + bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *) const; + bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *) const; + bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *) const; + bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *) const; + bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *) const; + bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *) const; + bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *) const; }; + +extern MYSQL_PLUGIN_IMPORT Type_handler_geometry type_handler_geometry; #endif -class Type_handler_enum: public Type_handler_string_result +class Type_handler_typelib: public Type_handler_general_purpose_string +{ +public: + virtual ~Type_handler_typelib() { } + enum_field_types field_type() const { return MYSQL_TYPE_STRING; } + const Type_handler *type_handler_for_item_field() const; + const Type_handler *cast_to_int_type_handler() const; + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *, + Type_all_attributes *atrr, + Item **items, uint nitems) const; + bool Column_definition_prepare_stage1(THD *thd, + MEM_ROOT *mem_root, + Column_definition *c, + handler *file, + ulonglong table_flags) const; + bool Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file, + const Schema_specification_st *schema) + const; +}; + + +class Type_handler_enum: public Type_handler_typelib { + static const Name m_name_enum; public: virtual ~Type_handler_enum() {} - enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } + const Name name() const { return m_name_enum; } virtual enum_field_types real_field_type() const { return MYSQL_TYPE_ENUM; } + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; -class Type_handler_set: public Type_handler_string_result +class Type_handler_set: public Type_handler_typelib { + static const Name m_name_set; public: virtual ~Type_handler_set() {} - enum_field_types field_type() const { return MYSQL_TYPE_VARCHAR; } + const Name name() const { return m_name_set; } virtual enum_field_types real_field_type() const { return MYSQL_TYPE_SET; } + uint32 calc_pack_length(uint32 length) const; Field *make_conversion_table_field(TABLE *, uint metadata, const Field *target) const; + bool Column_definition_fix_attributes(Column_definition *c) const; + bool Column_definition_prepare_stage2(Column_definition *c, + handler *file, + ulonglong table_flags) const; + Field *make_table_field(const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE *table) const; }; @@ -499,97 +2795,192 @@ public: Makes sure that field_type(), cmp_type() and result_type() are always in sync to each other for hybrid functions. */ -class Type_handler_hybrid_field_type: public Type_handler +class Type_handler_hybrid_field_type { const Type_handler *m_type_handler; - const Type_handler *get_handler_by_result_type(Item_result type) const; + bool aggregate_for_min_max(const Type_handler *other); public: Type_handler_hybrid_field_type(); Type_handler_hybrid_field_type(const Type_handler *handler) :m_type_handler(handler) { } - Type_handler_hybrid_field_type(enum_field_types type) - :m_type_handler(get_handler_by_field_type(type)) - { } Type_handler_hybrid_field_type(const Type_handler_hybrid_field_type *other) :m_type_handler(other->m_type_handler) { } - enum_field_types field_type() const { return m_type_handler->field_type(); } + const Type_handler *type_handler() const { return m_type_handler; } enum_field_types real_field_type() const { return m_type_handler->real_field_type(); } - Item_result result_type() const { return m_type_handler->result_type(); } Item_result cmp_type() const { return m_type_handler->cmp_type(); } + enum_mysql_timestamp_type mysql_timestamp_type() const + { + return m_type_handler->mysql_timestamp_type(); + } + bool is_timestamp_type() const + { + return m_type_handler->is_timestamp_type(); + } void set_handler(const Type_handler *other) { m_type_handler= other; } const Type_handler *set_handler_by_result_type(Item_result type) { - return (m_type_handler= get_handler_by_result_type(type)); + return (m_type_handler= Type_handler::get_handler_by_result_type(type)); + } + const Type_handler *set_handler_by_cmp_type(Item_result type) + { + return (m_type_handler= Type_handler::get_handler_by_cmp_type(type)); } const Type_handler *set_handler_by_result_type(Item_result type, uint max_octet_length, CHARSET_INFO *cs) { - m_type_handler= get_handler_by_result_type(type); + m_type_handler= Type_handler::get_handler_by_result_type(type); return m_type_handler= m_type_handler->type_handler_adjusted_to_max_octet_length(max_octet_length, cs); } const Type_handler *set_handler_by_field_type(enum_field_types type) { - return (m_type_handler= get_handler_by_field_type(type)); + return (m_type_handler= Type_handler::get_handler_by_field_type(type)); } const Type_handler *set_handler_by_real_type(enum_field_types type) { - return (m_type_handler= get_handler_by_real_type(type)); - } - const Type_handler * - type_handler_adjusted_to_max_octet_length(uint max_octet_length, - CHARSET_INFO *cs) const - { - return - m_type_handler->type_handler_adjusted_to_max_octet_length(max_octet_length, - cs); - } - Field *make_num_distinct_aggregator_field(MEM_ROOT *mem_root, - const Item *item) const - { - return m_type_handler->make_num_distinct_aggregator_field(mem_root, item); + return (m_type_handler= Type_handler::get_handler_by_real_type(type)); } - Field *make_conversion_table_field(TABLE *table, uint metadata, - const Field *target) const + bool aggregate_for_comparison(const Type_handler *other); + bool aggregate_for_comparison(const char *funcname, + Item **items, uint nitems, + bool treat_int_to_uint_as_decimal); + bool aggregate_for_result(const Type_handler *other); + bool aggregate_for_result(const char *funcname, + Item **item, uint nitems, bool treat_bit_as_number); + bool aggregate_for_min_max(const char *funcname, Item **item, uint nitems); + + bool aggregate_for_num_op(const class Type_aggregator *aggregator, + const Type_handler *h0, const Type_handler *h1); +}; + + +extern MYSQL_PLUGIN_IMPORT Type_handler_row type_handler_row; +extern MYSQL_PLUGIN_IMPORT Type_handler_null type_handler_null; + +extern MYSQL_PLUGIN_IMPORT Type_handler_float type_handler_float; +extern MYSQL_PLUGIN_IMPORT Type_handler_double type_handler_double; + +extern MYSQL_PLUGIN_IMPORT Type_handler_bit type_handler_bit; + +extern MYSQL_PLUGIN_IMPORT Type_handler_enum type_handler_enum; +extern MYSQL_PLUGIN_IMPORT Type_handler_set type_handler_set; + +extern MYSQL_PLUGIN_IMPORT Type_handler_string type_handler_string; +extern MYSQL_PLUGIN_IMPORT Type_handler_var_string type_handler_var_string; +extern MYSQL_PLUGIN_IMPORT Type_handler_varchar type_handler_varchar; + +extern MYSQL_PLUGIN_IMPORT Type_handler_tiny_blob type_handler_tiny_blob; +extern MYSQL_PLUGIN_IMPORT Type_handler_medium_blob type_handler_medium_blob; +extern MYSQL_PLUGIN_IMPORT Type_handler_long_blob type_handler_long_blob; +extern MYSQL_PLUGIN_IMPORT Type_handler_blob type_handler_blob; + +extern MYSQL_PLUGIN_IMPORT Type_handler_tiny type_handler_tiny; +extern MYSQL_PLUGIN_IMPORT Type_handler_short type_handler_short; +extern MYSQL_PLUGIN_IMPORT Type_handler_int24 type_handler_int24; +extern MYSQL_PLUGIN_IMPORT Type_handler_long type_handler_long; +extern MYSQL_PLUGIN_IMPORT Type_handler_longlong type_handler_longlong; +extern MYSQL_PLUGIN_IMPORT Type_handler_longlong type_handler_ulonglong; + +extern MYSQL_PLUGIN_IMPORT Type_handler_newdecimal type_handler_newdecimal; +extern MYSQL_PLUGIN_IMPORT Type_handler_olddecimal type_handler_olddecimal; + +extern MYSQL_PLUGIN_IMPORT Type_handler_year type_handler_year; +extern MYSQL_PLUGIN_IMPORT Type_handler_newdate type_handler_newdate; +extern MYSQL_PLUGIN_IMPORT Type_handler_date type_handler_date; +extern MYSQL_PLUGIN_IMPORT Type_handler_time type_handler_time; +extern MYSQL_PLUGIN_IMPORT Type_handler_time2 type_handler_time2; +extern MYSQL_PLUGIN_IMPORT Type_handler_datetime type_handler_datetime; +extern MYSQL_PLUGIN_IMPORT Type_handler_datetime2 type_handler_datetime2; +extern MYSQL_PLUGIN_IMPORT Type_handler_timestamp type_handler_timestamp; +extern MYSQL_PLUGIN_IMPORT Type_handler_timestamp2 type_handler_timestamp2; + +extern MYSQL_PLUGIN_IMPORT Type_handler_tiny_blob type_handler_tiny_blob; +extern MYSQL_PLUGIN_IMPORT Type_handler_blob type_handler_blob; +extern MYSQL_PLUGIN_IMPORT Type_handler_medium_blob type_handler_medium_blob; +extern MYSQL_PLUGIN_IMPORT Type_handler_long_blob type_handler_long_blob; + +class Type_aggregator +{ + bool m_is_commutative; + class Pair { - return m_type_handler->make_conversion_table_field(table, metadata, target); - } - void make_sort_key(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, - Sort_param *param) const + public: + const Type_handler *m_handler1; + const Type_handler *m_handler2; + const Type_handler *m_result; + Pair() { } + Pair(const Type_handler *handler1, + const Type_handler *handler2, + const Type_handler *result) + :m_handler1(handler1), m_handler2(handler2), m_result(result) + { } + bool eq(const Type_handler *handler1, const Type_handler *handler2) const + { + return m_handler1 == handler1 && m_handler2 == handler2; + } + }; + Dynamic_array<Pair> m_array; + const Pair* find_pair(const Type_handler *handler1, + const Type_handler *handler2) const; +public: + Type_aggregator(bool is_commutative= false) + :m_is_commutative(is_commutative) + { } + bool add(const Type_handler *handler1, + const Type_handler *handler2, + const Type_handler *result) { - m_type_handler->make_sort_key(to, item, sort_field, param); + return m_array.append(Pair(handler1, handler2, result)); } - void sortlength(THD *thd, - const Type_std_attributes *item, - SORT_FIELD_ATTR *attr) const + const Type_handler *find_handler(const Type_handler *handler1, + const Type_handler *handler2) const { - m_type_handler->sortlength(thd, item, attr); + const Pair* el= find_pair(handler1, handler2); + return el ? el->m_result : NULL; } - + bool is_commutative() const { return m_is_commutative; } }; -/** - This class is used for Item_type_holder, which preserves real_type. -*/ -class Type_handler_hybrid_real_field_type: - public Type_handler_hybrid_field_type +class Type_aggregator_commutative: public Type_aggregator { public: - Type_handler_hybrid_real_field_type(enum_field_types type) - :Type_handler_hybrid_field_type(get_handler_by_real_type(type)) + Type_aggregator_commutative() + :Type_aggregator(true) { } }; +class Type_handler_data +{ +public: + Type_aggregator_commutative m_type_aggregator_for_result; + Type_aggregator_commutative m_type_aggregator_for_comparison; + + Type_aggregator_commutative m_type_aggregator_for_plus; + Type_aggregator_commutative m_type_aggregator_for_mul; + + Type_aggregator m_type_aggregator_for_minus; + Type_aggregator m_type_aggregator_for_div; + Type_aggregator m_type_aggregator_for_mod; +#ifndef DBUG_OFF + // This is used for mtr purposes in debug builds + Type_aggregator m_type_aggregator_non_commutative_test; +#endif + bool init(); +}; + + +extern Type_handler_data *type_handler_data; + #endif /* SQL_TYPE_H_INCLUDED */ diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index 05698ce82cc..612cc97f6a2 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -54,12 +54,12 @@ static HASH udf_hash; static mysql_rwlock_t THR_LOCK_udf; -static udf_func *add_udf(LEX_STRING *name, Item_result ret, - char *dl, Item_udftype typ); +static udf_func *add_udf(LEX_CSTRING *name, Item_result ret, + const char *dl, Item_udftype typ); static void del_udf(udf_func *udf); static void *find_udf_dl(const char *dl); -static char *init_syms(udf_func *tmp, char *nm) +static const char *init_syms(udf_func *tmp, char *nm) { char *end; @@ -192,7 +192,7 @@ void udf_init() while (!(error= read_record_info.read_record(&read_record_info))) { DBUG_PRINT("info",("init udf record")); - LEX_STRING name; + LEX_CSTRING name; name.str=get_field(&mem, table->field[0]); name.length = (uint) strlen(name.str); char *dl_name= get_field(&mem, table->field[2]); @@ -242,7 +242,8 @@ void udf_init() } tmp->dlhandle = dl; { - char buf[SAFE_NAME_LEN+16], *missing; + char buf[SAFE_NAME_LEN+16]; + const char *missing; if ((missing= init_syms(tmp, buf))) { sql_print_error(ER_THD(new_thd, ER_CANT_FIND_DL_ENTRY), missing); @@ -311,9 +312,9 @@ static void del_udf(udf_func *udf) The functions will be automaticly removed when the least threads doesn't use it anymore */ - char *name= udf->name.str; + const char *name= udf->name.str; uint name_length=udf->name.length; - udf->name.str=(char*) "*"; + udf->name.str= "*"; udf->name.length=1; my_hash_update(&udf_hash,(uchar*) udf,(uchar*) name,name_length); } @@ -351,6 +352,7 @@ udf_func *find_udf(const char *name,uint length,bool mark_used) { udf_func *udf=0; DBUG_ENTER("find_udf"); + DBUG_ASSERT(strlen(name) == length); if (!initialized) DBUG_RETURN(NULL); @@ -362,8 +364,7 @@ udf_func *find_udf(const char *name,uint length,bool mark_used) else mysql_rwlock_rdlock(&THR_LOCK_udf); /* Called during parsing */ - if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) name, - length ? length : (uint) strlen(name)))) + if ((udf=(udf_func*) my_hash_search(&udf_hash,(uchar*) name, length))) { if (!udf->dlhandle) udf=0; // Could not be opened @@ -395,7 +396,7 @@ static void *find_udf_dl(const char *dl) /* Assume that name && dl is already allocated */ -static udf_func *add_udf(LEX_STRING *name, Item_result ret, char *dl, +static udf_func *add_udf(LEX_CSTRING *name, Item_result ret, const char *dl, Item_udftype type) { if (!name || !dl || !(uint) type || (uint) type > (uint) UDFTYPE_AGGREGATE) @@ -431,7 +432,7 @@ static int mysql_drop_function_internal(THD *thd, udf_func *udf, TABLE *table) { DBUG_ENTER("mysql_drop_function_internal"); - char *exact_name_str= udf->name.str; + const char *exact_name_str= udf->name.str; uint exact_name_len= udf->name.length; del_udf(udf); @@ -548,7 +549,8 @@ int mysql_create_function(THD *thd,udf_func *udf) } udf->dlhandle=dl; { - char buf[SAFE_NAME_LEN+16], *missing; + char buf[SAFE_NAME_LEN+16]; + const char *missing; if ((missing= init_syms(udf, buf))) { my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), missing); @@ -604,7 +606,7 @@ err: } -int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) +int mysql_drop_function(THD *thd, const LEX_CSTRING *udf_name) { TABLE *table; TABLE_LIST tables; diff --git a/sql/sql_udf.h b/sql/sql_udf.h index d3ec1cc1f95..1c805227f97 100644 --- a/sql/sql_udf.h +++ b/sql/sql_udf.h @@ -37,10 +37,10 @@ typedef longlong (*Udf_func_longlong)(UDF_INIT *, UDF_ARGS *, uchar *, typedef struct st_udf_func { - LEX_STRING name; + LEX_CSTRING name; Item_result returns; Item_udftype type; - char *dl; + const char *dl; void *dlhandle; Udf_func_any func; Udf_func_init func_init; @@ -137,10 +137,10 @@ class udf_handler :public Sql_alloc #ifdef HAVE_DLOPEN void udf_init(void),udf_free(void); -udf_func *find_udf(const char *name, uint len=0,bool mark_used=0); +udf_func *find_udf(const char *name, uint len, bool mark_used=0); void free_udf(udf_func *udf); int mysql_create_function(THD *thd,udf_func *udf); -int mysql_drop_function(THD *thd,const LEX_STRING *name); +int mysql_drop_function(THD *thd, const LEX_CSTRING *name); #else static inline void udf_init(void) { } static inline void udf_free(void) { } diff --git a/sql/sql_union.cc b/sql/sql_union.cc index 7cbc69f2ee7..6b5c328fd5d 100644 --- a/sql/sql_union.cc +++ b/sql/sql_union.cc @@ -48,15 +48,67 @@ bool mysql_union(THD *thd, LEX *lex, select_result *result, ** store records in temporary table for UNION ***************************************************************************/ -int select_union::prepare(List<Item> &list, SELECT_LEX_UNIT *u) +int select_unit::prepare(List<Item> &list, SELECT_LEX_UNIT *u) { unit= u; return 0; } +/** + This called by SELECT_LEX_UNIT::exec when select changed +*/ -int select_union::send_data(List<Item> &values) +void select_unit::change_select() +{ + uint current_select_number= thd->lex->current_select->select_number; + DBUG_ENTER("select_unit::change_select"); + DBUG_PRINT("enter", ("select in unit change: %u -> %u", + curr_sel, current_select_number)); + DBUG_ASSERT(curr_sel != current_select_number); + curr_sel= current_select_number; + /* New SELECT processing starts */ + DBUG_ASSERT(table->file->inited == 0); + switch (thd->lex->current_select->linkage) + { + case INTERSECT_TYPE: + intersect_mark->value= prev_step= curr_step; + curr_step= current_select_number; + case EXCEPT_TYPE: + step= thd->lex->current_select->linkage; + break; + default: + step= UNION_TYPE; + break; + } + DBUG_VOID_RETURN; +} +/** + Fill temporary tables for UNION/EXCEPT/INTERSECT + + @Note +UNION: + just add records to the table (with 'counter' field first if INTERSECT + present in the sequence). +EXCEPT: + looks for the record in the table (with 'counter' field first if + INTERSECT present in the sequence) and delete it if found +INTESECT: + looks for the same record with 'counter' field of previous operation, + put as a 'counter' number of the current SELECT. + We scan the table and remove all records which marked with not last + 'counter' after processing all records in send_eof and only if it last + SELECT of sequence of INTERSECTS. + + @param values List of record items to process. + + @retval 0 - OK + @retval -1 - duplicate + @retval 1 - error +*/ +int select_unit::send_data(List<Item> &values) { + int rc; + int not_reported_error= 0; if (unit->offset_limit_cnt) { // using limit offset,count unit->offset_limit_cnt--; @@ -66,44 +118,189 @@ int select_union::send_data(List<Item> &values) return 0; if (table->no_rows_with_nulls) table->null_catch_flags= CHECK_ROW_FOR_NULLS_TO_REJECT; - fill_record(thd, table, table->field, values, TRUE, FALSE); + if (intersect_mark) + { + fill_record(thd, table, table->field + 1, values, TRUE, FALSE); + table->field[0]->store((ulonglong) curr_step, 1); + } + else + fill_record(thd, table, table->field, values, TRUE, FALSE); if (thd->is_error()) - return 1; + { + rc= 1; + goto end; + } if (table->no_rows_with_nulls) { table->null_catch_flags&= ~CHECK_ROW_FOR_NULLS_TO_REJECT; if (table->null_catch_flags) - return 0; + { + rc= 0; + goto end; + } } - if ((write_err= table->file->ha_write_tmp_row(table->record[0]))) + // select_unit::change_select() change step & Co correctly for each SELECT + switch (step) { - if (write_err == HA_ERR_FOUND_DUPP_KEY) + case UNION_TYPE: { + if ((write_err= table->file->ha_write_tmp_row(table->record[0]))) + { + if (write_err == HA_ERR_FOUND_DUPP_KEY) + { + /* + Inform upper level that we found a duplicate key, that should not + be counted as part of limit + */ + rc= -1; + goto end; + } + bool is_duplicate= FALSE; + /* create_internal_tmp_table_from_heap will generate error if needed */ + if (table->file->is_fatal_error(write_err, HA_CHECK_DUP) && + create_internal_tmp_table_from_heap(thd, table, + tmp_table_param.start_recinfo, + &tmp_table_param.recinfo, + write_err, 1, &is_duplicate)) + { + rc= 1; + goto end; + } + + if (is_duplicate) + { + rc= -1; + goto end; + } + } + break; + } + case EXCEPT_TYPE: + { + int find_res; /* - Inform upper level that we found a duplicate key, that should not - be counted as part of limit + The temporary table uses very first index or constrain for + checking unique constrain. */ - return -1; + if (!(find_res= table->file->find_unique_row(table->record[0], 0))) + { + DBUG_ASSERT(!table->triggers); + table->status|= STATUS_DELETED; + not_reported_error= table->file->ha_delete_tmp_row(table->record[0]); + rc= MY_TEST(not_reported_error); + goto end; + } + else + { + if ((rc= not_reported_error= (find_res != 1))) + goto end; + } + break; + } + case INTERSECT_TYPE: + { + int find_res; + /* + The temporary table uses very first index or constrain for + checking unique constrain. + */ + if (!(find_res= table->file->find_unique_row(table->record[0], 0))) + { + DBUG_ASSERT(!table->triggers); + if (table->field[0]->val_int() != prev_step) + { + rc= 0; + goto end; + } + store_record(table, record[1]); + table->field[0]->store(curr_step, 0); + not_reported_error= table->file->ha_update_tmp_row(table->record[1], + table->record[0]); + rc= MY_TEST(not_reported_error); + DBUG_ASSERT(rc != HA_ERR_RECORD_IS_THE_SAME); + goto end; + } + else + { + if ((rc= not_reported_error= (find_res != 1))) + goto end; + } + break; } - bool is_duplicate= FALSE; - /* create_internal_tmp_table_from_heap will generate error if needed */ - if (table->file->is_fatal_error(write_err, HA_CHECK_DUP) && - create_internal_tmp_table_from_heap(thd, table, - tmp_table_param.start_recinfo, - &tmp_table_param.recinfo, - write_err, 1, &is_duplicate)) - return 1; - if (is_duplicate) - return -1; + default: + DBUG_ASSERT(0); } - return 0; + rc= 0; + +end: + if (not_reported_error) + { + DBUG_ASSERT(rc); + table->file->print_error(not_reported_error, MYF(0)); + } + return rc; +} + +bool select_unit::send_eof() +{ + if (step != INTERSECT_TYPE || + (thd->lex->current_select->next_select() && + thd->lex->current_select->next_select()->linkage == INTERSECT_TYPE)) + { + /* + it is not INTESECT or next SELECT in the sequence is INTERSECT so no + need filtering (the last INTERSECT in this sequence of intersects will + filter). + */ + return 0; + } + + /* + It is last select in the sequence of INTERSECTs so we should filter out + all records except marked with actual counter. + + TODO: as optimization for simple case this could be moved to + 'fake_select' WHERE condition + */ + handler *file= table->file; + int error; + + if (file->ha_rnd_init_with_error(1)) + return 1; + + do + { + error= file->ha_rnd_next(table->record[0]); + if (error) + { + if (error == HA_ERR_END_OF_FILE) + { + error= 0; + break; + } + if (unlikely(error == HA_ERR_RECORD_DELETED)) + { + error= 0; + continue; + } + break; + } + if (table->field[0]->val_int() != curr_step) + error= file->ha_delete_tmp_row(table->record[0]); + } while (!error); + file->ha_rnd_end(); + + if (error) + table->file->print_error(error, MYF(0)); + + return(MY_TEST(error)); } int select_union_recursive::send_data(List<Item> &values) { - int rc= select_union::send_data(values); + int rc= select_unit::send_data(values); if (write_err != HA_ERR_FOUND_DUPP_KEY && write_err != HA_ERR_FOUND_DUPP_UNIQUE) @@ -123,13 +320,7 @@ int select_union_recursive::send_data(List<Item> &values) } -bool select_union::send_eof() -{ - return 0; -} - - -bool select_union::flush() +bool select_unit::flush() { int error; if ((error=table->file->extra(HA_EXTRA_NO_CACHE))) @@ -140,11 +331,12 @@ bool select_union::flush() return 0; } + /* Create a temporary table to store the result of select_union. SYNOPSIS - select_union::create_result_table() + select_unit::create_result_table() thd thread handle column_types a list of items used to define columns of the temporary table @@ -155,6 +347,7 @@ bool select_union::flush() bit_fields_as_long convert bit fields to ulonglong create_table whether to physically create result table keep_row_order keep rows in order as they were inserted + hidden number of hidden fields (for INTERSECT) DESCRIPTION Create a temporary table that is used to store the result of a UNION, @@ -166,16 +359,18 @@ bool select_union::flush() */ bool -select_union::create_result_table(THD *thd_arg, List<Item> *column_types, +select_unit::create_result_table(THD *thd_arg, List<Item> *column_types, bool is_union_distinct, ulonglong options, const char *alias, bool bit_fields_as_long, bool create_table, - bool keep_row_order) + bool keep_row_order, + uint hidden) { DBUG_ASSERT(table == 0); tmp_table_param.init(); tmp_table_param.field_count= column_types->elements; tmp_table_param.bit_fields_as_long= bit_fields_as_long; + tmp_table_param.hidden_field_count= hidden; if (! (table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, (ORDER*) 0, is_union_distinct, 1, @@ -203,12 +398,14 @@ select_union_recursive::create_result_table(THD *thd_arg, const char *alias, bool bit_fields_as_long, bool create_table, - bool keep_row_order) + bool keep_row_order, + uint hidden) { - if (select_union::create_result_table(thd_arg, column_types, - is_union_distinct, options, - "", bit_fields_as_long, - create_table, keep_row_order)) + if (select_unit::create_result_table(thd_arg, column_types, + is_union_distinct, options, + "", bit_fields_as_long, + create_table, keep_row_order, + hidden)) return true; if (! (incr_table= create_tmp_table(thd_arg, &tmp_table_param, *column_types, @@ -253,7 +450,7 @@ select_union_recursive::create_result_table(THD *thd_arg, tables of JOIN - exec_tmp_table_[1 | 2]. */ -void select_union::cleanup() +void select_unit::cleanup() { table->file->extra(HA_EXTRA_RESET_STATE); table->file->ha_delete_all_rows(); @@ -264,7 +461,7 @@ void select_union_recursive::cleanup() { if (table) { - select_union::cleanup(); + select_unit::cleanup(); free_tmp_table(thd, table); } @@ -440,6 +637,243 @@ st_select_lex_unit::init_prepare_fake_select_lex(THD *thd_arg, } +bool st_select_lex_unit::prepare_join(THD *thd_arg, SELECT_LEX *sl, + select_result *tmp_result, + ulong additional_options, + bool is_union_select) +{ + DBUG_ENTER("st_select_lex_unit::prepare_join"); + bool can_skip_order_by; + sl->options|= SELECT_NO_UNLOCK; + JOIN *join= new JOIN(thd_arg, sl->item_list, + (sl->options | thd_arg->variables.option_bits | + additional_options), + tmp_result); + if (!join) + DBUG_RETURN(true); + + thd_arg->lex->current_select= sl; + + can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit); + + saved_error= join->prepare(sl->table_list.first, + sl->with_wild, + sl->where, + (can_skip_order_by ? 0 : + sl->order_list.elements) + + sl->group_list.elements, + can_skip_order_by ? + NULL : sl->order_list.first, + can_skip_order_by, + sl->group_list.first, + sl->having, + (is_union_select ? NULL : + thd_arg->lex->proc_list.first), + sl, this); + + /* There are no * in the statement anymore (for PS) */ + sl->with_wild= 0; + last_procedure= join->procedure; + + if (saved_error || (saved_error= thd_arg->is_fatal_error)) + DBUG_RETURN(true); + /* + Remove all references from the select_lex_units to the subqueries that + are inside the ORDER BY clause. + */ + if (can_skip_order_by) + { + for (ORDER *ord= (ORDER *)sl->order_list.first; ord; ord= ord->next) + { + (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL); + } + } + DBUG_RETURN(false); +} + + +class Type_holder: public Sql_alloc, + public Item_args, + public Type_handler_hybrid_field_type, + public Type_all_attributes, + public Type_geometry_attributes +{ + TYPELIB *m_typelib; + bool m_maybe_null; +public: + Type_holder() + :m_typelib(NULL), + m_maybe_null(false) + { } + + void set_maybe_null(bool maybe_null_arg) { m_maybe_null= maybe_null_arg; } + bool get_maybe_null() const { return m_maybe_null; } + + uint decimal_precision() const + { + /* + Type_holder is not used directly to create fields, so + its virtual decimal_precision() is never called. + We should eventually extend create_result_table() to accept + an array of Type_holders directly, without having to allocate + Item_type_holder's and put them into List<Item>. + */ + DBUG_ASSERT(0); + return 0; + } + void set_geometry_type(uint type) + { + Type_geometry_attributes::set_geometry_type(type); + } + uint uint_geometry_type() const + { + return Type_geometry_attributes::get_geometry_type(); + } + void set_typelib(TYPELIB *typelib) + { + m_typelib= typelib; + } + TYPELIB *get_typelib() const + { + return m_typelib; + } + + bool aggregate_attributes(THD *thd) + { + for (uint i= 0; i < arg_count; i++) + m_maybe_null|= args[i]->maybe_null; + return + type_handler()->Item_hybrid_func_fix_attributes(thd, + "UNION", this, this, + args, arg_count); + } +}; + + +/** + Aggregate data type handlers for the "count" leftmost UNION parts. +*/ +bool st_select_lex_unit::join_union_type_handlers(THD *thd_arg, + Type_holder *holders, + uint count) +{ + DBUG_ENTER("st_select_lex_unit::join_union_type_handlers"); + SELECT_LEX *first_sl= first_select(), *sl= first_sl; + for (uint i= 0; i < count ; sl= sl->next_select(), i++) + { + Item *item; + List_iterator_fast<Item> it(sl->item_list); + for (uint pos= 0; (item= it++); pos++) + { + const Type_handler *item_type_handler= item->real_type_handler(); + if (sl == first_sl) + holders[pos].set_handler(item_type_handler); + else + { + if (first_sl->item_list.elements != sl->item_list.elements) + { + my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, + ER_THD(thd_arg, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT), + MYF(0)); + DBUG_RETURN(true); + } + if (holders[pos].aggregate_for_result(item_type_handler)) + { + my_error(ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION, MYF(0), + holders[pos].type_handler()->name().ptr(), + item_type_handler->name().ptr(), + "UNION"); + DBUG_RETURN(true); + } + } + } + } + DBUG_RETURN(false); +} + + +/** + Aggregate data type attributes for the "count" leftmost UNION parts. +*/ +bool st_select_lex_unit::join_union_type_attributes(THD *thd_arg, + Type_holder *holders, + uint count) +{ + DBUG_ENTER("st_select_lex_unit::join_union_type_attributes"); + SELECT_LEX *sl, *first_sl= first_select(); + uint item_pos; + for (uint pos= 0; pos < first_sl->item_list.elements; pos++) + { + if (holders[pos].alloc_arguments(thd_arg, count)) + DBUG_RETURN(true); + } + for (item_pos= 0, sl= first_sl ; + item_pos < count; + sl= sl->next_select(), item_pos++) + { + Item *item_tmp; + List_iterator_fast<Item> itx(sl->item_list); + for (uint holder_pos= 0 ; (item_tmp= itx++); holder_pos++) + { + /* + If the outer query has a GROUP BY clause, an outer reference to this + query block may have been wrapped in a Item_outer_ref, which has not + been fixed yet. An Item_type_holder must be created based on a fixed + Item, so use the inner Item instead. + */ + DBUG_ASSERT(item_tmp->fixed || + (item_tmp->type() == Item::REF_ITEM && + ((Item_ref *)(item_tmp))->ref_type() == + Item_ref::OUTER_REF)); + if (!item_tmp->fixed) + item_tmp= item_tmp->real_item(); + holders[holder_pos].add_argument(item_tmp); + } + } + for (uint pos= 0; pos < first_sl->item_list.elements; pos++) + { + if (holders[pos].aggregate_attributes(thd_arg)) + DBUG_RETURN(true); + } + DBUG_RETURN(false); +} + + +/** + Join data types for the leftmost "count" UNION parts + and store corresponding Item_type_holder's into "types". +*/ +bool st_select_lex_unit::join_union_item_types(THD *thd_arg, + List<Item> &types, + uint count) +{ + DBUG_ENTER("st_select_lex_unit::join_union_select_list_types"); + SELECT_LEX *first_sl= first_select(); + Type_holder *holders; + + if (!(holders= new (thd_arg->mem_root) + Type_holder[first_sl->item_list.elements]) || + join_union_type_handlers(thd_arg, holders, count) || + join_union_type_attributes(thd_arg, holders, count)) + DBUG_RETURN(true); + + types.empty(); + List_iterator_fast<Item> it(first_sl->item_list); + Item *item_tmp; + for (uint pos= 0; (item_tmp= it++); pos++) + { + /* Error's in 'new' will be detected after loop */ + types.push_back(new (thd_arg->mem_root) + Item_type_holder(thd_arg, + &item_tmp->name, + holders[pos].type_handler(), + &holders[pos]/*Type_all_attributes*/, + holders[pos].get_maybe_null())); + } + if (thd_arg->is_fatal_error) + DBUG_RETURN(true); // out of memory + DBUG_RETURN(false); +} bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, @@ -449,8 +883,10 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, SELECT_LEX *sl, *first_sl= first_select(); bool is_recursive= with_element && with_element->is_recursive; bool is_rec_result_table_created= false; + uint union_part_count= 0; select_result *tmp_result; bool is_union_select; + bool have_except= FALSE, have_intersect= FALSE; bool instantiate_tmp_table= false; DBUG_ENTER("st_select_lex_unit::prepare"); DBUG_ASSERT(thd == thd_arg); @@ -497,13 +933,28 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, thd_arg->lex->current_select= sl= first_sl; found_rows_for_union= first_sl->options & OPTION_FOUND_ROWS; - is_union_select= is_union() || fake_select_lex; + is_union_select= is_unit_op() || fake_select_lex; + for (SELECT_LEX *s= first_sl; s; s= s->next_select()) + { + switch (s->linkage) + { + case INTERSECT_TYPE: + have_intersect= TRUE; + break; + case EXCEPT_TYPE: + have_except= TRUE; + break; + default: + break; + } + } /* Global option */ if (is_union_select || is_recursive) { - if (is_union() && !union_needs_tmp_table()) + if (is_unit_op() && !union_needs_tmp_table() && + !have_except && !have_intersect) { SELECT_LEX *last= first_select(); while (last->next_select()) @@ -518,7 +969,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, else { if (!is_recursive) - union_result= new (thd_arg->mem_root) select_union(thd_arg); + union_result= new (thd_arg->mem_root) select_unit(thd_arg); else { with_element->rec_result= @@ -535,14 +986,22 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, tmp_result= sel_result; sl->context.resolve_in_select_list= TRUE; + + if (!is_union_select && !is_recursive) + { + if (prepare_join(thd_arg, first_sl, tmp_result, additional_options, + is_union_select)) + goto err; + types= first_sl->item_list; + goto cont; + } - for (;sl; sl= sl->next_select()) - { - bool can_skip_order_by; - sl->options|= SELECT_NO_UNLOCK; - JOIN *join= new JOIN(thd_arg, sl->item_list, - sl->options | thd_arg->variables.option_bits | additional_options, - tmp_result); + for (;sl; sl= sl->next_select(), union_part_count++) + { + if (prepare_join(thd_arg, sl, tmp_result, additional_options, + is_union_select)) + goto err; + /* setup_tables_done_option should be set only for very first SELECT, because it protect from secont setup_tables call for select-like non @@ -550,106 +1009,20 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, SELECT (for union it can be only INSERT ... SELECT). */ additional_options&= ~OPTION_SETUP_TABLES_DONE; - if (!join) - goto err; - - thd_arg->lex->current_select= sl; - - can_skip_order_by= is_union_select && !(sl->braces && sl->explicit_limit); - - saved_error= join->prepare(sl->table_list.first, - sl->with_wild, - sl->where, - (can_skip_order_by ? 0 : - sl->order_list.elements) + - sl->group_list.elements, - can_skip_order_by ? - NULL : sl->order_list.first, - can_skip_order_by, - sl->group_list.first, - sl->having, - (is_union_select ? NULL : - thd_arg->lex->proc_list.first), - sl, this); - - /* There are no * in the statement anymore (for PS) */ - sl->with_wild= 0; - last_procedure= join->procedure; - - if (saved_error || (saved_error= thd_arg->is_fatal_error)) - goto err; - /* - Remove all references from the select_lex_units to the subqueries that - are inside the ORDER BY clause. - */ - if (can_skip_order_by) - { - for (ORDER *ord= (ORDER *)sl->order_list.first; ord; ord= ord->next) - { - (*ord->item)->walk(&Item::eliminate_subselect_processor, FALSE, NULL); - } - } /* Use items list of underlaid select for derived tables to preserve information about fields lengths and exact types */ - if (!is_union_select && !is_recursive) - types= first_sl->item_list; - else if (sl == first_sl) + if (sl == first_sl) { if (is_recursive) { if (derived->with->rename_columns_of_derived_unit(thd, this)) - goto err; + goto err; if (check_duplicate_names(thd, sl->item_list, 0)) goto err; } - types.empty(); - List_iterator_fast<Item> it(sl->item_list); - Item *item_tmp; - while ((item_tmp= it++)) - { - /* - If the outer query has a GROUP BY clause, an outer reference to this - query block may have been wrapped in a Item_outer_ref, which has not - been fixed yet. An Item_type_holder must be created based on a fixed - Item, so use the inner Item instead. - */ - DBUG_ASSERT(item_tmp->fixed || - (item_tmp->type() == Item::REF_ITEM && - ((Item_ref *)(item_tmp))->ref_type() == - Item_ref::OUTER_REF)); - if (!item_tmp->fixed) - item_tmp= item_tmp->real_item(); - - /* Error's in 'new' will be detected after loop */ - types.push_back(new (thd_arg->mem_root) - Item_type_holder(thd_arg, item_tmp)); - } - - if (thd_arg->is_fatal_error) - goto err; // out of memory - } - else - { - if (types.elements != sl->item_list.elements) - { - my_message(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, - ER_THD(thd, ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),MYF(0)); - goto err; - } - if (!is_rec_result_table_created) - { - List_iterator_fast<Item> it(sl->item_list); - List_iterator_fast<Item> tp(types); - Item *type, *item_tmp; - while ((type= tp++, item_tmp= it++)) - { - if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp)) - DBUG_RETURN(TRUE); - } - } } if (is_recursive) { @@ -662,11 +1035,15 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, ulonglong create_options; create_options= (first_sl->options | thd_arg->variables.option_bits | TMP_TABLE_ALL_COLUMNS); + // Join data types for all non-recursive parts of a recursive UNION + if (join_union_item_types(thd, types, union_part_count + 1)) + goto err; if (union_result->create_result_table(thd, &types, MY_TEST(union_distinct), create_options, derived->alias, false, - instantiate_tmp_table, false)) + instantiate_tmp_table, false, + 0)) goto err; if (!derived->table) derived->table= derived->derived_result->table= @@ -676,7 +1053,11 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, } } } + // In case of a non-recursive UNION, join data types for all UNION parts. + if (!is_recursive && join_union_item_types(thd, types, union_part_count)) + goto err; +cont: /* If the query is using select_union_direct, we have postponed preparation of the underlying select_result until column types @@ -747,12 +1128,48 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, if (global_parameters()->ftfunc_list->elements) create_options= create_options | TMP_TABLE_FORCE_MYISAM; - - if (!is_recursive && - union_result->create_result_table(thd, &types, MY_TEST(union_distinct), - create_options, "", false, - instantiate_tmp_table, false)) - goto err; + if (!is_recursive) + { + uint hidden= 0; + if (have_intersect) + { + hidden= 1; + if (!intersect_mark) + { + /* + For intersect we add a hidden column first that contains + the current select number of the time when the row was + added to the temporary table + */ + + Query_arena *arena, backup_arena; + arena= thd->activate_stmt_arena_if_needed(&backup_arena); + + intersect_mark= new (thd_arg->mem_root) Item_int(thd, 0); + + if (arena) + thd->restore_active_arena(arena, &backup_arena); + + if (!intersect_mark) + goto err; + } + else + intersect_mark->value= 0; //reset + types.push_front(union_result->intersect_mark= intersect_mark); + union_result->intersect_mark->name.str= "___"; + union_result->intersect_mark->name.length= 3; + } + bool error= + union_result->create_result_table(thd, &types, + MY_TEST(union_distinct), + create_options, "", false, + instantiate_tmp_table, false, + hidden); + if (intersect_mark) + types.pop(); + if (error) + goto err; + } if (fake_select_lex && !fake_select_lex->first_cond_optimization) { save_tablenr= result_table_list.tablenr_exec; @@ -778,6 +1195,9 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, arena= thd->activate_stmt_arena_if_needed(&backup_arena); saved_error= table->fill_item_list(&item_list); + // Item_list is inherited from 'types', so there could be the counter + if (intersect_mark) + item_list.pop(); // remove intersect counter if (arena) thd->restore_active_arena(arena, &backup_arena); @@ -831,7 +1251,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result, We're in execution of a prepared statement or stored procedure: reset field items to point at fields from the created temporary table. */ - table->reset_item_list(&item_list); + table->reset_item_list(&item_list, intersect_mark ? 1 : 0); } } @@ -968,6 +1388,8 @@ bool st_select_lex_unit::exec() { ha_rows records_at_start= 0; thd->lex->current_select= sl; + if (union_result) + union_result->change_select(); if (fake_select_lex) { if (sl != &thd->lex->select_lex) @@ -1359,10 +1781,10 @@ bool st_select_lex_unit::cleanup() void st_select_lex_unit::reinit_exec_mechanism() { - prepared= optimized= executed= 0; + prepared= optimized= optimized_2= executed= 0; optimize_started= 0; #ifndef DBUG_OFF - if (is_union()) + if (is_unit_op()) { List_iterator_fast<Item> it(item_list); Item *field; @@ -1444,7 +1866,7 @@ List<Item> *st_select_lex_unit::get_column_types(bool for_cursor) } - if (is_union()) + if (is_unit_op()) { DBUG_ASSERT(prepared); /* Types are generated during prepare */ diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 4488435491e..25fc1993536 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -140,7 +140,7 @@ static bool check_fields(THD *thd, List<Item> &items) if (!(field= item->field_for_view_update())) { /* item has name, because it comes from VIEW SELECT list */ - my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); + my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name.str); return TRUE; } /* @@ -1992,6 +1992,9 @@ loop_end: TABLE *tbl= table; do { + LEX_CSTRING field_name; + field_name.str= tbl->alias.c_ptr(); + field_name.length= strlen(field_name.str); /* Signal each table (including tables referenced by WITH CHECK OPTION clause) for which we will store row position in the temporary table @@ -2000,7 +2003,7 @@ loop_end: tbl->prepare_for_position(); Field_string *field= new Field_string(tbl->file->ref_length, 0, - tbl->alias.c_ptr(), + &field_name, &my_charset_bin); if (!field) DBUG_RETURN(1); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index d39dc739893..75d8841d25c 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -39,7 +39,7 @@ #define MD5_BUFF_LENGTH 33 -const LEX_STRING view_type= { C_STRING_WITH_LEN("VIEW") }; +const LEX_CSTRING view_type= { STRING_WITH_LEN("VIEW") }; static int mysql_register_view(THD *, TABLE_LIST *, enum_view_create_mode); @@ -63,9 +63,9 @@ static void make_unique_view_field_name(THD *thd, Item *target, List<Item> &item_list, Item *last_element) { - char *name= (target->orig_name ? - target->orig_name : - target->name); + const char *name= (target->orig_name ? + target->orig_name : + target->name.str); size_t name_len; uint attempt; char buff[NAME_LEN+1]; @@ -85,7 +85,7 @@ static void make_unique_view_field_name(THD *thd, Item *target, { check= itc++; if (check != target && - my_strcasecmp(system_charset_info, buff, check->name) == 0) + my_strcasecmp(system_charset_info, buff, check->name.str) == 0) { ok= FALSE; break; @@ -96,7 +96,7 @@ static void make_unique_view_field_name(THD *thd, Item *target, itc.rewind(); } - target->orig_name= target->name; + target->orig_name= target->name.str; target->set_name(thd, buff, name_len, system_charset_info); } @@ -140,7 +140,7 @@ bool check_duplicate_names(THD *thd, List<Item> &item_list, bool gen_unique_view itc.rewind(); while ((check= itc++) && check != item) { - if (my_strcasecmp(system_charset_info, item->name, check->name) == 0) + if (my_strcasecmp(system_charset_info, item->name.str, check->name.str) == 0) { if (!gen_unique_view_name) goto err; @@ -156,7 +156,7 @@ bool check_duplicate_names(THD *thd, List<Item> &item_list, bool gen_unique_view DBUG_RETURN(FALSE); err: - my_error(ER_DUP_FIELDNAME, MYF(0), item->name); + my_error(ER_DUP_FIELDNAME, MYF(0), item->name.str); DBUG_RETURN(TRUE); } @@ -178,10 +178,10 @@ void make_valid_column_names(THD *thd, List<Item> &item_list) for (uint column_no= 1; (item= it++); column_no++) { - if (!item->is_autogenerated_name || !check_column_name(item->name)) + if (!item->is_autogenerated_name || !check_column_name(item->name.str)) continue; name_len= my_snprintf(buff, NAME_LEN, "Name_exp_%u", column_no); - item->orig_name= item->name; + item->orig_name= item->name.str; item->set_name(thd, buff, name_len, system_charset_info); } @@ -225,10 +225,10 @@ fill_defined_view_parts (THD *thd, TABLE_LIST *view) view->definer.user= decoy.definer.user; lex->definer= &view->definer; } - if (lex->create_view_algorithm == VIEW_ALGORITHM_INHERIT) - lex->create_view_algorithm= (uint8) decoy.algorithm; - if (lex->create_view_suid == VIEW_SUID_DEFAULT) - lex->create_view_suid= decoy.view_suid ? + if (lex->create_view->algorithm == VIEW_ALGORITHM_INHERIT) + lex->create_view->algorithm= (uint8) decoy.algorithm; + if (lex->create_view->suid == VIEW_SUID_DEFAULT) + lex->create_view->suid= decoy.view_suid ? VIEW_SUID_DEFINER : VIEW_SUID_INVOKER; return FALSE; @@ -602,7 +602,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, { Item_field *fld= item->field_for_view_update(); uint priv= (get_column_grant(thd, &view->grant, view->db, - view->table_name, item->name) & + view->table_name, item->name.str) & VIEW_ANY_ACL); if (fld && !fld->field->table->s->tmp_table) @@ -620,7 +620,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, { my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "create view", thd->security_ctx->priv_user, - thd->security_ctx->priv_host, report_item->name, + thd->security_ctx->priv_host, report_item->name.str, view->table_name); res= TRUE; goto err; @@ -647,8 +647,8 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, { C_STRING_WITH_LEN("ALTER ") }, { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; - buff.append(command[thd->lex->create_view_mode].str, - command[thd->lex->create_view_mode].length); + buff.append(command[thd->lex->create_view->mode].str, + command[thd->lex->create_view->mode].length); view_store_options(thd, views, &buff); buff.append(STRING_WITH_LEN("VIEW ")); @@ -683,8 +683,14 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views, buff.append(views->source.str, views->source.length); int errcode= query_error_code(thd, TRUE); + /* + Don't log any unsafe warnings for CREATE VIEW as it's safely replicated + with statement based replication + */ + thd->reset_unsafe_warnings(); if (thd->binlog_query(THD::STMT_QUERY_TYPE, - buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcode)) + buff.ptr(), buff.length(), FALSE, FALSE, FALSE, + errcode)) res= TRUE; } @@ -705,11 +711,11 @@ err: } -static void make_view_filename(LEX_STRING *dir, char *dir_buff, +static void make_view_filename(LEX_CSTRING *dir, char *dir_buff, size_t dir_buff_len, - LEX_STRING *path, char *path_buff, + LEX_CSTRING *path, char *path_buff, size_t path_buff_len, - LEX_STRING *file, + LEX_CSTRING *file, TABLE_LIST *view) { /* print file name */ @@ -735,37 +741,37 @@ static const int required_view_parameters= 15; parse() */ static File_option view_parameters[]= -{{{ C_STRING_WITH_LEN("query")}, +{{{ STRING_WITH_LEN("query")}, my_offsetof(TABLE_LIST, select_stmt), FILE_OPTIONS_ESTRING}, - {{ C_STRING_WITH_LEN("md5")}, + {{ STRING_WITH_LEN("md5")}, my_offsetof(TABLE_LIST, md5), FILE_OPTIONS_STRING}, - {{ C_STRING_WITH_LEN("updatable")}, + {{ STRING_WITH_LEN("updatable")}, my_offsetof(TABLE_LIST, updatable_view), FILE_OPTIONS_ULONGLONG}, - {{ C_STRING_WITH_LEN("algorithm")}, + {{ STRING_WITH_LEN("algorithm")}, my_offsetof(TABLE_LIST, algorithm), FILE_OPTIONS_VIEW_ALGO}, - {{ C_STRING_WITH_LEN("definer_user")}, + {{ STRING_WITH_LEN("definer_user")}, my_offsetof(TABLE_LIST, definer.user), FILE_OPTIONS_STRING}, - {{ C_STRING_WITH_LEN("definer_host")}, + {{ STRING_WITH_LEN("definer_host")}, my_offsetof(TABLE_LIST, definer.host), FILE_OPTIONS_STRING}, - {{ C_STRING_WITH_LEN("suid")}, + {{ STRING_WITH_LEN("suid")}, my_offsetof(TABLE_LIST, view_suid), FILE_OPTIONS_ULONGLONG}, - {{ C_STRING_WITH_LEN("with_check_option")}, + {{ STRING_WITH_LEN("with_check_option")}, my_offsetof(TABLE_LIST, with_check), FILE_OPTIONS_ULONGLONG}, - {{ C_STRING_WITH_LEN("timestamp")}, + {{ STRING_WITH_LEN("timestamp")}, my_offsetof(TABLE_LIST, timestamp), FILE_OPTIONS_TIMESTAMP}, - {{ C_STRING_WITH_LEN("create-version")}, + {{ STRING_WITH_LEN("create-version")}, my_offsetof(TABLE_LIST, file_version), FILE_OPTIONS_ULONGLONG}, - {{ C_STRING_WITH_LEN("source")}, + {{ STRING_WITH_LEN("source")}, my_offsetof(TABLE_LIST, source), FILE_OPTIONS_ESTRING}, {{(char*) STRING_WITH_LEN("client_cs_name")}, @@ -777,21 +783,21 @@ static File_option view_parameters[]= {{(char*) STRING_WITH_LEN("view_body_utf8")}, my_offsetof(TABLE_LIST, view_body_utf8), FILE_OPTIONS_ESTRING}, - {{ C_STRING_WITH_LEN("mariadb-version")}, + {{ STRING_WITH_LEN("mariadb-version")}, my_offsetof(TABLE_LIST, mariadb_version), FILE_OPTIONS_ULONGLONG}, {{NullS, 0}, 0, FILE_OPTIONS_STRING} }; -static LEX_STRING view_file_type[]= {{(char*) STRING_WITH_LEN("VIEW") }}; +static LEX_CSTRING view_file_type[]= {{STRING_WITH_LEN("VIEW") }}; int mariadb_fix_view(THD *thd, TABLE_LIST *view, bool wrong_checksum, bool swap_alg) { char dir_buff[FN_REFLEN + 1], path_buff[FN_REFLEN + 1]; - LEX_STRING dir, file, path; + LEX_CSTRING dir, file, path; DBUG_ENTER("mariadb_fix_view"); if (!wrong_checksum && view->mariadb_version) @@ -906,7 +912,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, char md5[MD5_BUFF_LENGTH]; bool can_be_merged; char dir_buff[FN_REFLEN + 1], path_buff[FN_REFLEN + 1]; - LEX_STRING dir, file, path; + LEX_CSTRING dir, file, path; int error= 0; DBUG_ENTER("mysql_register_view"); @@ -928,7 +934,7 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, DBUG_PRINT("info", ("View: %.*s", view_query.length(), view_query.ptr())); /* fill structure */ - view->source= thd->lex->create_view_select; + view->source= thd->lex->create_view->select; if (!thd->make_lex_string(&view->select_stmt, view_query.ptr(), view_query.length())) @@ -953,18 +959,18 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view, } view->md5.length= 32; can_be_merged= lex->can_be_merged(); - if (lex->create_view_algorithm == VIEW_ALGORITHM_MERGE && + if (lex->create_view->algorithm == VIEW_ALGORITHM_MERGE && !lex->can_be_merged()) { push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_WARN_VIEW_MERGE, ER_THD(thd, ER_WARN_VIEW_MERGE)); - lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; + lex->create_view->algorithm= DTYPE_ALGORITHM_UNDEFINED; } - view->algorithm= lex->create_view_algorithm; + view->algorithm= lex->create_view->algorithm; view->definer.user= lex->definer->user; view->definer.host= lex->definer->host; - view->view_suid= lex->create_view_suid; - view->with_check= lex->create_view_check; + view->view_suid= lex->create_view->suid; + view->with_check= lex->create_view->check; DBUG_EXECUTE_IF("simulate_register_view_failure", { @@ -1008,14 +1014,14 @@ loop_out: /* check old .frm */ { char path_buff[FN_REFLEN]; - LEX_STRING path; + LEX_CSTRING path; File_parser *parser; path.str= path_buff; fn_format(path_buff, file.str, dir.str, "", MY_UNPACK_FILENAME); path.length= strlen(path_buff); - if (ha_table_exists(thd, view->db, view->table_name, NULL)) + if (ha_table_exists(thd, view->db, view->table_name)) { if (lex->create_info.if_not_exists()) { @@ -1095,7 +1101,7 @@ loop_out: UNION */ if (view->updatable_view && - !lex->select_lex.master_unit()->is_union() && + !lex->select_lex.master_unit()->is_unit_op() && !(lex->select_lex.table_list.first)->next_local && find_table_in_global_list(lex->query_tables->next_global, lex->query_tables->db, @@ -1153,7 +1159,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, DBUG_ENTER("mysql_make_view"); DBUG_PRINT("info", ("table: %p (%s)", table, table->table_name)); - if (table->required_type == FRMTYPE_TABLE) + if (table->required_type == TABLE_TYPE_NORMAL) { my_error(ER_WRONG_OBJECT, MYF(0), share->db.str, share->table_name.str, "BASE TABLE"); @@ -1327,7 +1333,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, { char old_db_buf[SAFE_NAME_LEN+1]; - LEX_STRING old_db= { old_db_buf, sizeof(old_db_buf) }; + LEX_CSTRING old_db= { old_db_buf, sizeof(old_db_buf) }; bool dbchanged; Parser_state parser_state; if (parser_state.init(thd, table->select_stmt.str, @@ -1338,7 +1344,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, Use view db name as thread default database, in order to ensure that the view is parsed and prepared correctly. */ - if ((result= mysql_opt_change_db(thd, &table->view_db, &old_db, 1, + if ((result= mysql_opt_change_db(thd, &table->view_db, + (LEX_STRING*) &old_db, 1, &dbchanged))) goto end; @@ -1357,7 +1364,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, * MODE_NO_UNSIGNED_SUBTRACTION affect execution - MODE_NO_DIR_IN_CREATE affect table creation only - MODE_POSTGRESQL compounded from other modes - - MODE_ORACLE compounded from other modes + - MODE_ORACLE affects Item creation (e.g for CONCAT) - MODE_MSSQL compounded from other modes - MODE_DB2 compounded from other modes - MODE_MAXDB affect only CREATE TABLE parsing @@ -1372,7 +1379,8 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, + MODE_NO_BACKSLASH_ESCAPES affect expression parsing */ thd->variables.sql_mode&= ~(MODE_PIPES_AS_CONCAT | MODE_ANSI_QUOTES | - MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES); + MODE_IGNORE_SPACE | MODE_NO_BACKSLASH_ESCAPES | + MODE_ORACLE); /* Parse the query. */ @@ -1541,7 +1549,9 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, */ for (tbl= view_main_select_tables; tbl; tbl= tbl->next_local) { - tbl->lock_type= table->lock_type; + /* We have to keep the lock type for sequence tables */ + if (!tbl->sequence) + tbl->lock_type= table->lock_type; tbl->mdl_request.set_type((tbl->lock_type >= TL_WRITE_ALLOW_WRITE) ? MDL_SHARED_WRITE : MDL_SHARED_READ); } @@ -1672,7 +1682,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, We can safely ignore the VIEW's ORDER BY if we merge into union branch, as order is not important there. */ - if (!table->select_lex->master_unit()->is_union() && + if (!table->select_lex->master_unit()->is_unit_op() && table->select_lex->order_list.elements == 0) table->select_lex->order_list.push_back(&lex->select_lex.order_list); else @@ -1680,7 +1690,7 @@ bool mysql_make_view(THD *thd, TABLE_SHARE *share, TABLE_LIST *table, if (old_lex->sql_command == SQLCOM_SELECT && (old_lex->describe & DESCRIBE_EXTENDED) && lex->select_lex.order_list.elements && - !table->select_lex->master_unit()->is_union()) + !table->select_lex->master_unit()->is_unit_op()) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, ER_VIEW_ORDERBY_IGNORED, @@ -1763,7 +1773,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) char path[FN_REFLEN + 1]; TABLE_LIST *view; String non_existant_views; - char *wrong_object_db= NULL, *wrong_object_name= NULL; + const char *wrong_object_db= NULL, *wrong_object_name= NULL; bool error= FALSE; bool some_views_deleted= FALSE; bool something_wrong= FALSE; @@ -1797,8 +1807,8 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) if (thd->lex->if_exists()) { push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, - ER_BAD_TABLE_ERROR, - ER_THD(thd, ER_BAD_TABLE_ERROR), + ER_UNKNOWN_VIEW, + ER_THD(thd, ER_UNKNOWN_VIEW), name); continue; } @@ -1840,7 +1850,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode) } if (non_existant_views.length()) { - my_error(ER_BAD_TABLE_ERROR, MYF(0), non_existant_views.c_ptr_safe()); + my_error(ER_UNKNOWN_VIEW, MYF(0), non_existant_views.c_ptr_safe()); } something_wrong= error || wrong_object_name || non_existant_views.length(); @@ -2128,7 +2138,7 @@ mysql_rename_view(THD *thd, const char *new_name, TABLE_LIST *view) { - LEX_STRING pathstr; + LEX_CSTRING pathstr; File_parser *parser; char path_buff[FN_REFLEN + 1]; bool error= TRUE; @@ -2144,7 +2154,7 @@ mysql_rename_view(THD *thd, { TABLE_LIST view_def; char dir_buff[FN_REFLEN + 1]; - LEX_STRING dir, file; + LEX_CSTRING dir, file; /* To be PS-friendly we should either to restore state of diff --git a/sql/sql_view.h b/sql/sql_view.h index b9eb92198f8..ff94d2de935 100644 --- a/sql/sql_view.h +++ b/sql/sql_view.h @@ -60,7 +60,7 @@ void make_valid_column_names(THD *thd, List<Item> &item_list); #define VIEW_ANY_ACL (SELECT_ACL | UPDATE_ACL | INSERT_ACL | DELETE_ACL) -extern const LEX_STRING view_type; +extern const LEX_CSTRING view_type; void make_valid_column_names(List<Item> &item_list); diff --git a/sql/sql_window.cc b/sql/sql_window.cc index 5fae2228646..3869628dfea 100644 --- a/sql/sql_window.cc +++ b/sql/sql_window.cc @@ -1,3 +1,4 @@ +#include "sql_parse.h" #include "sql_select.h" #include "sql_list.h" #include "item_windowfunc.h" @@ -12,13 +13,13 @@ Window_spec::check_window_names(List_iterator_fast<Window_spec> &it) { if (window_names_are_checked) return false; - char *name= this->name(); - char *ref_name= window_reference(); + const char *name= this->name(); + const char *ref_name= window_reference(); it.rewind(); Window_spec *win_spec; while((win_spec= it++) && win_spec != this) { - char *win_spec_name= win_spec->name(); + const char *win_spec_name= win_spec->name(); if (!win_spec_name) break; if (name && my_strcasecmp(system_charset_info, name, win_spec_name) == 0) @@ -307,6 +308,82 @@ setup_windows(THD *thd, Ref_ptr_array ref_pointer_array, TABLE_LIST *tables, DBUG_RETURN(0); } + +/** + @brief + Find fields common for all partition lists used in window functions + + @param thd The thread handle + + @details + This function looks for the field references in the partition lists + of all window functions used in this select that are common for + all the partition lists. The function returns an ORDER list contained + all such references.The list either is specially built by the function + or is taken directly from the first window specification. + + @retval + pointer to the first element of the ORDER list contained field + references common for all partition lists + 0 if no such reference is found. +*/ + +ORDER *st_select_lex::find_common_window_func_partition_fields(THD *thd) +{ + ORDER *ord; + Item *item; + DBUG_ASSERT(window_funcs.elements); + List_iterator_fast<Item_window_func> it(window_funcs); + Item_window_func *first_wf= it++; + if (!first_wf->window_spec->partition_list) + return 0; + List<Item> common_fields; + uint first_partition_elements= 0; + for (ord= first_wf->window_spec->partition_list->first; ord; ord= ord->next) + { + if ((*ord->item)->real_item()->type() == Item::FIELD_ITEM) + common_fields.push_back(*ord->item, thd->mem_root); + first_partition_elements++; + } + if (window_specs.elements == 1 && + common_fields.elements == first_partition_elements) + return first_wf->window_spec->partition_list->first; + List_iterator<Item> li(common_fields); + Item_window_func *wf; + while (common_fields.elements && (wf= it++)) + { + if (!wf->window_spec->partition_list) + return 0; + while ((item= li++)) + { + for (ord= wf->window_spec->partition_list->first; ord; ord= ord->next) + { + if (item->eq(*ord->item, false)) + break; + } + if (!ord) + li.remove(); + } + li.rewind(); + } + if (!common_fields.elements) + return 0; + if (common_fields.elements == first_partition_elements) + return first_wf->window_spec->partition_list->first; + SQL_I_List<ORDER> res_list; + for (ord= first_wf->window_spec->partition_list->first, item= li++; + ord; ord= ord->next) + { + if (item != *ord->item) + continue; + if (add_to_list(thd, res_list, item, ord->direction)) + return 0; + item= li++; + } + return res_list.first; +} + + ///////////////////////////////////////////////////////////////////////////// // Sorting window functions to minimize the number of table scans // performed during the computation of these functions @@ -327,7 +404,8 @@ int compare_order_elements(ORDER *ord1, ORDER *ord2) Item *item2= (*ord2->item)->real_item(); DBUG_ASSERT(item1->type() == Item::FIELD_ITEM && item2->type() == Item::FIELD_ITEM); - int cmp= ((Item_field *) item1)->field - ((Item_field *) item2)->field; + int cmp= ((Item_field *) item1)->field->field_index - + ((Item_field *) item2)->field->field_index; if (cmp == 0) { if (ord1->direction == ord2->direction) @@ -384,8 +462,8 @@ int compare_window_frame_bounds(Window_frame_bound *win_frame_bound1, return CMP_EQ; else { - res= strcmp(win_frame_bound1->offset->name, - win_frame_bound2->offset->name); + res= strcmp(win_frame_bound1->offset->name.str, + win_frame_bound2->offset->name.str); res= res > 0 ? CMP_GT : CMP_LT; if (is_bottom_bound) res= -res; diff --git a/sql/sql_window.h b/sql/sql_window.h index 6a56fc84392..ed1d9e36492 100644 --- a/sql/sql_window.h +++ b/sql/sql_window.h @@ -96,7 +96,7 @@ class Window_spec : public Sql_alloc public: virtual ~Window_spec() {} - LEX_STRING *window_ref; + LEX_CSTRING *window_ref; SQL_I_List<ORDER> *partition_list; @@ -106,7 +106,7 @@ class Window_spec : public Sql_alloc Window_spec *referenced_win_spec; - Window_spec(LEX_STRING *win_ref, + Window_spec(LEX_CSTRING *win_ref, SQL_I_List<ORDER> *part_list, SQL_I_List<ORDER> *ord_list, Window_frame *win_frame) @@ -114,11 +114,14 @@ class Window_spec : public Sql_alloc partition_list(part_list), order_list(ord_list), window_frame(win_frame), referenced_win_spec(NULL) {} - virtual char *name() { return NULL; } + virtual const char *name() { return NULL; } bool check_window_names(List_iterator_fast<Window_spec> &it); - char *window_reference() { return window_ref ? window_ref->str : NULL; } + const char *window_reference() + { + return window_ref ? window_ref->str : NULL; + } void join_partition_and_order_lists() { @@ -138,17 +141,17 @@ class Window_def : public Window_spec { public: - LEX_STRING *window_name; + LEX_CSTRING *window_name; - Window_def(LEX_STRING *win_name, - LEX_STRING *win_ref, + Window_def(LEX_CSTRING *win_name, + LEX_CSTRING *win_ref, SQL_I_List<ORDER> *part_list, SQL_I_List<ORDER> *ord_list, Window_frame *win_frame) : Window_spec(win_ref, part_list, ord_list, win_frame), window_name(win_name) {} - char *name() { return window_name->str; } + const char *name() { return window_name->str; } }; diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index e994ec5f2a5..17fcd83f681 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -65,6 +65,8 @@ #include "set_var.h" #include "rpl_mi.h" #include "lex_token.h" +#include "sql_lex.h" +#include "sql_sequence.h" /* this is to get the bison compilation windows warnings out */ #ifdef _MSC_VER @@ -98,7 +100,7 @@ int yylex(void *yylval, void *yythd); #define MYSQL_YYABORT_UNLESS(A) \ if (!(A)) \ { \ - my_parse_error(thd, ER_SYNTAX_ERROR); \ + thd->parse_error(); \ MYSQL_YYABORT; \ } @@ -111,43 +113,6 @@ int yylex(void *yylval, void *yythd); #define YYDEBUG 0 #endif -/** - @brief Push an error message into MySQL error stack with line - and position information. - - This function provides semantic action implementers with a way - to push the famous "You have a syntax error near..." error - message into the error stack, which is normally produced only if - a parse error is discovered internally by the Bison generated - parser. -*/ - -static void my_parse_error_intern(THD *thd, const char *err_text, - const char *yytext) -{ - Lex_input_stream *lip= &thd->m_parser_state->m_lip; - if (!yytext) - { - if (!(yytext= lip->get_tok_start())) - yytext= ""; - } - /* Push an error into the error stack */ - ErrConvString err(yytext, strlen(yytext), - thd->variables.character_set_client); - my_error(ER_PARSE_ERROR, MYF(0), err_text, err.ptr(), lip->yylineno); -} - - -static void my_parse_error(THD *thd, uint err_number, const char *yytext=0) -{ - return my_parse_error_intern(thd, ER_THD(thd, err_number), yytext); -} - -void LEX::parse_error() -{ - my_parse_error(thd, ER_SYNTAX_ERROR); -} - /** @brief Bison callback to report a syntax/OOM error @@ -164,7 +129,7 @@ void LEX::parse_error() This function is not for use in semantic actions and is internal to the parser, as it performs some pre-return cleanup. - In semantic actions, please use my_parse_error or my_error to + In semantic actions, please use thd->parse_error() or my_error to push an error into the error stack and MYSQL_YYABORT to abort from the parser. */ @@ -181,7 +146,7 @@ void MYSQLerror(THD *thd, const char *s) /* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */ if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0) s= ER_THD(thd, ER_SYNTAX_ERROR); - my_parse_error_intern(thd, s, 0); + thd->parse_error(s, 0); } @@ -207,80 +172,6 @@ void turn_parser_debug_on() } #endif -static bool is_native_function(THD *thd, const LEX_STRING *name) -{ - if (find_native_function_builder(thd, *name)) - return true; - - if (is_lex_native_function(name)) - return true; - - return false; -} - - -static sp_head *make_sp_head(THD *thd, sp_name *name, - enum stored_procedure_type type) -{ - LEX *lex= thd->lex; - sp_head *sp; - - /* Order is important here: new - reset - init */ - if ((sp= new sp_head())) - { - sp->reset_thd_mem_root(thd); - sp->init(lex); - sp->m_type= type; - if (name) - sp->init_sp_name(thd, name); - sp->m_chistics= &lex->sp_chistics; - lex->sphead= sp; - } - bzero(&lex->sp_chistics, sizeof(lex->sp_chistics)); - return sp; -} - -static bool maybe_start_compound_statement(THD *thd) -{ - if (!thd->lex->sphead) - { - if (!make_sp_head(thd, NULL, TYPE_ENUM_PROCEDURE)) - return 1; - - Lex->sp_chistics.suid= SP_IS_NOT_SUID; - Lex->sphead->set_body_start(thd, YYLIP->get_cpp_ptr()); - } - return 0; -} - -static bool push_sp_label(THD *thd, LEX_STRING label) -{ - sp_pcontext *ctx= thd->lex->spcont; - sp_label *lab= ctx->find_label(label); - - if (lab) - { - my_error(ER_SP_LABEL_REDEFINE, MYF(0), label.str); - return 1; - } - else - { - lab= thd->lex->spcont->push_label(thd, label, - thd->lex->sphead->instructions()); - lab->type= sp_label::ITERATION; - } - return 0; -} - -static bool push_sp_empty_label(THD *thd) -{ - if (maybe_start_compound_statement(thd)) - return 1; - /* Unlabeled controls get an empty label. */ - thd->lex->spcont->push_label(thd, empty_lex_str, - thd->lex->sphead->instructions()); - return 0; -} /** Helper action for a case expression statement (the expr in 'CASE expr'). @@ -290,22 +181,20 @@ static bool push_sp_empty_label(THD *thd) @return 0 on success */ -int case_stmt_action_expr(LEX *lex, Item* expr) +int LEX::case_stmt_action_expr(Item* expr) { - sp_head *sp= lex->sphead; - sp_pcontext *parsing_ctx= lex->spcont; - int case_expr_id= parsing_ctx->register_case_expr(); + int case_expr_id= spcont->register_case_expr(); sp_instr_set_case_expr *i; - if (parsing_ctx->push_case_expr_id(case_expr_id)) + if (spcont->push_case_expr_id(case_expr_id)) return 1; - i= new (lex->thd->mem_root) - sp_instr_set_case_expr(sp->instructions(), parsing_ctx, case_expr_id, expr, - lex); + i= new (thd->mem_root) + sp_instr_set_case_expr(sphead->instructions(), spcont, case_expr_id, expr, + this); - sp->add_cont_backpatch(i); - return sp->add_instr(i); + sphead->add_cont_backpatch(i); + return sphead->add_instr(i); } /** @@ -316,33 +205,30 @@ int case_stmt_action_expr(LEX *lex, Item* expr) @param simple true for simple cases, false for searched cases */ -int case_stmt_action_when(LEX *lex, Item *when, bool simple) +int LEX::case_stmt_action_when(Item *when, bool simple) { - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - uint ip= sp->instructions(); + uint ip= sphead->instructions(); sp_instr_jump_if_not *i; Item_case_expr *var; Item *expr; - THD *thd= lex->thd; if (simple) { var= new (thd->mem_root) - Item_case_expr(thd, ctx->get_current_case_expr_id()); + Item_case_expr(thd, spcont->get_current_case_expr_id()); #ifndef DBUG_OFF if (var) { - var->m_sp= sp; + var->m_sp= sphead; } #endif expr= new (thd->mem_root) Item_func_eq(thd, var, when); - i= new (thd->mem_root) sp_instr_jump_if_not(ip, ctx, expr, lex); + i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, expr, this); } else - i= new (thd->mem_root) sp_instr_jump_if_not(ip, ctx, when, lex); + i= new (thd->mem_root) sp_instr_jump_if_not(ip, spcont, when, this); /* BACKPATCH: Registering forward jump from @@ -350,10 +236,11 @@ int case_stmt_action_when(LEX *lex, Item *when, bool simple) (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example) */ - return !MY_TEST(i) || - sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0)) || - sp->add_cont_backpatch(i) || - sp->add_instr(i); + return + !MY_TEST(i) || + sphead->push_backpatch(thd, i, spcont->push_label(thd, &empty_clex_str, 0)) || + sphead->add_cont_backpatch(i) || + sphead->add_instr(i); } /** @@ -362,13 +249,11 @@ int case_stmt_action_when(LEX *lex, Item *when, bool simple) @param lex the parser lex context */ -int case_stmt_action_then(LEX *lex) +int LEX::case_stmt_action_then() { - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - uint ip= sp->instructions(); - sp_instr_jump *i= new (lex->thd->mem_root) sp_instr_jump(ip, ctx); - if (!MY_TEST(i) || sp->add_instr(i)) + uint ip= sphead->instructions(); + sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, spcont); + if (!MY_TEST(i) || sphead->add_instr(i)) return 1; /* @@ -377,7 +262,7 @@ int case_stmt_action_then(LEX *lex) (jump_if_not from instruction 2 to 5, 5 to 8 ... in the example) */ - sp->backpatch(ctx->pop_label()); + sphead->backpatch(spcont->pop_label()); /* BACKPATCH: Registering forward jump from @@ -385,18 +270,7 @@ int case_stmt_action_then(LEX *lex) (jump from instruction 4 to 12, 7 to 12 ... in the example) */ - return sp->push_backpatch(lex->thd, i, ctx->last_label()); -} - -static bool -find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp) -{ - tmp->var= find_sys_var(thd, tmp->base_name.str, tmp->base_name.length); - - if (tmp->var != NULL) - tmp->base_name= null_lex_str; - - return thd->is_error(); + return sphead->push_backpatch(thd, i, spcont->last_label()); } @@ -404,7 +278,6 @@ find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp) Helper action for a SET statement. Used to push a system variable into the assignment list. - @param thd the current thread @param tmp the system variable with base name @param var_type the scope of the variable @param val the value being assigned to the variable @@ -412,16 +285,15 @@ find_sys_var_null_base(THD *thd, struct sys_var_with_base *tmp) @return TRUE if error, FALSE otherwise. */ -static bool -set_system_variable(THD *thd, struct sys_var_with_base *tmp, - enum enum_var_type var_type, Item *val) +bool +LEX::set_system_variable(struct sys_var_with_base *tmp, + enum enum_var_type var_type, Item *val) { set_var *var; - LEX *lex= thd->lex; /* No AUTOCOMMIT from a stored function or trigger. */ - if (lex->spcont && tmp->var == Sys_autocommit_ptr) - lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + if (spcont && tmp->var == Sys_autocommit_ptr) + sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; if (val && val->type() == Item::FIELD_ITEM && ((Item_field*)val)->table_name) @@ -434,45 +306,7 @@ set_system_variable(THD *thd, struct sys_var_with_base *tmp, set_var(thd, var_type, tmp->var, &tmp->base_name, val))) return TRUE; - return lex->var_list.push_back(var, thd->mem_root); -} - - -/** - Helper action for a SET statement. - Used to push a SP local variable into the assignment list. - - @param thd the current thread - @param var_type the SP local variable - @param val the value being assigned to the variable - - @return TRUE if error, FALSE otherwise. -*/ - -static bool -set_local_variable(THD *thd, sp_variable *spv, Item *val) -{ - Item *it; - LEX *lex= thd->lex; - sp_instr_set *sp_set; - - if (val) - it= val; - else if (spv->default_value) - it= spv->default_value; - else - { - it= new (thd->mem_root) Item_null(thd); - if (it == NULL) - return TRUE; - } - - sp_set= new (thd->mem_root) - sp_instr_set(lex->sphead->instructions(), lex->spcont, - spv->offset, it, spv->sql_type(), - lex, TRUE); - - return (sp_set == NULL || lex->sphead->add_instr(sp_set)); + return var_list.push_back(var, thd->mem_root); } @@ -480,17 +314,14 @@ set_local_variable(THD *thd, sp_variable *spv, Item *val) Helper action for a SET statement. Used to SET a field of NEW row. - @param thd the current thread @param name the field name @param val the value being assigned to the row @return TRUE if error, FALSE otherwise. */ -static bool -set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val) +bool LEX::set_trigger_new_row(LEX_CSTRING *name, Item *val) { - LEX *lex= thd->lex; Item_trigger_field *trg_fld; sp_instr_set_trigger_field *sp_fld; @@ -498,22 +329,21 @@ set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val) if (! val) val= new (thd->mem_root) Item_null(thd); - DBUG_ASSERT(lex->trg_chistics.action_time == TRG_ACTION_BEFORE && - (lex->trg_chistics.event == TRG_EVENT_INSERT || - lex->trg_chistics.event == TRG_EVENT_UPDATE)); + DBUG_ASSERT(trg_chistics.action_time == TRG_ACTION_BEFORE && + (trg_chistics.event == TRG_EVENT_INSERT || + trg_chistics.event == TRG_EVENT_UPDATE)); trg_fld= new (thd->mem_root) - Item_trigger_field(thd, lex->current_context(), + Item_trigger_field(thd, current_context(), Item_trigger_field::NEW_ROW, - name->str, UPDATE_ACL, FALSE); + name, UPDATE_ACL, FALSE); if (trg_fld == NULL) return TRUE; sp_fld= new (thd->mem_root) - sp_instr_set_trigger_field(lex->sphead->instructions(), - lex->spcont, trg_fld, val, - lex); + sp_instr_set_trigger_field(sphead->instructions(), + spcont, trg_fld, val, this); if (sp_fld == NULL) return TRUE; @@ -522,16 +352,15 @@ set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val) Let us add this item to list of all Item_trigger_field objects in trigger. */ - lex->trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field); + trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field); - return lex->sphead->add_instr(sp_fld); + return sphead->add_instr(sp_fld); } /** Create an object to represent a SP variable in the Item-hierarchy. - @param thd The current thread. @param name The SP variable name. @param spvar The SP variable (optional). @param start_in_q Start position of the SP variable name in the query. @@ -543,29 +372,27 @@ set_trigger_new_row(THD *thd, LEX_STRING *name, Item *val) @return An Item_splocal object representing the SP variable, or NULL on error. */ -static Item_splocal* -create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable *spvar, - const char *start_in_q, const char *end_in_q) +Item_splocal* +LEX::create_item_for_sp_var(LEX_CSTRING *name, sp_variable *spvar, + const char *start_in_q, const char *end_in_q) { Item_splocal *item; - LEX *lex= thd->lex; uint pos_in_q, len_in_q; - sp_pcontext *spc = lex->spcont; /* If necessary, look for the variable. */ - if (spc && !spvar) - spvar= spc->find_variable(name, false); + if (spcont && !spvar) + spvar= spcont->find_variable(name, false); if (!spvar) { - my_error(ER_SP_UNDECLARED_VAR, MYF(0), name.str); + my_error(ER_SP_UNDECLARED_VAR, MYF(0), name->str); return NULL; } - DBUG_ASSERT(spc && spvar); + DBUG_ASSERT(spcont && spvar); /* Position and length of the SP variable name in the query. */ - pos_in_q= start_in_q - lex->sphead->m_tmp_query; + pos_in_q= start_in_q - sphead->m_tmp_query; len_in_q= end_in_q - start_in_q; item= new (thd->mem_root) @@ -574,7 +401,7 @@ create_item_for_sp_var(THD *thd, LEX_STRING name, sp_variable *spvar, #ifndef DBUG_OFF if (item) - item->m_sp= lex->sphead; + item->m_sp= sphead; #endif return item; @@ -673,58 +500,75 @@ Item* handle_sql2003_note184_exception(THD *thd, Item* left, bool equal, @return <code>false</code> if successful, <code>true</code> if an error was reported. In the latter case parsing should stop. */ -bool add_select_to_union_list(LEX *lex, bool is_union_distinct, - bool is_top_level) +bool LEX::add_select_to_union_list(bool is_union_distinct, + enum sub_select_type type, + bool is_top_level) { - /* + const char *type_name= (type == INTERSECT_TYPE ? "INTERSECT" : + (type == EXCEPT_TYPE ? "EXCEPT" : "UNION")); + /* Only the last SELECT can have INTO. Since the grammar won't allow INTO in a nested SELECT, we make this check only when creating a top-level SELECT. */ - if (is_top_level && lex->result) + if (is_top_level && result) { - my_error(ER_WRONG_USAGE, MYF(0), "UNION", "INTO"); + my_error(ER_WRONG_USAGE, MYF(0), type_name, "INTO"); return TRUE; } - if (lex->current_select->order_list.first && !lex->current_select->braces) + if (current_select->order_list.first && !current_select->braces) { - my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY"); + my_error(ER_WRONG_USAGE, MYF(0), type_name, "ORDER BY"); return TRUE; } - if (lex->current_select->explicit_limit && !lex->current_select->braces) + if (current_select->explicit_limit && !current_select->braces) + { + my_error(ER_WRONG_USAGE, MYF(0), type_name, "LIMIT"); + return TRUE; + } + if (current_select->linkage == GLOBAL_OPTIONS_TYPE) { - my_error(ER_WRONG_USAGE, MYF(0), "UNION", "LIMIT"); + thd->parse_error(); return TRUE; } - if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + if (!is_union_distinct && (type == INTERSECT_TYPE || type == EXCEPT_TYPE)) { - my_parse_error(lex->thd, ER_SYNTAX_ERROR); + my_error(ER_WRONG_USAGE, MYF(0), type_name, "ALL"); return TRUE; } + /* + Priority implementation, but also trying to keep things as flat + as possible */ + if (type == INTERSECT_TYPE && + (current_select->linkage != INTERSECT_TYPE && + current_select != current_select->master_unit()->first_select())) + { + /* + This and previous SELECTs should go one level down because of + priority + */ + SELECT_LEX *prev= exclude_last_select(); + if (add_unit_in_brackets(prev)) + return TRUE; + return add_select_to_union_list(is_union_distinct, type, 0); + } + else + { + check_automatic_up(type); + } /* This counter shouldn't be incremented for UNION parts */ - lex->nest_level--; - if (mysql_new_select(lex, 0)) + nest_level--; + if (mysql_new_select(this, 0, NULL)) return TRUE; - mysql_init_select(lex); - lex->current_select->linkage=UNION_TYPE; + mysql_init_select(this); + current_select->linkage= type; if (is_union_distinct) /* UNION DISTINCT - remember position */ - lex->current_select->master_unit()->union_distinct= - lex->current_select; - return FALSE; -} - - -static bool add_create_index_prepare(LEX *lex, Table_ident *table) -{ - lex->sql_command= SQLCOM_CREATE_INDEX; - if (!lex->current_select->add_table_to_list(lex->thd, table, NULL, - TL_OPTION_UPDATING, - TL_READ_NO_INSERT, - MDL_SHARED_UPGRADABLE)) - return TRUE; - lex->alter_info.reset(); - lex->alter_info.flags= Alter_info::ALTER_ADD_INDEX; - lex->option_list= NULL; + { + current_select->master_unit()->union_distinct= + current_select; + } + else + DBUG_ASSERT(type == UNION_TYPE); return FALSE; } @@ -747,7 +591,7 @@ static bool add_create_index_prepare(LEX *lex, Table_ident *table) @param no_lookahead True if the parser has no lookahead */ -static void sp_create_assignment_lex(THD *thd, bool no_lookahead) +void sp_create_assignment_lex(THD *thd, bool no_lookahead) { LEX *lex= thd->lex; @@ -785,7 +629,7 @@ static void sp_create_assignment_lex(THD *thd, bool no_lookahead) @return false if success, true otherwise. */ -static bool sp_create_assignment_instr(THD *thd, bool no_lookahead) +bool sp_create_assignment_instr(THD *thd, bool no_lookahead) { LEX *lex= thd->lex; @@ -838,50 +682,33 @@ static bool sp_create_assignment_instr(THD *thd, bool no_lookahead) return false; } - -static void add_key_to_list(LEX *lex, LEX_STRING *field_name, - enum Key::Keytype type, bool check_exists) +void LEX::add_key_to_list(LEX_CSTRING *field_name, + enum Key::Keytype type, bool check_exists) { Key *key; - MEM_ROOT *mem_root= lex->thd->mem_root; + MEM_ROOT *mem_root= thd->mem_root; key= new (mem_root) - Key(type, null_lex_str, HA_KEY_ALG_UNDEF, false, + Key(type, &null_clex_str, HA_KEY_ALG_UNDEF, false, DDL_options(check_exists ? DDL_options::OPT_IF_NOT_EXISTS : DDL_options::OPT_NONE)); - key->columns.push_back(new (mem_root) Key_part_spec(*field_name, 0), + key->columns.push_back(new (mem_root) Key_part_spec(field_name, 0), mem_root); - lex->alter_info.key_list.push_back(key, mem_root); + alter_info.key_list.push_back(key, mem_root); } -void LEX::init_last_field(Column_definition *field, const char *field_name, - CHARSET_INFO *cs) +void LEX::init_last_field(Column_definition *field, + const LEX_CSTRING *field_name, + const CHARSET_INFO *cs) { last_field= field; - field->field_name= field_name; + field->field_name= *field_name; /* reset LEX fields that are used in Create_field::set_and_check() */ charset= cs; } -void LEX::set_last_field_type(const Lex_field_type_st &type) -{ - last_field->sql_type= type.field_type(); - last_field->charset= charset; - - if (type.length()) - { - int err; - last_field->length= my_strtoll10(type.length(), NULL, &err); - if (err) - last_field->length= ~0ULL; // safety - } - else - last_field->length= 0; - - last_field->decimals= type.dec() ? (uint)atoi(type.dec()) : 0; -} bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) { @@ -929,19 +756,33 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) ulong ulong_num; ulonglong ulonglong_number; longlong longlong_number; + uint sp_instr_addr; /* structs */ - LEX_STRING lex_str; + LEX_CSTRING lex_str; LEX_SYMBOL symbol; + Lex_string_with_metadata_st lex_string_with_metadata; struct sys_var_with_base variable; - struct { int vars, conds, hndlrs, curs; } spblock; + Lex_string_with_pos_st lex_string_with_pos; + Lex_spblock_st spblock; + Lex_spblock_handlers_st spblock_handlers; Lex_length_and_dec_st Lex_length_and_dec; Lex_cast_type_st Lex_cast_type; Lex_field_type_st Lex_field_type; Lex_dyncol_type_st Lex_dyncol_type; + Lex_for_loop_st for_loop; + Lex_for_loop_bounds_st for_loop_bounds; + struct + { + LEX_CSTRING name; + uint offset; + } sp_cursor_name_and_offset; /* pointers */ Create_field *create_field; + Spvar_definition *spvar_definition; + Row_definition_list *spvar_definition_list; + const Type_handler *type_handler; CHARSET_INFO *charset; Condition_information_item *cond_info_item; DYNCALL_CREATE_DEF *dyncol_def; @@ -951,25 +792,28 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) Item_param *item_param; Key_part_spec *key_part; LEX *lex; - LEX_STRING *lex_str_ptr; + sp_assignment_lex *assignment_lex; + class sp_lex_cursor *sp_cursor_stmt; + LEX_CSTRING *lex_str_ptr; LEX_USER *lex_user; List<Condition_information_item> *cond_info_list; List<DYNCALL_CREATE_DEF> *dyncol_def_list; List<Item> *item_list; + List<sp_assignment_lex> *sp_assignment_lex_list; List<Statement_information_item> *stmt_info_list; List<String> *string_list; - List<LEX_STRING> *lex_str_list; + List<LEX_CSTRING> *lex_str_list; Statement_information_item *stmt_info_item; String *string; TABLE_LIST *table_list; Table_ident *table; + Qualified_column_ident *qualified_column_ident; char *simple_string; const char *const_simple_string; chooser_compare_func_creator boolfunc2creator; class my_var *myvar; class sp_condition_value *spcondvalue; class sp_head *sphead; - class sp_label *splabel; class sp_name *spname; class sp_variable *spvar; class With_clause *with_clause; @@ -984,6 +828,8 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) st_trg_execution_order trg_execution_order; /* enums */ + enum enum_view_suid view_suid; + enum sub_select_type unit_type; enum Condition_information_item::Name cond_info_item_name; enum enum_diag_condition_item_name diag_condition_item_name; enum Diagnostics_information::Which_area diag_area; @@ -992,7 +838,6 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr) enum Item_udftype udf_type; enum Key::Keytype key_type; enum Statement_information_item::Name stmt_info_item_name; - enum enum_field_types field_type; enum enum_filetype filetype; enum enum_tx_isolation tx_isolation; enum enum_var_type var_type; @@ -1030,7 +875,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); Comments for TOKENS. For each token, please include in the same line a comment that contains the following tags: - SQL-2011-N : Non Reserved keywird as per SQL-2011 + SQL-2011-N : Non Reserved keyword as per SQL-2011 SQL-2003-R : Reserved keyword as per SQL-2003 SQL-2003-N : Non Reserved keyword as per SQL-2003 SQL-1999-R : Reserved keyword as per SQL-1999 @@ -1112,6 +957,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CLASS_ORIGIN_SYM /* SQL-2003-N */ %token CLIENT_SYM %token CLOSE_SYM /* SQL-2003-R */ +%token CLOB /* SQL-2003-R */ %token COALESCE /* SQL-2003-N */ %token CODE_SYM %token COLLATE_SYM /* SQL-2003-R */ @@ -1157,12 +1003,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CURSOR_SYM /* SQL-2003-R */ %token CURSOR_NAME_SYM /* SQL-2003-N */ %token CURTIME /* MYSQL-FUNC */ +%token CYCLE_SYM %token DATABASE %token DATABASES %token DATAFILE_SYM %token DATA_SYM /* SQL-2003-N */ %token DATETIME %token DATE_ADD_INTERVAL /* MYSQL-FUNC */ +%token DATE_FORMAT_SYM /* MYSQL-FUNC */ %token DATE_SUB_INTERVAL /* MYSQL-FUNC */ %token DATE_SYM /* SQL-2003-R */ %token DAY_HOUR_SYM @@ -1174,6 +1022,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token DECIMAL_NUM %token DECIMAL_SYM /* SQL-2003-R */ %token DECLARE_SYM /* SQL-2003-R */ +%token DECODE_SYM /* Oracle function, non-reserved */ %token DEFAULT /* SQL-2003-R */ %token DEFINER_SYM %token DELAYED_SYM @@ -1194,6 +1043,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token DOUBLE_SYM /* SQL-2003-R */ %token DO_DOMAIN_IDS_SYM %token DO_SYM +%token DOT_DOT_SYM %token DROP /* SQL-2003-R */ %token DUAL_SYM %token DUMPFILE @@ -1202,6 +1052,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token EACH_SYM /* SQL-2003-R */ %token ELSE /* SQL-2003-R */ %token ELSEIF_SYM +%token ELSIF_SYM /* Oracle, reserved in PL/SQL*/ %token ENABLE_SYM %token ENCLOSED %token END /* SQL-2003-R */ @@ -1220,8 +1071,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token EVERY_SYM /* SQL-2003-N */ %token EXCHANGE_SYM %token EXAMINED_SYM +%token EXCEPT_SYM /* SQL-2003-R */ %token EXCLUDE_SYM /* SQL-2011-N */ %token EXECUTE_SYM /* SQL-2003-R */ +%token EXCEPTION_SYM /* SQL-2003-N, Oracle-PLSQL-R */ %token EXISTS /* SQL-2003-R */ %token EXIT_SYM %token EXPANSION_SYM @@ -1259,6 +1112,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token GET_FORMAT /* MYSQL-FUNC */ %token GET_SYM /* SQL-2003-R */ %token GLOBAL_SYM /* SQL-2003-R */ +%token GOTO_SYM /* Oracle, reserved in PL/SQL*/ %token GRANT /* SQL-2003-R */ %token GRANTS %token GROUP_SYM /* SQL-2003-R */ @@ -1289,6 +1143,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IGNORE_SERVER_IDS_SYM %token IMMEDIATE_SYM /* SQL-2003-R */ %token IMPORT +%token INCREMENT_SYM %token INDEXES %token INDEX_SYM %token INFILE @@ -1299,6 +1154,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token INSERT /* SQL-2003-R */ %token INSERT_METHOD %token INSTALL_SYM +%token INTERSECT_SYM /* SQL-2003-R */ %token INTERVAL_SYM /* SQL-2003-R */ %token INTO /* SQL-2003-R */ %token INT_SYM /* SQL-2003-R */ @@ -1308,6 +1164,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IPC_SYM %token IS /* SQL-2003-R */ %token ISOLATION /* SQL-2003-R */ +%token ISOPEN_SYM /* Oracle-N */ %token ISSUER_SYM %token ITERATE_SYM %token JOIN_SYM /* SQL-2003-R */ @@ -1319,6 +1176,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token LANGUAGE_SYM /* SQL-2003-R */ %token LAST_SYM /* SQL-2003-N */ %token LAST_VALUE +%token LASTVAL_SYM /* PostgreSQL sequence function */ %token LE /* OPERATOR */ %token LEADING /* SQL-2003-R */ %token LEAVES @@ -1377,7 +1235,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MAX_UPDATES_PER_HOUR %token MAX_STATEMENT_TIME_SYM %token MAX_USER_CONNECTIONS_SYM -%token MAX_VALUE_SYM /* SQL-2003-N */ +%token MAXVALUE_SYM /* SQL-2003-N */ %token MEDIUMBLOB %token MEDIUMINT %token MEDIUMTEXT @@ -1390,6 +1248,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token MINUTE_MICROSECOND_SYM %token MINUTE_SECOND_SYM %token MINUTE_SYM /* SQL-2003-R */ +%token MINVALUE_SYM %token MIN_ROWS %token MIN_SYM /* SQL-2003-N */ %token MODE_SYM @@ -1413,12 +1272,18 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NEG %token NEW_SYM /* SQL-2003-R */ %token NEXT_SYM /* SQL-2003-N */ +%token NEXTVAL_SYM /* PostgreSQL sequence function */ +%token NOCACHE_SYM +%token NOCYCLE_SYM %token NODEGROUP_SYM %token NONE_SYM /* SQL-2003-R */ %token NOT2_SYM %token NOT_SYM /* SQL-2003-R */ +%token NOTFOUND_SYM /* Oracle-R */ %token NOW_SYM %token NO_SYM /* SQL-2003-R */ +%token NOMAXVALUE_SYM +%token NOMINVALUE_SYM %token NO_WAIT_SYM %token NO_WRITE_TO_BINLOG %token NTILE_SYM @@ -1428,6 +1293,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NUMERIC_SYM /* SQL-2003-R */ %token NTH_VALUE_SYM /* SQL-2011 */ %token NVARCHAR_SYM +%token OF_SYM /* SQL-1992-R, Oracle-R */ %token OFFSET_SYM %token OLD_PASSWORD_SYM %token ON /* SQL-2003-R */ @@ -1475,6 +1341,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PREPARE_SYM /* SQL-2003-R */ %token PRESERVE_SYM %token PREV_SYM +%token PREVIOUS_SYM %token PRIMARY_SYM /* SQL-2003-R */ %token PRIVILEGES /* SQL-2003-N */ %token PROCEDURE_SYM /* SQL-2003-R */ @@ -1487,8 +1354,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token QUARTER_SYM %token QUERY_SYM %token QUICK +%token RAISE_SYM /* Oracle-PLSQL-R */ %token RANGE_SYM /* SQL-2003-R */ %token RANK_SYM +%token RAW /* Oracle */ %token READS_SYM /* SQL-2003-R */ %token READ_ONLY_SYM %token READ_SYM /* SQL-2003-N */ @@ -1519,6 +1388,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token REPLICATION %token REQUIRE_SYM %token RESET_SYM +%token RESTART_SYM %token RESIGNAL_SYM /* SQL-2003-R */ %token RESOURCES %token RESTORE_SYM @@ -1528,6 +1398,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token RETURNING_SYM %token RETURNS_SYM /* SQL-2003-R */ %token RETURN_SYM /* SQL-2003-R */ +%token REUSE_SYM /* Oracle-R */ %token REVERSE_SYM %token REVOKE /* SQL-2003-R */ %token RIGHT /* SQL-2003-R */ @@ -1535,8 +1406,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token ROLLBACK_SYM /* SQL-2003-R */ %token ROLLUP_SYM /* SQL-2003-R */ %token ROUTINE_SYM /* SQL-2003-N */ +%token ROWCOUNT_SYM /* Oracle-N */ %token ROW_SYM /* SQL-2003-R */ %token ROWS_SYM /* SQL-2003-R */ +%token ROWTYPE_SYM /* Oracle-PLSQL-R */ %token ROW_COUNT_SYM /* SQL-2003-N */ %token ROW_FORMAT_SYM %token ROW_NUMBER_SYM @@ -1550,12 +1423,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SELECT_SYM /* SQL-2003-R */ %token SENSITIVE_SYM /* FUTURE-USE */ %token SEPARATOR_SYM +%token SEQUENCE_SYM %token SERIALIZABLE_SYM /* SQL-2003-N */ %token SERIAL_SYM %token SESSION_SYM /* SQL-2003-N */ %token SERVER_SYM %token SERVER_OPTIONS %token SET /* SQL-2003-R */ +%token SETVAL_SYM /* PostgreSQL sequence function */ %token SET_VAR %token SHARE_SYM %token SHIFT_LEFT /* OPERATOR */ @@ -1681,6 +1556,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token VALUE_SYM /* SQL-2003-R */ %token VARBINARY %token VARCHAR /* SQL-2003-R */ +%token VARCHAR2 /* Oracle */ %token VARIABLES %token VARIANCE_SYM %token VARYING /* SQL-2003-R */ @@ -1733,35 +1609,51 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %left INTERVAL_SYM %type <lex_str> - IDENT IDENT_QUOTED TEXT_STRING DECIMAL_NUM FLOAT_NUM NUM LONG_NUM + IDENT IDENT_QUOTED DECIMAL_NUM FLOAT_NUM NUM LONG_NUM HEX_NUM HEX_STRING - LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text + LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident_or_text IDENT_sys TEXT_STRING_sys TEXT_STRING_literal - NCHAR_STRING opt_component key_cache_name + opt_component key_cache_name sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty opt_constraint constraint opt_ident + sp_decl_ident + sp_block_label opt_place opt_db + +%type <lex_string_with_metadata> + TEXT_STRING + NCHAR_STRING %type <lex_str_ptr> opt_table_alias +%type <lex_string_with_pos> + ident ident_with_tok_start + %type <table> table_ident table_ident_nodb references xid table_ident_opt_wild create_like +%type <qualified_column_ident> + optionally_qualified_column_ident + %type <simple_string> - remember_name remember_end opt_db remember_tok_start + remember_name remember_end remember_tok_start wild_and_where - field_length opt_field_length opt_field_length_default_1 %type <const_simple_string> - opt_place + field_length opt_field_length opt_field_length_default_1 %type <string> text_string hex_or_bin_String opt_gconcat_separator -%type <field_type> int_type real_type +%type <type_handler> int_type real_type %type <Lex_field_type> type_with_opt_collate field_type + field_type_numeric + field_type_string + field_type_lob + field_type_temporal + field_type_misc %type <Lex_dyncol_type> opt_dyncol_type dyncol_type numeric_dyncol_type temporal_dyncol_type string_dyncol_type @@ -1788,7 +1680,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_recursive %type <object_ddl_options> - create_or_replace + create_or_replace opt_if_not_exists opt_if_exists @@ -1810,11 +1702,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ws_nweights ws_level_flag_desc ws_level_flag_reverse ws_level_flags opt_ws_levels ws_level_list ws_level_list_item ws_level_number - ws_level_range ws_level_list_or_range + ws_level_range ws_level_list_or_range bool %type <ulonglong_number> ulonglong_num real_ulonglong_num size_number +%type <longlong_number> + longlong_num + %type <choice> choice %type <lock_type> @@ -1822,7 +1717,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <item> literal text_literal insert_ident order_ident temporal_literal - simple_ident expr opt_expr opt_else sum_expr in_sum_expr + simple_ident expr sum_expr in_sum_expr variable variable_aux bool_pri predicate bit_expr parenthesized_expr table_wild simple_expr column_default_non_parenthesized_expr udf_expr @@ -1830,7 +1725,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); geometry_function signed_literal expr_or_literal opt_escape sp_opt_default - simple_ident_nospvar simple_ident_q + simple_ident_nospvar simple_ident_q simple_ident_q2 field_or_var limit_option part_func_expr window_func_expr @@ -1850,9 +1745,21 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); NUM_literal %type <item_list> - expr_list opt_udf_expr_list udf_expr_list when_list + expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else ident_list ident_list_arg opt_expr_list +%type <sp_cursor_stmt> + sp_cursor_stmt_lex + sp_cursor_stmt + +%type <assignment_lex> + assignment_source_lex + assignment_source_expr + +%type <sp_assignment_lex_list> + cursor_actual_parameters + opt_parenthesized_cursor_actual_parameters + %type <var_type> option_type opt_var_type opt_var_ident_type @@ -1893,6 +1800,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <Lex_length_and_dec> precision opt_precision float_options %type <symbol> keyword keyword_sp + keyword_sp_data_type + keyword_sp_not_data_type %type <lex_user> user grant_user grant_role user_or_role current_role admin_option_for_role user_maybe_role @@ -1929,6 +1838,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <virtual_column> opt_check_constraint check_constraint virtual_column_func column_default_expr +%type <unit_type> unit_type_decl %type <NONE> analyze_stmt_command @@ -1977,11 +1887,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); statement sp_suid sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa opt_field_or_var_spec fields_or_vars opt_load_data_set_spec - view_algorithm view_or_trigger_or_sp_or_event - definer_tail no_definer_tail - view_suid view_tail view_list_opt view_list view_select - view_check_option trigger_tail sp_tail sf_tail event_tail - udf_tail udf_tail2 + view_list_opt view_list view_select + trigger_tail sp_tail sf_tail event_tail + udf_tail create_function_tail install uninstall partition_entry binlog_base64_event normal_key_options normal_key_opts all_key_opt spatial_key_options fulltext_key_options normal_key_opt @@ -2010,13 +1918,15 @@ END_OF_INPUT %type <NONE> case_stmt_specification %type <NONE> loop_body while_body repeat_body -%type <num> sp_decl_idents sp_handler_type sp_hcond_list +%type <num> view_algorithm view_check_option +%type <view_suid> view_suid opt_view_suid + +%type <num> sp_decl_idents sp_decl_idents_init_vars +%type <num> sp_handler_type sp_hcond_list %type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value -%type <spblock> sp_decls sp_decl -%type <lex> sp_cursor_stmt +%type <spblock> sp_decls sp_decl sp_decl_body sp_decl_variable_list %type <spname> sp_name -%type <splabel> sp_block_content -%type <spvar> sp_param_name_and_type +%type <spvar> sp_param_name sp_param_name_and_type %type <spvar_mode> sp_opt_inout %type <index_hint> index_hint_type %type <num> index_hint_clause normal_join inner_join @@ -2037,6 +1947,9 @@ END_OF_INPUT %type <cond_info_item_name> condition_information_item_name; %type <cond_info_list> condition_information; +%type <spvar_definition> row_field_name row_field_definition +%type <spvar_definition_list> row_field_definition_list row_type_body + %type <NONE> opt_window_clause window_def_list window_def window_spec %type <lex_str_ptr> window_name %type <NONE> opt_window_ref opt_window_frame_clause @@ -2333,7 +2246,7 @@ master_def: if ($3 > MASTER_DELAY_MAX) { my_error(ER_MASTER_DELAY_VALUE_OUT_OF_RANGE, MYF(0), - $3, MASTER_DELAY_MAX); + (ulong) $3, (ulong) MASTER_DELAY_MAX); } else Lex->mi.sql_delay = $3; @@ -2513,7 +2426,7 @@ optional_connection_name: /* empty */ { LEX *lex= thd->lex; - lex->mi.connection_name= null_lex_str; + lex->mi.connection_name= null_clex_str; } | connection_name ; @@ -2549,7 +2462,7 @@ create: */ lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; lex->create_info.default_table_charset= NULL; - lex->name= null_lex_str; + lex->name= null_clex_str; lex->create_last_non_select_table= lex->last_table(); } create_body @@ -2568,13 +2481,71 @@ create: } create_table_set_open_action_and_adjust_tables(lex); } + | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident + { + LEX *lex= thd->lex; + lex->create_info.init(); + if (lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4)) + MYSQL_YYABORT; + + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + + /* + For CREATE TABLE, an non-existing table is not an error. + Instruct open_tables() to just take an MDL lock if the + table does not exist. + */ + lex->alter_info.reset(); + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + lex->name= null_clex_str; + lex->create_last_non_select_table= lex->last_table(); + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition())) + MYSQL_YYABORT; + } + opt_sequence opt_create_table_options + { + LEX *lex= thd->lex; + + if (lex->create_info.seq_create_info->check_and_adjust(1)) + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), + lex->select_lex.table_list.first->db, + lex->select_lex.table_list.first->table_name); + MYSQL_YYABORT; + } + + /* No fields specified, generate them */ + if (prepare_sequence_fields(thd, &lex->alter_info.create_list)) + MYSQL_YYABORT; + + /* CREATE SEQUENCE always creates a sequence */ + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= 1; + + lex->current_select= &lex->select_lex; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.use_default_db_type(thd); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_USING_OTHER_HANDLER, + ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), + hton_name(lex->create_info.db_type)->str, + $5->table.str); + } + create_table_set_open_action_and_adjust_tables(lex); + } | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident opt_key_algorithm_clause ON table_ident { - if (add_create_index_prepare(Lex, $8)) + if (Lex->add_create_index_prepare($8)) MYSQL_YYABORT; - if (Lex->add_create_index($2, $5, $6, $1 | $4)) + if (Lex->add_create_index($2, &$5, $6, $1 | $4)) MYSQL_YYABORT; } '(' key_list ')' normal_key_options @@ -2582,9 +2553,9 @@ create: | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident ON table_ident { - if (add_create_index_prepare(Lex, $7)) + if (Lex->add_create_index_prepare($7)) MYSQL_YYABORT; - if (Lex->add_create_index($2, $5, HA_KEY_ALG_UNDEF, $1 | $4)) + if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } '(' key_list ')' fulltext_key_options @@ -2592,9 +2563,9 @@ create: | create_or_replace spatial INDEX_SYM opt_if_not_exists ident ON table_ident { - if (add_create_index_prepare(Lex, $7)) + if (Lex->add_create_index_prepare($7)) MYSQL_YYABORT; - if (Lex->add_create_index($2, $5, HA_KEY_ALG_UNDEF, $1 | $4)) + if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) MYSQL_YYABORT; } '(' key_list ')' spatial_key_options @@ -2611,15 +2582,50 @@ create: MYSQL_YYABORT; lex->name= $4; } - | create_or_replace + | create_or_replace definer_opt opt_view_suid VIEW_SYM + opt_if_not_exists table_ident + { + if (Lex->add_create_view(thd, $1 | $5, + DTYPE_ALGORITHM_UNDEFINED, $3, $6)) + MYSQL_YYABORT; + } + view_list_opt AS view_select + { } + | create_or_replace view_algorithm definer_opt opt_view_suid VIEW_SYM + opt_if_not_exists table_ident + { + if (Lex->add_create_view(thd, $1 | $6, $2, $4, $7)) + MYSQL_YYABORT; + } + view_list_opt AS view_select + { } + | create_or_replace definer_opt TRIGGER_SYM + { Lex->create_info.set($1); } + trigger_tail + { } + | create_or_replace definer_opt PROCEDURE_SYM + { Lex->create_info.set($1); } + sp_tail + { } + | create_or_replace definer_opt EVENT_SYM + { Lex->create_info.set($1); } + event_tail + { } + | create_or_replace definer FUNCTION_SYM + { Lex->create_info.set($1); } + sf_tail + { } + | create_or_replace no_definer FUNCTION_SYM + { Lex->create_info.set($1); } + create_function_tail + { } + | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM { Lex->create_info.set($1); - Lex->create_view_mode= ($1.or_replace() ? VIEW_CREATE_OR_REPLACE : - VIEW_CREATE_NEW); - Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; - Lex->create_view_suid= TRUE; + Lex->udf.type= UDFTYPE_AGGREGATE; } - view_or_trigger_or_sp_or_event { } + udf_tail + { } | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list opt_require_clause opt_resource_options { @@ -2645,6 +2651,107 @@ create: { } ; +create_function_tail: + sf_tail { } + | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; } + ; + +opt_sequence: + /* empty */ { } + | sequence_defs + ; + +sequence_defs: + sequence_def + | sequence_defs sequence_def + ; + +sequence_def: + MINVALUE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->min_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | NO_SYM MINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | NOMINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | MAXVALUE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->max_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | NO_SYM MAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | NOMAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | START_SYM opt_with longlong_num + { + Lex->create_info.seq_create_info->start= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_start; + } + | INCREMENT_SYM opt_by longlong_num + { + Lex->create_info.seq_create_info->increment= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_increment; + } + | CACHE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->cache= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_cache; + } + | NOCACHE_SYM + { + Lex->create_info.seq_create_info->cache= 0; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_cache; + } + | CYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 1; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_cycle; + } + | NOCYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 0; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_cycle; + } + | RESTART_SYM + { + if (Lex->sql_command != SQLCOM_ALTER_SEQUENCE) + { + thd->parse_error(ER_SYNTAX_ERROR, "RESTART"); + YYABORT; + } + Lex->create_info.seq_create_info->used_fields|= seq_field_used_restart; + } + | RESTART_SYM opt_with longlong_num + { + if (Lex->sql_command != SQLCOM_ALTER_SEQUENCE) + { + thd->parse_error(ER_SYNTAX_ERROR, "RESTART"); + YYABORT; + } + Lex->create_info.seq_create_info->restart= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_restart | seq_field_used_restart_value; + } + ; + server_def: SERVER_SYM opt_if_not_exists ident_or_text { @@ -2672,7 +2779,8 @@ server_option: { MYSQL_YYABORT_UNLESS(Lex->server_options.host.str == 0); Lex->server_options.host= $2; - my_casedn_str(system_charset_info, Lex->server_options.host.str); + my_casedn_str(system_charset_info, + (char*) Lex->server_options.host.str); } | DATABASE TEXT_STRING_sys { @@ -2701,16 +2809,16 @@ server_option: ; event_tail: - remember_name EVENT_SYM opt_if_not_exists sp_name + remember_name opt_if_not_exists sp_name { LEX *lex=Lex; lex->stmt_definition_begin= $1; - if (lex->add_create_options_with_check($3)) + if (lex->add_create_options_with_check($2)) MYSQL_YYABORT; if (!(lex->event_parse_data= Event_parse_data::new_instance(thd))) MYSQL_YYABORT; - lex->event_parse_data->identifier= $4; + lex->event_parse_data->identifier= $3; lex->event_parse_data->on_completion= Event_parse_data::ON_COMPLETION_DROP; @@ -2838,10 +2946,10 @@ ev_sql_stmt: if (lex->sphead) my_yyabort_error((ER_EVENT_RECURSION_FORBIDDEN, MYF(0))); - if (!make_sp_head(thd, lex->event_parse_data->identifier, TYPE_ENUM_PROCEDURE)) + if (!lex->make_sp_head(thd, lex->event_parse_data->identifier, + &sp_handler_procedure)) MYSQL_YYABORT; - lex->sp_chistics.suid= SP_IS_SUID; //always the definer! lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); } sp_proc_stmt @@ -2874,29 +2982,13 @@ clear_privileges: sp_name: ident '.' ident { - if (!$1.str || check_db_name(&$1)) - my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str)); - if (check_routine_name(&$3)) + if (!($$= Lex->make_sp_name(thd, &$1, &$3))) MYSQL_YYABORT; - $$= new (thd->mem_root) sp_name($1, $3, true); - if ($$ == NULL) - MYSQL_YYABORT; - $$->init_qname(thd); } | ident { - LEX *lex= thd->lex; - LEX_STRING db; - if (check_routine_name(&$1)) - { - MYSQL_YYABORT; - } - if (lex->copy_db_to(&db.str, &db.length)) - MYSQL_YYABORT; - $$= new (thd->mem_root) sp_name(db, $1, false); - if ($$ == NULL) + if (!($$= Lex->make_sp_name(thd, &$1))) MYSQL_YYABORT; - $$->init_qname(thd); } ; @@ -2948,12 +3040,8 @@ sp_suid: call: CALL_SYM sp_name { - LEX *lex = Lex; - - lex->sql_command= SQLCOM_CALL; - lex->spname= $2; - lex->value_list.empty(); - sp_add_used_routine(lex, thd, $2, TYPE_ENUM_PROCEDURE); + if (Lex->call_statement_start(thd, $2)) + MYSQL_YYABORT; } opt_sp_cparam_list {} ; @@ -2991,34 +3079,44 @@ sp_fdparams: | sp_param_name_and_type ; -sp_param_name_and_type: +sp_param_name: ident { - LEX *lex= Lex; - sp_pcontext *spc= lex->spcont; - - if (spc->find_variable($1, TRUE)) - my_yyabort_error((ER_SP_DUP_PARAM, MYF(0), $1.str)); - - sp_variable *spvar= spc->add_variable(thd, $1); + if (!($$= Lex->sp_param_init(&$1))) + MYSQL_YYABORT; + } + ; - lex->init_last_field(&spvar->field_def, $1.str, - thd->variables.collation_database); - $<spvar>$= spvar; +sp_param_name_and_type: + sp_param_name type_with_opt_collate + { + if (Lex->sp_param_fill_definition($$= $1)) + MYSQL_YYABORT; } - type_with_opt_collate + | sp_param_name TYPE_SYM OF_SYM ident '.' ident { - LEX *lex= Lex; - sp_variable *spvar= $<spvar>2; - - if (lex->sphead->fill_field_definition(thd, lex, lex->last_field)) - { + if (Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $4, $6)) + MYSQL_YYABORT; + } + | sp_param_name TYPE_SYM OF_SYM ident '.' ident '.' ident + { + if (Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $4, $6, $8)) + MYSQL_YYABORT; + } + | sp_param_name ROW_SYM TYPE_SYM OF_SYM ident + { + if (Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $5)) + MYSQL_YYABORT; + } + | sp_param_name ROW_SYM TYPE_SYM OF_SYM ident '.' ident + { + if (Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $5, $7)) + MYSQL_YYABORT; + } + | sp_param_name ROW_SYM row_type_body + { + if (Lex->sphead->spvar_fill_row(thd, $$= $1, $3)) MYSQL_YYABORT; - } - spvar->field_def.field_name= spvar->name.str; - spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; - - $$= spvar; } ; @@ -3044,6 +3142,30 @@ sp_opt_inout: | INOUT_SYM { $$= sp_variable::MODE_INOUT; } ; +sp_parenthesized_fdparam_list: + '(' + { + Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start() + 1; + } + sp_fdparam_list + ')' + { + Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start(); + } + ; + +sp_parenthesized_pdparam_list: + '(' + { + Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start() + 1; + } + sp_pdparam_list + ')' + { + Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start(); + } + ; + sp_proc_stmts: /* Empty */ {} | sp_proc_stmts sp_proc_stmt ';' @@ -3057,7 +3179,7 @@ sp_proc_stmts1: sp_decls: /* Empty */ { - $$.vars= $$.conds= $$.hndlrs= $$.curs= 0; + $$.init(); } | sp_decls sp_decl ';' { @@ -3065,196 +3187,180 @@ sp_decls: because letting the grammar rules reflect it caused tricky shift/reduce conflicts with the wrong result. (And we get better error handling this way.) */ - if (($2.vars || $2.conds) && ($1.curs || $1.hndlrs)) - my_yyabort_error((ER_SP_VARCOND_AFTER_CURSHNDLR, MYF(0))); - if ($2.curs && $1.hndlrs) - my_yyabort_error((ER_SP_CURSOR_AFTER_HANDLER, MYF(0))); - $$.vars= $1.vars + $2.vars; - $$.conds= $1.conds + $2.conds; - $$.hndlrs= $1.hndlrs + $2.hndlrs; - $$.curs= $1.curs + $2.curs; + if (Lex->sp_declarations_join(&$$, $1, $2)) + MYSQL_YYABORT; } ; sp_decl: - DECLARE_SYM sp_decl_idents - { - LEX *lex= Lex; - sp_pcontext *pctx= lex->spcont; + DECLARE_SYM sp_decl_body { $$= $2; } + ; - // get the last variable: - uint num_vars= pctx->context_var_count(); - uint var_idx= pctx->var_context2runtime(num_vars - 1); - sp_variable *spvar= pctx->find_variable(var_idx); - lex->sphead->reset_lex(thd); - pctx->declare_var_boundary($2); - thd->lex->init_last_field(&spvar->field_def, spvar->name.str, - thd->variables.collation_database); +optionally_qualified_column_ident: + sp_decl_ident + { + if (!($$= new (thd->mem_root) Qualified_column_ident(&$1))) + MYSQL_YYABORT; } - type_with_opt_collate - sp_opt_default + | sp_decl_ident '.' ident { - LEX *lex= Lex; - sp_pcontext *pctx= lex->spcont; - uint num_vars= pctx->context_var_count(); - Item *dflt_value_item= $5; - - if (!dflt_value_item) - { - dflt_value_item= new (thd->mem_root) Item_null(thd); - if (dflt_value_item == NULL) - MYSQL_YYABORT; - /* QQ Set to the var_type with null_value? */ - } - - for (uint i = num_vars-$2 ; i < num_vars ; i++) - { - uint var_idx= pctx->var_context2runtime(i); - sp_variable *spvar= pctx->find_variable(var_idx); - bool last= i == num_vars - 1; - - if (!spvar) - MYSQL_YYABORT; - - if (!last) - spvar->field_def= *lex->last_field; - - spvar->default_value= dflt_value_item; - spvar->field_def.field_name= spvar->name.str; - - if (lex->sphead->fill_field_definition(thd, lex, - &spvar->field_def)) - { - MYSQL_YYABORT; - } - - spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL; - - /* The last instruction is responsible for freeing LEX. */ - - sp_instr_set *is= new (lex->thd->mem_root) - sp_instr_set(lex->sphead->instructions(), - pctx, var_idx, dflt_value_item, - $4.field_type(), lex, last); - if (is == NULL || lex->sphead->add_instr(is)) - MYSQL_YYABORT; - } - - pctx->declare_var_boundary(0); - if (lex->sphead->restore_lex(thd)) + if (!($$= new (thd->mem_root) Qualified_column_ident(&$1, &$3))) MYSQL_YYABORT; - $$.vars= $2; - $$.conds= $$.hndlrs= $$.curs= 0; } - | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond + | sp_decl_ident '.' ident '.' ident { - LEX *lex= Lex; - sp_pcontext *spc= lex->spcont; - - if (spc->find_condition($2, TRUE)) - my_yyabort_error((ER_SP_DUP_COND, MYF(0), $2.str)); - if(spc->add_condition(thd, $2, $5)) + if (!($$= new (thd->mem_root) Qualified_column_ident(thd, + &$1, &$3, &$5))) MYSQL_YYABORT; - $$.vars= $$.hndlrs= $$.curs= 0; - $$.conds= 1; } - | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM + ; + +row_field_name: + ident { - LEX *lex= Lex; - sp_head *sp= lex->sphead; + if (check_string_char_length(&$1, 0, NAME_CHAR_LEN, + system_charset_info, 1)) + my_yyabort_error((ER_TOO_LONG_IDENT, MYF(0), $1.str)); + if (!($$= new (thd->mem_root) Spvar_definition())) + MYSQL_YYABORT; + Lex->init_last_field($$, &$1, thd->variables.collation_database); + } + ; - sp_handler *h= lex->spcont->add_handler(thd, - (sp_handler::enum_type) $2); +row_field_definition: + row_field_name type_with_opt_collate + ; - lex->spcont= lex->spcont->push_context(thd, - sp_pcontext::HANDLER_SCOPE); +row_field_definition_list: + row_field_definition + { + if (!($$= new (thd->mem_root) Row_definition_list())) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | row_field_definition_list ',' row_field_definition + { + uint unused; + if ($1->find_row_field_by_name(&$3->field_name, &unused)) + my_yyabort_error((ER_DUP_FIELDNAME, MYF(0), $3->field_name.str)); + $$= $1; + $$->push_back($3, thd->mem_root); + } + ; - sp_pcontext *ctx= lex->spcont; - sp_instr_hpush_jump *i= - new (thd->mem_root) sp_instr_hpush_jump(sp->instructions(), - ctx, h); +row_type_body: + '(' row_field_definition_list ')' { $$= $2; } + ; - if (i == NULL || sp->add_instr(i)) - MYSQL_YYABORT; +sp_decl_idents_init_vars: + sp_decl_idents + { + Lex->sp_variable_declarations_init(thd, $1); + } + ; - /* For continue handlers, mark end of handler scope. */ - if ($2 == sp_handler::CONTINUE && - sp->push_backpatch(thd, i, ctx->last_label())) +sp_decl_variable_list: + sp_decl_idents_init_vars + type_with_opt_collate + sp_opt_default + { + if (Lex->sp_variable_declarations_finalize(thd, $1, + &Lex->last_field[0], $3)) MYSQL_YYABORT; + $$.init_using_vars($1); + } + | sp_decl_idents_init_vars + TYPE_SYM OF_SYM optionally_qualified_column_ident + sp_opt_default + { + if (Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $4, $5)) + MYSQL_YYABORT; + $$.init_using_vars($1); + } + | sp_decl_idents_init_vars + ROW_SYM TYPE_SYM OF_SYM optionally_qualified_column_ident + sp_opt_default + { + if (Lex->sp_variable_declarations_rowtype_finalize(thd, $1, $5, $6)) + MYSQL_YYABORT; + $$.init_using_vars($1); + } + | sp_decl_idents_init_vars + ROW_SYM row_type_body + sp_opt_default + { + if (Lex->sp_variable_declarations_row_finalize(thd, $1, $3, $4)) + MYSQL_YYABORT; + $$.init_using_vars($1); + } + ; - if (sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0))) +sp_decl_body: + sp_decl_variable_list + | sp_decl_ident CONDITION_SYM FOR_SYM sp_cond + { + if (Lex->spcont->declare_condition(thd, &$1, $4)) + MYSQL_YYABORT; + $$.vars= $$.hndlrs= $$.curs= 0; + $$.conds= 1; + } + | sp_handler_type HANDLER_SYM FOR_SYM + { + if (Lex->sp_handler_declaration_init(thd, $1)) MYSQL_YYABORT; } sp_hcond_list sp_proc_stmt { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label *hlab= lex->spcont->pop_label(); /* After this hdlr */ - sp_instr_hreturn *i; - - if ($2 == sp_handler::CONTINUE) - { - i= new (thd->mem_root) - sp_instr_hreturn(sp->instructions(), ctx); - if (i == NULL || - sp->add_instr(i)) - MYSQL_YYABORT; - } - else - { /* EXIT or UNDO handler, just jump to the end of the block */ - i= new (thd->mem_root) - sp_instr_hreturn(sp->instructions(), ctx); - if (i == NULL || - sp->add_instr(i) || - sp->push_backpatch(thd, i, lex->spcont->last_label())) /* Block end */ - MYSQL_YYABORT; - } - lex->sphead->backpatch(hlab); - - lex->spcont= ctx->pop_context(); - + if (Lex->sp_handler_declaration_finalize(thd, $1)) + MYSQL_YYABORT; $$.vars= $$.conds= $$.curs= 0; $$.hndlrs= 1; } - | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt + | sp_decl_ident CURSOR_SYM { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - uint offp; - sp_instr_cpush *i; - - if (ctx->find_cursor($2, &offp, TRUE)) - my_yyabort_error((ER_SP_DUP_CURS, MYF(0), $2.str)); - - i= new (thd->mem_root) - sp_instr_cpush(sp->instructions(), ctx, $5, - ctx->current_cursor_count()); - if (i == NULL || sp->add_instr(i) || ctx->add_cursor($2)) + Lex->sp_block_init(thd); + } + opt_parenthesized_cursor_formal_parameters + FOR_SYM sp_cursor_stmt + { + sp_pcontext *param_ctx= Lex->spcont; + if (Lex->sp_block_finalize(thd)) + MYSQL_YYABORT; + if (Lex->sp_declare_cursor(thd, &$1, $6, param_ctx, true)) MYSQL_YYABORT; $$.vars= $$.conds= $$.hndlrs= 0; $$.curs= 1; } ; +opt_parenthesized_cursor_formal_parameters: + /* Empty */ + | '(' sp_fdparams ')' + ; + + +sp_cursor_stmt_lex: + { + DBUG_ASSERT(thd->lex->sphead); + if (!($$= new (thd->mem_root) sp_lex_cursor(thd, thd->lex))) + MYSQL_YYABORT; + } + ; + sp_cursor_stmt: + sp_cursor_stmt_lex { - Lex->sphead->reset_lex(thd); + DBUG_ASSERT(thd->free_list == NULL); + Lex->sphead->reset_lex(thd, $1); } select { - LEX *lex= Lex; - - DBUG_ASSERT(lex->sql_command == SQLCOM_SELECT); - - if (lex->result) - my_yyabort_error((ER_SP_BAD_CURSOR_SELECT, MYF(0))); - lex->sp_lex_in_use= TRUE; - $$= lex; - if (lex->sphead->restore_lex(thd)) + DBUG_ASSERT(Lex == $1); + if ($1->stmt_finalize(thd) || + $1->sphead->restore_lex(thd)) MYSQL_YYABORT; + $$= $1; } ; @@ -3329,7 +3435,7 @@ sp_hcond: } | ident /* CONDITION name */ { - $$= Lex->spcont->find_condition($1, false); + $$= Lex->spcont->find_condition(&$1, false); if ($$ == NULL) my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str)); } @@ -3356,13 +3462,7 @@ sp_hcond: signal_stmt: SIGNAL_SYM signal_value opt_set_signal_information { - LEX *lex= thd->lex; - Yacc_state *state= & thd->m_parser_state->m_yacc; - - lex->sql_command= SQLCOM_SIGNAL; - lex->m_sql_cmd= - new (thd->mem_root) Sql_cmd_signal($2, state->m_set_signal_info); - if (lex->m_sql_cmd == NULL) + if (Lex->add_signal_statement(thd, $2)) MYSQL_YYABORT; } ; @@ -3376,7 +3476,7 @@ signal_value: /* SIGNAL foo cannot be used outside of stored programs */ if (lex->spcont == NULL) my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str)); - cond= lex->spcont->find_condition($1, false); + cond= lex->spcont->find_condition(&$1, false); if (cond == NULL) my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str)); if (cond->type != sp_condition_value::SQLSTATE) @@ -3442,7 +3542,7 @@ signal_allowed_expr: SIGNAL/RESIGNAL ... SET <signal condition item name> = @foo := expr */ - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -3483,14 +3583,7 @@ signal_condition_information_item_name: resignal_stmt: RESIGNAL_SYM opt_signal_value opt_set_signal_information { - LEX *lex= thd->lex; - Yacc_state *state= & thd->m_parser_state->m_yacc; - - lex->sql_command= SQLCOM_RESIGNAL; - lex->m_sql_cmd= - new (thd->mem_root) Sql_cmd_resignal($2, - state->m_set_signal_info); - if (lex->m_sql_cmd == NULL) + if (Lex->add_resignal_statement(thd, $2)) MYSQL_YYABORT; } ; @@ -3559,15 +3652,15 @@ simple_target_specification: ident { Lex_input_stream *lip= &thd->m_parser_state->m_lip; - $$= create_item_for_sp_var(thd, $1, NULL, - lip->get_tok_start(), lip->get_ptr()); - + $$= thd->lex->create_item_for_sp_var(&$1, NULL, + lip->get_tok_start(), + lip->get_ptr()); if ($$ == NULL) MYSQL_YYABORT; } | '@' ident_or_text { - $$= new (thd->mem_root) Item_func_get_user_var(thd, $2); + $$= new (thd->mem_root) Item_func_get_user_var(thd, &$2); if ($$ == NULL) MYSQL_YYABORT; } @@ -3641,17 +3734,21 @@ condition_information_item_name: { $$= Condition_information_item::RETURNED_SQLSTATE; } ; +sp_decl_ident: + ident { $$= $1; } + ; + sp_decl_idents: - ident + sp_decl_ident { /* NOTE: field definition is filled in sp_decl section. */ LEX *lex= Lex; sp_pcontext *spc= lex->spcont; - if (spc->find_variable($1, TRUE)) + if (spc->find_variable(&$1, TRUE)) my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $1.str)); - spc->add_variable(thd, $1); + spc->add_variable(thd, &$1); $$= 1; } | sp_decl_idents ',' ident @@ -3661,9 +3758,9 @@ sp_decl_idents: LEX *lex= Lex; sp_pcontext *spc= lex->spcont; - if (spc->find_variable($3, TRUE)) + if (spc->find_variable(&$3, TRUE)) my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $3.str)); - spc->add_variable(thd, $3); + spc->add_variable(thd, &$3); $$= $1 + 1; } ; @@ -3710,7 +3807,7 @@ sp_proc_stmt_compound_ok: sp_proc_stmt_if: IF_SYM { - if (maybe_start_compound_statement(thd)) + if (Lex->maybe_start_compound_statement(thd)) MYSQL_YYABORT; Lex->sphead->new_cont_backpatch(NULL); } @@ -3777,20 +3874,9 @@ sp_proc_stmt_return: { LEX *lex= Lex; sp_head *sp= lex->sphead; - - if (sp->m_type != TYPE_ENUM_FUNCTION) - my_yyabort_error((ER_SP_BADRETURN, MYF(0))); - - sp_instr_freturn *i; - - i= new (thd->mem_root) - sp_instr_freturn(sp->instructions(), lex->spcont, $3, - sp->m_return_field_def.sql_type, lex); - if (i == NULL || sp->add_instr(i)) - MYSQL_YYABORT; - sp->m_flags|= sp_head::HAS_RETURN; - - if (sp->restore_lex(thd)) + if (sp->m_handler->add_instr_freturn(thd, sp, lex->spcont, + $3, lex) || + sp->restore_lex(thd)) MYSQL_YYABORT; } ; @@ -3798,130 +3884,92 @@ sp_proc_stmt_return: sp_proc_stmt_leave: LEAVE_SYM label_ident { - LEX *lex= Lex; - sp_head *sp = lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label *lab= ctx->find_label($2); - - if (! lab) - my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "LEAVE", $2.str)); - - sp_instr_jump *i; - uint ip= sp->instructions(); - uint n; - /* - When jumping to a BEGIN-END block end, the target jump - points to the block hpop/cpop cleanup instructions, - so we should exclude the block context here. - When jumping to something else (i.e., SP_LAB_ITER), - there are no hpop/cpop at the jump destination, - so we should include the block context here for cleanup. - */ - bool exclusive= (lab->type == sp_label::BEGIN); - - n= ctx->diff_handlers(lab->ctx, exclusive); - if (n) - { - sp_instr_hpop *hpop= new (thd->mem_root) - sp_instr_hpop(ip++, ctx, n); - if (hpop == NULL) - MYSQL_YYABORT; - sp->add_instr(hpop); - } - n= ctx->diff_cursors(lab->ctx, exclusive); - if (n) - { - sp_instr_cpop *cpop= new (thd->mem_root) - sp_instr_cpop(ip++, ctx, n); - if (cpop == NULL) - MYSQL_YYABORT; - sp->add_instr(cpop); - } - i= new (thd->mem_root) sp_instr_jump(ip, ctx); - if (i == NULL) + if (Lex->sp_leave_statement(thd, &$2)) MYSQL_YYABORT; - sp->push_backpatch(thd, i, lab); /* Jumping forward */ - sp->add_instr(i); } ; sp_proc_stmt_iterate: ITERATE_SYM label_ident { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_label *lab= ctx->find_label($2); + if (Lex->sp_iterate_statement(thd, &$2)) + MYSQL_YYABORT; + } + ; - if (! lab || lab->type != sp_label::ITERATION) - my_yyabort_error((ER_SP_LILABEL_MISMATCH, MYF(0), "ITERATE", $2.str)); +assignment_source_lex: + { + DBUG_ASSERT(Lex->sphead); + if (!($$= new (thd->mem_root) sp_assignment_lex(thd, thd->lex))) + MYSQL_YYABORT; + } + ; - sp_instr_jump *i; - uint ip= sp->instructions(); - uint n; +assignment_source_expr: + assignment_source_lex + { + DBUG_ASSERT(thd->free_list == NULL); + Lex->sphead->reset_lex(thd, $1); + } + expr + { + DBUG_ASSERT($1 == thd->lex); + $$= $1; + $$->sp_lex_in_use= true; + $$->set_item_and_free_list($3, thd->free_list); + thd->free_list= NULL; + if ($$->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + ; - n= ctx->diff_handlers(lab->ctx, FALSE); /* Inclusive the dest. */ - if (n) - { - sp_instr_hpop *hpop= new (thd->mem_root) - sp_instr_hpop(ip++, ctx, n); - if (hpop == NULL || - sp->add_instr(hpop)) - MYSQL_YYABORT; - } - n= ctx->diff_cursors(lab->ctx, FALSE); /* Inclusive the dest. */ - if (n) - { - sp_instr_cpop *cpop= new (thd->mem_root) - sp_instr_cpop(ip++, ctx, n); - if (cpop == NULL || - sp->add_instr(cpop)) - MYSQL_YYABORT; - } - i= new (thd->mem_root) - sp_instr_jump(ip, ctx, lab->ip); /* Jump back */ - if (i == NULL || - sp->add_instr(i)) +cursor_actual_parameters: + assignment_source_expr + { + if (!($$= new (thd->mem_root) List<sp_assignment_lex>)) MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | cursor_actual_parameters ',' assignment_source_expr + { + $$= $1; + $$->push_back($3, thd->mem_root); } ; +opt_parenthesized_cursor_actual_parameters: + /* Empty */ { $$= NULL; } + | '(' cursor_actual_parameters ')' { $$= $2; } + ; + sp_proc_stmt_open: - OPEN_SYM ident + OPEN_SYM ident opt_parenthesized_cursor_actual_parameters { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint offset; - sp_instr_copen *i; - - if (! lex->spcont->find_cursor($2, &offset, false)) - my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $2.str)); - i= new (thd->mem_root) - sp_instr_copen(sp->instructions(), lex->spcont, offset); - if (i == NULL || - sp->add_instr(i)) + if (Lex->sp_open_cursor(thd, &$2, $3)) MYSQL_YYABORT; } ; -sp_proc_stmt_fetch: - FETCH_SYM sp_opt_fetch_noise ident INTO +sp_proc_stmt_fetch_head: + FETCH_SYM ident INTO { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint offset; - sp_instr_cfetch *i; - - if (! lex->spcont->find_cursor($3, &offset, false)) - my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $3.str)); - i= new (thd->mem_root) - sp_instr_cfetch(sp->instructions(), lex->spcont, offset); - if (i == NULL || - sp->add_instr(i)) + if (Lex->sp_add_cfetch(thd, &$2)) MYSQL_YYABORT; } - sp_fetch_list - {} + | FETCH_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, &$3)) + MYSQL_YYABORT; + } + | FETCH_SYM NEXT_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, &$4)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_fetch: + sp_proc_stmt_fetch_head sp_fetch_list { } ; sp_proc_stmt_close: @@ -3932,7 +3980,7 @@ sp_proc_stmt_close: uint offset; sp_instr_cclose *i; - if (! lex->spcont->find_cursor($2, &offset, false)) + if (! lex->spcont->find_cursor(&$2, &offset, false)) my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $2.str)); i= new (thd->mem_root) sp_instr_cclose(sp->instructions(), lex->spcont, offset); @@ -3942,12 +3990,6 @@ sp_proc_stmt_close: } ; -sp_opt_fetch_noise: - /* Empty */ - | NEXT_SYM FROM - | FROM - ; - sp_fetch_list: ident { @@ -3956,7 +3998,7 @@ sp_fetch_list: sp_pcontext *spc= lex->spcont; sp_variable *spv; - if (!spc || !(spv = spc->find_variable($1, false))) + if (!spc || !(spv = spc->find_variable(&$1, false))) my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str)); /* An SP local variable */ @@ -3970,7 +4012,7 @@ sp_fetch_list: sp_pcontext *spc= lex->spcont; sp_variable *spv; - if (!spc || !(spv = spc->find_variable($3, false))) + if (!spc || !(spv = spc->find_variable(&$3, false))) my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $3.str)); /* An SP local variable */ @@ -3990,7 +4032,7 @@ sp_if: sp_instr_jump_if_not *i= new (thd->mem_root) sp_instr_jump_if_not(ip, ctx, $2, lex); if (i == NULL || - sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0)) || + sp->push_backpatch(thd, i, ctx->push_label(thd, &empty_clex_str, 0)) || sp->add_cont_backpatch(i) || sp->add_instr(i)) MYSQL_YYABORT; @@ -4007,7 +4049,7 @@ sp_if: sp->add_instr(i)) MYSQL_YYABORT; sp->backpatch(ctx->pop_label()); - sp->push_backpatch(thd, i, ctx->push_label(thd, empty_lex_str, 0)); + sp->push_backpatch(thd, i, ctx->push_label(thd, &empty_clex_str, 0)); } sp_elseifs { @@ -4026,7 +4068,7 @@ sp_elseifs: case_stmt_specification: CASE_SYM { - if (maybe_start_compound_statement(thd)) + if (Lex->maybe_start_compound_statement(thd)) MYSQL_YYABORT; /** @@ -4072,7 +4114,7 @@ case_stmt_specification: BACKPATCH: Creating target label for the jump to after END CASE (instruction 12 in the example) */ - Lex->spcont->push_label(thd, empty_lex_str, Lex->sphead->instructions()); + Lex->spcont->push_label(thd, &empty_clex_str, Lex->sphead->instructions()); } case_stmt_body else_clause_opt @@ -4097,7 +4139,7 @@ case_stmt_body: { Lex->sphead->reset_lex(thd); /* For expr $2 */ } expr { - if (case_stmt_action_expr(Lex, $2)) + if (Lex->case_stmt_action_expr($2)) MYSQL_YYABORT; if (Lex->sphead->restore_lex(thd)) @@ -4129,7 +4171,7 @@ simple_when_clause: /* Simple case: <caseval> = <whenval> */ LEX *lex= Lex; - if (case_stmt_action_when(lex, $3, true)) + if (lex->case_stmt_action_when($3, true)) MYSQL_YYABORT; /* For expr $3 */ if (lex->sphead->restore_lex(thd)) @@ -4138,8 +4180,7 @@ simple_when_clause: THEN_SYM sp_proc_stmts1 { - LEX *lex= Lex; - if (case_stmt_action_then(lex)) + if (Lex->case_stmt_action_then()) MYSQL_YYABORT; } ; @@ -4152,7 +4193,7 @@ searched_when_clause: expr { LEX *lex= Lex; - if (case_stmt_action_when(lex, $3, false)) + if (lex->case_stmt_action_when($3, false)) MYSQL_YYABORT; /* For expr $3 */ if (lex->sphead->restore_lex(thd)) @@ -4161,8 +4202,7 @@ searched_when_clause: THEN_SYM sp_proc_stmts1 { - LEX *lex= Lex; - if (case_stmt_action_then(lex)) + if (Lex->case_stmt_action_then()) MYSQL_YYABORT; } ; @@ -4183,88 +4223,62 @@ else_clause_opt: ; sp_opt_label: - /* Empty */ { $$= null_lex_str; } + /* Empty */ { $$= null_clex_str; } | label_ident { $$= $1; } ; -sp_labeled_block: - label_ident ':' BEGIN_SYM +sp_block_label: + label_ident ':' { - LEX *lex= Lex; - sp_pcontext *ctx= lex->spcont; - sp_label *lab= ctx->find_label($1); + if (Lex->spcont->block_label_declare(&$1)) + MYSQL_YYABORT; + $$= $1; + } + ; - if (lab) - my_yyabort_error((ER_SP_LABEL_REDEFINE, MYF(0), $1.str)); - lex->name= $1; +sp_labeled_block: + sp_block_label + BEGIN_SYM + { + Lex->sp_block_init(thd, &$1); } - sp_block_content sp_opt_label + sp_decls + sp_proc_stmts + END + sp_opt_label { - if ($6.str) - { - if (my_strcasecmp(system_charset_info, $6.str, $5->name.str) != 0) - my_yyabort_error((ER_SP_LABEL_MISMATCH, MYF(0), $6.str)); - } + if (Lex->sp_block_finalize(thd, $4, &$7)) + MYSQL_YYABORT; } ; sp_unlabeled_block: BEGIN_SYM { - Lex->name= empty_lex_str; // Unlabeled blocks get an empty label + Lex->sp_block_init(thd); } - sp_block_content - { } - ; - -sp_unlabeled_block_not_atomic: - BEGIN_SYM not ATOMIC_SYM /* TODO: BEGIN ATOMIC (not -> opt_not) */ + sp_decls + sp_proc_stmts + END { - if (maybe_start_compound_statement(thd)) + if (Lex->sp_block_finalize(thd, $3)) MYSQL_YYABORT; - Lex->name= empty_lex_str; // Unlabeled blocks get an empty label } - sp_block_content - { } ; -sp_block_content: +sp_unlabeled_block_not_atomic: + BEGIN_SYM not ATOMIC_SYM /* TODO: BEGIN ATOMIC (not -> opt_not) */ { - LEX *lex= Lex; - sp_label *lab= lex->spcont->push_label(thd, lex->name, - lex->sphead->instructions()); - lab->type= sp_label::BEGIN; - lex->spcont= lex->spcont->push_context(thd, - sp_pcontext::REGULAR_SCOPE); + if (Lex->maybe_start_compound_statement(thd)) + MYSQL_YYABORT; + Lex->sp_block_init(thd); } sp_decls sp_proc_stmts END { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - sp_pcontext *ctx= lex->spcont; - sp_instr *i; - - sp->backpatch(ctx->last_label()); /* We always have a label */ - if ($2.hndlrs) - { - i= new (thd->mem_root) - sp_instr_hpop(sp->instructions(), ctx, $2.hndlrs); - if (i == NULL || - sp->add_instr(i)) - MYSQL_YYABORT; - } - if ($2.curs) - { - i= new (thd->mem_root) - sp_instr_cpop(sp->instructions(), ctx, $2.curs); - if (i == NULL || - sp->add_instr(i)) - MYSQL_YYABORT; - } - lex->spcont= ctx->pop_context(); - $$ = lex->spcont->pop_label(); + if (Lex->sp_block_finalize(thd, $5)) + MYSQL_YYABORT; } ; @@ -4286,30 +4300,15 @@ while_body: expr DO_SYM { LEX *lex= Lex; - sp_head *sp= lex->sphead; - uint ip= sp->instructions(); - sp_instr_jump_if_not *i= new (thd->mem_root) - sp_instr_jump_if_not(ip, lex->spcont, $1, lex); - if (i == NULL || - /* Jumping forward */ - sp->push_backpatch(thd, i, lex->spcont->last_label()) || - sp->new_cont_backpatch(i) || - sp->add_instr(i)) + if (lex->sp_while_loop_expression(thd, $1)) MYSQL_YYABORT; - if (sp->restore_lex(thd)) + if (lex->sphead->restore_lex(thd)) MYSQL_YYABORT; } sp_proc_stmts1 END WHILE_SYM { - LEX *lex= Lex; - uint ip= lex->sphead->instructions(); - sp_label *lab= lex->spcont->last_label(); /* Jumping back */ - sp_instr_jump *i= new (thd->mem_root) - sp_instr_jump(ip, lex->spcont, lab->ip); - if (i == NULL || - lex->sphead->add_instr(i)) + if (Lex->sp_while_loop_finalize(thd)) MYSQL_YYABORT; - lex->sphead->do_cont_backpatch(); } ; @@ -4333,79 +4332,68 @@ repeat_body: } ; -pop_sp_label: +pop_sp_loop_label: sp_opt_label { - sp_label *lab; - Lex->sphead->backpatch(lab= Lex->spcont->pop_label()); - if ($1.str) - { - if (my_strcasecmp(system_charset_info, $1.str, - lab->name.str) != 0) - my_yyabort_error((ER_SP_LABEL_MISMATCH, MYF(0), $1.str)); - } - } - ; - -pop_sp_empty_label: - { - sp_label *lab; - Lex->sphead->backpatch(lab= Lex->spcont->pop_label()); - DBUG_ASSERT(lab->name.length == 0); + if (Lex->sp_pop_loop_label(thd, &$1)) + MYSQL_YYABORT; } ; sp_labeled_control: label_ident ':' LOOP_SYM { - if (push_sp_label(thd, $1)) + if (Lex->sp_push_loop_label(thd, &$1)) MYSQL_YYABORT; } - loop_body pop_sp_label + loop_body pop_sp_loop_label { } | label_ident ':' WHILE_SYM { - if (push_sp_label(thd, $1)) + if (Lex->sp_push_loop_label(thd, &$1)) MYSQL_YYABORT; Lex->sphead->reset_lex(thd); } - while_body pop_sp_label + while_body pop_sp_loop_label { } | label_ident ':' REPEAT_SYM { - if (push_sp_label(thd, $1)) + if (Lex->sp_push_loop_label(thd, &$1)) MYSQL_YYABORT; } - repeat_body pop_sp_label + repeat_body pop_sp_loop_label { } ; sp_unlabeled_control: LOOP_SYM { - if (push_sp_empty_label(thd)) + if (Lex->sp_push_loop_empty_label(thd)) MYSQL_YYABORT; } loop_body - pop_sp_empty_label - { } + { + Lex->sp_pop_loop_empty_label(thd); + } | WHILE_SYM { - if (push_sp_empty_label(thd)) + if (Lex->sp_push_loop_empty_label(thd)) MYSQL_YYABORT; Lex->sphead->reset_lex(thd); } while_body - pop_sp_empty_label - { } + { + Lex->sp_pop_loop_empty_label(thd); + } | REPEAT_SYM { - if (push_sp_empty_label(thd)) + if (Lex->sp_push_loop_empty_label(thd)) MYSQL_YYABORT; } repeat_body - pop_sp_empty_label - { } + { + Lex->sp_pop_loop_empty_label(thd); + } ; trg_action_time: @@ -4762,11 +4750,11 @@ size_number: ulonglong number; uint text_shift_number= 0; longlong prefix_number; - char *start_ptr= $1.str; + const char *start_ptr= $1.str; uint str_len= $1.length; - char *end_ptr= start_ptr + str_len; + const char *end_ptr= start_ptr + str_len; int error; - prefix_number= my_strtoll10(start_ptr, &end_ptr, &error); + prefix_number= my_strtoll10(start_ptr, (char**) &end_ptr, &error); if ((start_ptr + str_len - 1) == end_ptr) { switch (end_ptr[0]) @@ -4824,7 +4812,7 @@ create_body: if (! src_table) MYSQL_YYABORT; /* CREATE TABLE ... LIKE is not allowed for views. */ - src_table->required_type= FRMTYPE_TABLE; + src_table->required_type= TABLE_TYPE_NORMAL; } ; @@ -4922,7 +4910,7 @@ have_partitioning: /* empty */ { #ifdef WITH_PARTITION_STORAGE_ENGINE - LEX_STRING partition_name={C_STRING_WITH_LEN("partition")}; + LEX_CSTRING partition_name={STRING_WITH_LEN("partition")}; if (!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN)) my_yyabort_error((ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-partition")); @@ -4939,7 +4927,7 @@ partition_entry: LEX *lex= Lex; if (!lex->part_info) { - my_parse_error(thd, ER_PARTITION_ENTRY_ERROR); + thd->parse_error(ER_PARTITION_ENTRY_ERROR); MYSQL_YYABORT; } /* @@ -4996,7 +4984,7 @@ opt_key_algo: Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_55; break; default: - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -5112,7 +5100,7 @@ part_func_expr: { if (!Lex->safe_to_cache_query) { - my_parse_error(thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR); + thd->parse_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR); MYSQL_YYABORT; } $$=$1; @@ -5152,7 +5140,7 @@ part_defs: if (part_info->num_parts != count_curr_parts) { - my_parse_error(thd, ER_PARTITION_WRONG_NO_PART_ERROR); + thd->parse_error(ER_PARTITION_WRONG_NO_PART_ERROR); MYSQL_YYABORT; } } @@ -5274,7 +5262,7 @@ opt_part_values: ; part_func_max: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; @@ -5282,7 +5270,7 @@ part_func_max: part_info->num_columns != 1U) { part_info->print_debug("Kilroy II", NULL); - my_parse_error(thd, ER_PARTITION_COLUMN_LIST_ERROR); + thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR); MYSQL_YYABORT; } else @@ -5313,7 +5301,7 @@ part_values_in: part_info->num_columns > MAX_REF_PARTS) { part_info->print_debug("Kilroy III", NULL); - my_parse_error(thd, ER_PARTITION_COLUMN_LIST_ERROR); + thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR); MYSQL_YYABORT; } /* @@ -5334,7 +5322,7 @@ part_values_in: partition_info *part_info= Lex->part_info; if (part_info->num_columns < 2U) { - my_parse_error(thd, ER_ROW_SINGLE_PARTITION_FIELD_ERROR); + thd->parse_error(ER_ROW_SINGLE_PARTITION_FIELD_ERROR); MYSQL_YYABORT; } } @@ -5375,7 +5363,7 @@ part_value_item: error. */ part_info->print_debug("Kilroy I", NULL); - my_parse_error(thd, ER_PARTITION_COLUMN_LIST_ERROR); + thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR); MYSQL_YYABORT; } part_info->curr_list_object= 0; @@ -5388,12 +5376,12 @@ part_value_item_list: ; part_value_expr_item: - MAX_VALUE_SYM + MAXVALUE_SYM { partition_info *part_info= Lex->part_info; if (part_info->part_type == LIST_PARTITION) { - my_parse_error(thd, ER_MAXVALUE_IN_VALUES_IN); + thd->parse_error(ER_MAXVALUE_IN_VALUES_IN); MYSQL_YYABORT; } if (part_info->add_max_value(thd)) @@ -5409,7 +5397,7 @@ part_value_expr_item: if (!lex->safe_to_cache_query) { - my_parse_error(thd, ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR); + thd->parse_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR); MYSQL_YYABORT; } if (part_info->add_column_list_value(thd, part_expr)) @@ -5431,7 +5419,7 @@ opt_sub_partition: We come here when we have defined subpartitions on the first partition but not on all the subsequent partitions. */ - my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR); + thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR); MYSQL_YYABORT; } } @@ -5443,7 +5431,7 @@ opt_sub_partition: if (part_info->num_subparts != part_info->count_curr_subparts) { - my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR); + thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR); MYSQL_YYABORT; } } @@ -5451,7 +5439,7 @@ opt_sub_partition: { if (part_info->partitions.elements > 1) { - my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR); + thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR); MYSQL_YYABORT; } part_info->num_subparts= part_info->count_curr_subparts; @@ -5486,7 +5474,7 @@ sub_part_definition: the second partition (the current partition processed have already been put into the partitions list. */ - my_parse_error(thd, ER_PARTITION_WRONG_NO_SUBPART_ERROR); + thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR); MYSQL_YYABORT; } if (!sub_p_elem || @@ -5720,7 +5708,7 @@ create_table_option: Lex->create_info.table_options|= HA_OPTION_PACK_KEYS; break; default: - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; @@ -5741,7 +5729,7 @@ create_table_option: Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_ON; break; default: - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } Lex->create_info.used_fields|= HA_CREATE_USED_STATS_AUTO_RECALC; @@ -5761,7 +5749,7 @@ create_table_option: Lex->create_info.table_options|= HA_OPTION_STATS_PERSISTENT; break; default: - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } Lex->create_info.used_fields|= HA_CREATE_USED_STATS_PERSISTENT; @@ -5784,7 +5772,7 @@ create_table_option: we can store the higher bits from stats_sample_pages in .frm too. */ if ($3 == 0 || $3 > 0xffff) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } Lex->create_info.stats_sample_pages=$3; @@ -5913,6 +5901,11 @@ create_table_option: engine_option_value($1, &Lex->create_info.option_list, &Lex->option_list_last); } + | SEQUENCE_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= $3; + } ; default_charset: @@ -6025,28 +6018,28 @@ key_def: key_or_index opt_if_not_exists opt_ident opt_USING_key_algorithm { Lex->option_list= NULL; - if (Lex->add_key(Key::MULTIPLE, $3, $4, $2)) + if (Lex->add_key(Key::MULTIPLE, &$3, $4, $2)) MYSQL_YYABORT; } '(' key_list ')' normal_key_options { } | key_or_index opt_if_not_exists ident TYPE_SYM btree_or_rtree { Lex->option_list= NULL; - if (Lex->add_key(Key::MULTIPLE, $3, $5, $2)) + if (Lex->add_key(Key::MULTIPLE, &$3, $5, $2)) MYSQL_YYABORT; } '(' key_list ')' normal_key_options { } | fulltext opt_key_or_index opt_if_not_exists opt_ident { Lex->option_list= NULL; - if (Lex->add_key($1, $4, HA_KEY_ALG_UNDEF, $3)) + if (Lex->add_key($1, &$4, HA_KEY_ALG_UNDEF, $3)) MYSQL_YYABORT; } '(' key_list ')' fulltext_key_options { } | spatial opt_key_or_index opt_if_not_exists opt_ident { Lex->option_list= NULL; - if (Lex->add_key($1, $4, HA_KEY_ALG_UNDEF, $3)) + if (Lex->add_key($1, &$4, HA_KEY_ALG_UNDEF, $3)) MYSQL_YYABORT; } '(' key_list ')' spatial_key_options { } @@ -6055,7 +6048,7 @@ key_def: opt_USING_key_algorithm { Lex->option_list= NULL; - if (Lex->add_key($2, $4.str ? $4 : $1, $5, $3)) + if (Lex->add_key($2, $4.str ? &$4 : &$1, $5, $3)) MYSQL_YYABORT; } '(' key_list ')' normal_key_options { } @@ -6063,7 +6056,7 @@ key_def: TYPE_SYM btree_or_rtree { Lex->option_list= NULL; - if (Lex->add_key($2, $4.str ? $4 : $1, $6, $3)) + if (Lex->add_key($2, $4.str ? &$4 : &$1, $6, $3)) MYSQL_YYABORT; } '(' key_list ')' normal_key_options { } @@ -6071,7 +6064,7 @@ key_def: { if (Lex->check_add_key($4) || !(Lex->last_key= (new (thd->mem_root) - Key(Key::MULTIPLE, $1.str ? $1 : $5, + Key(Key::MULTIPLE, $1.str ? &$1 : &$5, HA_KEY_ALG_UNDEF, true, $4)))) MYSQL_YYABORT; Lex->option_list= NULL; @@ -6080,11 +6073,11 @@ key_def: { LEX *lex=Lex; Key *key= (new (thd->mem_root) - Foreign_key($5.str ? $5 : $1, - lex->last_key->columns, - $10->db, - $10->table, - lex->ref_list, + Foreign_key($5.str ? &$5 : &$1, + &lex->last_key->columns, + &$10->db, + &$10->table, + &lex->ref_list, lex->fk_delete_opt, lex->fk_update_opt, lex->fk_match_option, @@ -6130,7 +6123,7 @@ check_constraint: ; opt_constraint: - /* empty */ { $$= null_lex_str; } + /* empty */ { $$= null_clex_str; } | constraint { $$= $1; } ; @@ -6151,7 +6144,7 @@ field_spec: if (!f) MYSQL_YYABORT; - lex->init_last_field(f, $1.str, NULL); + lex->init_last_field(f, &$1, NULL); $<create_field>$= f; } field_type_or_serial opt_check_constraint @@ -6168,19 +6161,18 @@ field_spec: $$->create_if_not_exists= Lex->check_exists; if ($$->flags & PRI_KEY_FLAG) - add_key_to_list(lex, &$1, Key::PRIMARY, Lex->check_exists); + lex->add_key_to_list(&$1, Key::PRIMARY, lex->check_exists); else if ($$->flags & UNIQUE_KEY_FLAG) - add_key_to_list(lex, &$1, Key::UNIQUE, Lex->check_exists); + lex->add_key_to_list(&$1, Key::UNIQUE, lex->check_exists); } ; field_type_or_serial: - field_type { Lex->set_last_field_type($1); } field_def + field_type { Lex->last_field->set_attributes($1, Lex->charset); } + field_def | SERIAL_SYM { - Lex_field_type_st type; - type.set(MYSQL_TYPE_LONGLONG); - Lex->set_last_field_type(type); + Lex->last_field->set_handler(&type_handler_longlong); Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNSIGNED_FLAG | UNIQUE_KEY_FLAG; } @@ -6319,65 +6311,86 @@ column_default_expr: ; field_type: + field_type_numeric + | field_type_temporal + | field_type_string + | field_type_lob + | field_type_misc + ; + +field_type_numeric: int_type opt_field_length field_options { $$.set($1, $2); } | real_type opt_precision field_options { $$.set($1, $2); } | FLOAT_SYM float_options field_options { - $$.set(MYSQL_TYPE_FLOAT, $2); + $$.set(&type_handler_float, $2); if ($2.length() && !$2.dec()) { int err; ulonglong tmp_length= my_strtoll10($2.length(), NULL, &err); if (err || tmp_length > PRECISION_FOR_DOUBLE) my_yyabort_error((ER_WRONG_FIELD_SPEC, MYF(0), - Lex->last_field->field_name)); + Lex->last_field->field_name.str)); if (tmp_length > PRECISION_FOR_FLOAT) - $$.set(MYSQL_TYPE_DOUBLE); + $$.set(&type_handler_double); else - $$.set(MYSQL_TYPE_FLOAT); + $$.set(&type_handler_float); } } | BIT_SYM opt_field_length_default_1 { - $$.set(MYSQL_TYPE_BIT, $2); + $$.set(&type_handler_bit, $2); } | BOOL_SYM { - $$.set(MYSQL_TYPE_TINY, "1"); + $$.set(&type_handler_tiny, "1"); } | BOOLEAN_SYM { - $$.set(MYSQL_TYPE_TINY, "1"); + $$.set(&type_handler_tiny, "1"); } - | char opt_field_length_default_1 opt_binary + | DECIMAL_SYM float_options field_options + { $$.set(&type_handler_newdecimal, $2);} + | NUMERIC_SYM float_options field_options + { $$.set(&type_handler_newdecimal, $2);} + | FIXED_SYM float_options field_options + { $$.set(&type_handler_newdecimal, $2);} + ; + + +field_type_string: + char opt_field_length_default_1 opt_binary { - $$.set(MYSQL_TYPE_STRING, $2); + $$.set(&type_handler_string, $2); } | nchar opt_field_length_default_1 opt_bin_mod { - $$.set(MYSQL_TYPE_STRING, $2); + $$.set(&type_handler_string, $2); bincmp_collation(national_charset_info, $3); } | BINARY opt_field_length_default_1 { Lex->charset=&my_charset_bin; - $$.set(MYSQL_TYPE_STRING, $2); + $$.set(&type_handler_string, $2); } | varchar field_length opt_binary { - $$.set(MYSQL_TYPE_VARCHAR, $2); + $$.set(&type_handler_varchar, $2); } | nvarchar field_length opt_bin_mod { - $$.set(MYSQL_TYPE_VARCHAR, $2); + $$.set(&type_handler_varchar, $2); bincmp_collation(national_charset_info, $3); } | VARBINARY field_length { Lex->charset=&my_charset_bin; - $$.set(MYSQL_TYPE_VARCHAR, $2); + $$.set(&type_handler_varchar, $2); } - | YEAR_SYM opt_field_length field_options + ; + +field_type_temporal: + YEAR_SYM opt_field_length field_options { if ($2) { @@ -6393,18 +6406,23 @@ field_type: buff, "YEAR(4)"); } } - $$.set(MYSQL_TYPE_YEAR, $2); + $$.set(&type_handler_year, $2); } - | DATE_SYM - { $$.set(MYSQL_TYPE_DATE); } + | DATE_SYM { $$.set(thd->type_handler_for_date()); } | TIME_SYM opt_field_length - { $$.set(opt_mysql56_temporal_format ? - MYSQL_TYPE_TIME2 : MYSQL_TYPE_TIME, $2); } + { + $$.set(opt_mysql56_temporal_format ? + static_cast<const Type_handler*>(&type_handler_time2) : + static_cast<const Type_handler*>(&type_handler_time), + $2); + } | TIMESTAMP opt_field_length { if (thd->variables.sql_mode & MODE_MAXDB) $$.set(opt_mysql56_temporal_format ? - MYSQL_TYPE_DATETIME2 : MYSQL_TYPE_DATETIME, $2); + static_cast<const Type_handler*>(&type_handler_datetime2) : + static_cast<const Type_handler*>(&type_handler_datetime), + $2); else { /* @@ -6413,29 +6431,39 @@ field_type: */ if (!opt_explicit_defaults_for_timestamp) Lex->last_field->flags|= NOT_NULL_FLAG; - $$.set(opt_mysql56_temporal_format ? MYSQL_TYPE_TIMESTAMP2 - : MYSQL_TYPE_TIMESTAMP, $2); + $$.set(opt_mysql56_temporal_format ? + static_cast<const Type_handler*>(&type_handler_timestamp2): + static_cast<const Type_handler*>(&type_handler_timestamp), + $2); } } | DATETIME opt_field_length - { $$.set(opt_mysql56_temporal_format ? - MYSQL_TYPE_DATETIME2 : MYSQL_TYPE_DATETIME, $2); } - | TINYBLOB + { + $$.set(opt_mysql56_temporal_format ? + static_cast<const Type_handler*>(&type_handler_datetime2) : + static_cast<const Type_handler*>(&type_handler_datetime), + $2); + } + ; + + +field_type_lob: + TINYBLOB { Lex->charset=&my_charset_bin; - $$.set(MYSQL_TYPE_TINY_BLOB); + $$.set(&type_handler_tiny_blob); } | BLOB_SYM opt_field_length { Lex->charset=&my_charset_bin; - $$.set(MYSQL_TYPE_BLOB, $2); + $$.set(&type_handler_blob, $2); } | spatial_type float_options srid_option { #ifdef HAVE_SPATIAL Lex->charset=&my_charset_bin; Lex->last_field->geom_type= $1; - $$.set(MYSQL_TYPE_GEOMETRY, $2); + $$.set(&type_handler_geometry, $2); #else my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, sym_group_geom.needed_define)); @@ -6444,47 +6472,44 @@ field_type: | MEDIUMBLOB { Lex->charset=&my_charset_bin; - $$.set(MYSQL_TYPE_MEDIUM_BLOB); + $$.set(&type_handler_medium_blob); } | LONGBLOB { Lex->charset=&my_charset_bin; - $$.set(MYSQL_TYPE_LONG_BLOB); + $$.set(&type_handler_long_blob); } | LONG_SYM VARBINARY { Lex->charset=&my_charset_bin; - $$.set(MYSQL_TYPE_MEDIUM_BLOB); + $$.set(&type_handler_medium_blob); } | LONG_SYM varchar opt_binary - { $$.set(MYSQL_TYPE_MEDIUM_BLOB); } + { $$.set(&type_handler_medium_blob); } | TINYTEXT opt_binary - { $$.set(MYSQL_TYPE_TINY_BLOB); } + { $$.set(&type_handler_tiny_blob); } | TEXT_SYM opt_field_length opt_binary - { $$.set(MYSQL_TYPE_BLOB, $2); } + { $$.set(&type_handler_blob, $2); } | MEDIUMTEXT opt_binary - { $$.set(MYSQL_TYPE_MEDIUM_BLOB); } + { $$.set(&type_handler_medium_blob); } | LONGTEXT opt_binary - { $$.set(MYSQL_TYPE_LONG_BLOB); } - | DECIMAL_SYM float_options field_options - { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);} - | NUMERIC_SYM float_options field_options - { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);} - | FIXED_SYM float_options field_options - { $$.set(MYSQL_TYPE_NEWDECIMAL, $2);} - | ENUM '(' string_list ')' opt_binary - { $$.set(MYSQL_TYPE_ENUM); } - | SET '(' string_list ')' opt_binary - { $$.set(MYSQL_TYPE_SET); } + { $$.set(&type_handler_long_blob); } | LONG_SYM opt_binary - { $$.set(MYSQL_TYPE_MEDIUM_BLOB); } + { $$.set(&type_handler_medium_blob); } | JSON_SYM { Lex->charset= &my_charset_utf8mb4_bin; - $$.set(MYSQL_TYPE_LONG_BLOB); + $$.set(&type_handler_long_blob); } ; +field_type_misc: + ENUM '(' string_list ')' opt_binary + { $$.set(&type_handler_enum); } + | SET '(' string_list ')' opt_binary + { $$.set(&type_handler_set); } + ; + spatial_type: GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; } | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; } @@ -6519,23 +6544,22 @@ nvarchar: ; int_type: - INT_SYM { $$=MYSQL_TYPE_LONG; } - | TINYINT { $$=MYSQL_TYPE_TINY; } - | SMALLINT { $$=MYSQL_TYPE_SHORT; } - | MEDIUMINT { $$=MYSQL_TYPE_INT24; } - | BIGINT { $$=MYSQL_TYPE_LONGLONG; } + INT_SYM { $$= &type_handler_long; } + | TINYINT { $$= &type_handler_tiny; } + | SMALLINT { $$= &type_handler_short; } + | MEDIUMINT { $$= &type_handler_int24; } + | BIGINT { $$= &type_handler_longlong; } ; real_type: REAL { $$= thd->variables.sql_mode & MODE_REAL_AS_FLOAT ? - MYSQL_TYPE_FLOAT : MYSQL_TYPE_DOUBLE; + static_cast<const Type_handler *>(&type_handler_float) : + static_cast<const Type_handler *>(&type_handler_double); } - | DOUBLE_SYM - { $$=MYSQL_TYPE_DOUBLE; } - | DOUBLE_SYM PRECISION - { $$=MYSQL_TYPE_DOUBLE; } + | DOUBLE_SYM { $$= &type_handler_double; } + | DOUBLE_SYM PRECISION { $$= &type_handler_double; } ; srid_option: @@ -6672,7 +6696,7 @@ type_with_opt_collate: if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))) MYSQL_YYABORT; } - Lex->set_last_field_type($1); + Lex->last_field->set_attributes($1, Lex->charset); } ; @@ -6766,7 +6790,7 @@ ws_nweights: { if ($2 == 0) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -6854,14 +6878,14 @@ opt_ref_list: ref_list: ref_list ',' ident { - Key_part_spec *key= new (thd->mem_root) Key_part_spec($3, 0); + Key_part_spec *key= new (thd->mem_root) Key_part_spec(&$3, 0); if (key == NULL) MYSQL_YYABORT; Lex->ref_list.push_back(key, thd->mem_root); } | ident { - Key_part_spec *key= new (thd->mem_root) Key_part_spec($1, 0); + Key_part_spec *key= new (thd->mem_root) Key_part_spec(&$1, 0); if (key == NULL) MYSQL_YYABORT; LEX *lex= Lex; @@ -7087,7 +7111,7 @@ key_list: key_part: ident { - $$= new (thd->mem_root) Key_part_spec($1, 0); + $$= new (thd->mem_root) Key_part_spec(&$1, 0); if ($$ == NULL) MYSQL_YYABORT; } @@ -7096,19 +7120,19 @@ key_part: int key_part_len= atoi($3.str); if (!key_part_len) my_yyabort_error((ER_KEY_PART_0, MYF(0), $1.str)); - $$= new (thd->mem_root) Key_part_spec($1, (uint) key_part_len); + $$= new (thd->mem_root) Key_part_spec(&$1, (uint) key_part_len); if ($$ == NULL) MYSQL_YYABORT; } ; opt_ident: - /* empty */ { $$= null_lex_str; } + /* empty */ { $$= null_clex_str; } | field_ident { $$= $1; } ; opt_component: - /* empty */ { $$= null_lex_str; } + /* empty */ { $$= null_clex_str; } | '.' ident { $$= $2; } ; @@ -7125,8 +7149,8 @@ string_list: alter: ALTER { - Lex->name= null_lex_str; - Lex->only_view= FALSE; + Lex->name= null_clex_str; + Lex->table_type= TABLE_TYPE_UNKNOWN; Lex->sql_command= SQLCOM_ALTER_TABLE; Lex->duplicates= DUP_ERROR; Lex->select_lex.init_order(); @@ -7185,7 +7209,7 @@ alter: if (lex->sphead) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE")); - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sp_chistics.init(); } sp_a_chistics { @@ -7200,7 +7224,7 @@ alter: if (lex->sphead) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); - bzero((char *)&lex->sp_chistics, sizeof(st_sp_chistics)); + lex->sp_chistics.init(); } sp_a_chistics { @@ -7209,31 +7233,24 @@ alter: lex->sql_command= SQLCOM_ALTER_FUNCTION; lex->spname= $3; } - | ALTER view_algorithm definer_opt + | ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident { - LEX *lex= Lex; - - if (lex->sphead) - my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW")); - lex->create_view_mode= VIEW_ALTER; + if (Lex->add_alter_view(thd, $2, $4, $6)) + MYSQL_YYABORT; } - view_tail + view_list_opt AS view_select {} - | ALTER definer_opt + | ALTER definer_opt opt_view_suid VIEW_SYM table_ident /* We have two separate rules for ALTER VIEW rather that optional view_algorithm above, to resolve the ambiguity with the ALTER EVENT below. */ { - LEX *lex= Lex; - - if (lex->sphead) - my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "ALTER VIEW")); - lex->create_view_algorithm= VIEW_ALGORITHM_INHERIT; - lex->create_view_mode= VIEW_ALTER; + if (Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5)) + MYSQL_YYABORT; } - view_tail + view_list_opt AS view_select {} | ALTER definer_opt remember_name EVENT_SYM sp_name { @@ -7260,7 +7277,7 @@ alter: { if (!($7 || $8 || $9 || $10 || $11)) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } /* @@ -7303,6 +7320,33 @@ alter: Lex->create_info.set($2); Lex->sql_command= SQLCOM_ALTER_USER; } + | ALTER SEQUENCE_SYM opt_if_exists_table_element + { + LEX *lex= Lex; + lex->name= null_clex_str; + lex->table_type= TABLE_TYPE_UNKNOWN; + lex->sql_command= SQLCOM_ALTER_SEQUENCE; + lex->create_info.init(); + lex->no_write_to_binlog= 0; + DBUG_ASSERT(!lex->m_sql_cmd); + } + table_ident + { + LEX *lex= Lex; + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition()) || + !lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_SEQUENCE, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + } + sequence_defs + { + /* Create a generic ALTER SEQUENCE statment. */ + Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_sequence(); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } ; ev_alter_on_schedule_completion: @@ -7331,7 +7375,7 @@ opt_ev_sql_stmt: ; ident_or_empty: - /* empty */ { $$= null_lex_str; } + /* empty */ { $$= null_clex_str; } | ident { $$= $1; } ; @@ -7616,7 +7660,7 @@ alter_list_item: { Lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN; Lex->create_last_non_select_table= Lex->last_table(); - $5->change= $4.str; + $5->change= $4; $5->after= $6; } | MODIFY_SYM opt_column opt_if_exists_table_element @@ -7721,7 +7765,7 @@ alter_list_item: MYSQL_YYABORT; } if (check_table_name($3->table.str,$3->table.length, FALSE) || - ($3->db.str && check_db_name(&$3->db))) + ($3->db.str && check_db_name((LEX_STRING*) &$3->db))) my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3->table.str)); lex->name= $3->table; lex->alter_info.flags|= Alter_info::ALTER_RENAME; @@ -7836,15 +7880,16 @@ opt_restrict: ; opt_place: - /* empty */ { $$= NULL; } + /* empty */ { $$= null_clex_str; } | AFTER_SYM ident { - $$= $2.str; + $$= $2; Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER; } | FIRST_SYM { - $$= first_keyword; + $$.str= first_keyword; + $$.length= 5; /* Length of "first" */ Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER; } ; @@ -7899,7 +7944,7 @@ start: if (($3 & MYSQL_START_TRANS_OPT_READ_WRITE) && ($3 & MYSQL_START_TRANS_OPT_READ_ONLY)) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } lex->start_transaction_opt= $3; @@ -8002,7 +8047,9 @@ opt_checksum_type: repair_table_or_view: table_or_tables table_list opt_mi_repair_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_repair_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_repair_type ; repair: @@ -8167,7 +8214,9 @@ binlog_base64_event: check_view_or_table: table_or_tables table_list opt_mi_check_type - | VIEW_SYM { Lex->only_view= TRUE; } table_list opt_view_check_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_check_type ; check: CHECK_SYM @@ -8714,7 +8763,7 @@ select_item_list: { Item *item= new (thd->mem_root) Item_field(thd, &thd->lex->current_select->context, - NULL, NULL, "*"); + NULL, NULL, &star_clex_str); if (item == NULL) MYSQL_YYABORT; if (add_item_to_list(thd, item)) @@ -8743,7 +8792,7 @@ select_item: $2->is_autogenerated_name= FALSE; $2->set_name(thd, $4.str, $4.length, system_charset_info); } - else if (!$2->name) + else if (!$2->name.str || $2->name.str == item_empty_name) { $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); } @@ -8764,12 +8813,12 @@ remember_name: remember_end: { - $$= (char*) YYLIP->get_cpp_tok_end(); + $$= (char*) YYLIP->get_cpp_tok_end_rtrim(); } ; select_alias: - /* empty */ { $$=null_lex_str;} + /* empty */ { $$=null_clex_str;} | AS ident { $$=$2; } | AS TEXT_STRING_sys { $$=$2; } | ident { $$=$1; } @@ -9368,23 +9417,23 @@ column_default_non_parenthesized_expr: } | CAST_SYM '(' expr AS cast_type ')' { - LEX *lex= Lex; - $$= create_func_cast(thd, $3, $5.type(), $5.length(), $5.dec(), - lex->charset); - if ($$ == NULL) + if (!($$= $5.create_typecast_item(thd, $3, Lex->charset))) MYSQL_YYABORT; } - | CASE_SYM opt_expr when_list opt_else END + | CASE_SYM when_list_opt_else END { - $$= new (thd->mem_root) Item_func_case(thd, *$3, $2, $4); - if ($$ == NULL) + if (!($$= new(thd->mem_root) Item_func_case_searched(thd, *$2))) + MYSQL_YYABORT; + } + | CASE_SYM expr when_list_opt_else END + { + $3->push_front($2, thd->mem_root); + if (!($$= new (thd->mem_root) Item_func_case_simple(thd, *$3))) MYSQL_YYABORT; } | CONVERT_SYM '(' expr ',' cast_type ')' { - $$= create_func_cast(thd, $3, $5.type(), $5.length(), $5.dec(), - Lex->charset); - if ($$ == NULL) + if (!($$= $5.create_typecast_item(thd, $3, Lex->charset))) MYSQL_YYABORT; } | CONVERT_SYM '(' expr USING charset_name ')' @@ -9410,6 +9459,41 @@ column_default_non_parenthesized_expr: if ($$ == NULL) MYSQL_YYABORT; } + | NEXT_SYM VALUE_SYM FOR_SYM table_ident + { + if (!($$= Lex->create_item_func_nextval(thd, $4))) + MYSQL_YYABORT; + } + | NEXTVAL_SYM '(' table_ident ')' + { + if (!($$= Lex->create_item_func_nextval(thd, $3))) + MYSQL_YYABORT; + } + | PREVIOUS_SYM VALUE_SYM FOR_SYM table_ident + { + if (!($$= Lex->create_item_func_lastval(thd, $4))) + MYSQL_YYABORT; + } + | LASTVAL_SYM '(' table_ident ')' + { + if (!($$= Lex->create_item_func_lastval(thd, $3))) + MYSQL_YYABORT; + } + | SETVAL_SYM '(' table_ident ',' longlong_num ')' + { + if (!($$= Lex->create_item_func_setval(thd, $3, $5, 0, 1))) + MYSQL_YYABORT; + } + | SETVAL_SYM '(' table_ident ',' longlong_num ',' bool ')' + { + if (!($$= Lex->create_item_func_setval(thd, $3, $5, 0, $7))) + MYSQL_YYABORT; + } + | SETVAL_SYM '(' table_ident ',' longlong_num ',' bool ',' ulonglong_num ')' + { + if (!($$= Lex->create_item_func_setval(thd, $3, $5, $9, $7))) + MYSQL_YYABORT; + } ; simple_expr: @@ -9428,9 +9512,8 @@ simple_expr: | '(' parenthesized_expr ')' { $$= $2; } | BINARY simple_expr %prec NEG { - $$= create_func_cast(thd, $2, ITEM_CAST_CHAR, NULL, NULL, - &my_charset_bin); - if ($$ == NULL) + Type_cast_attributes at(&my_charset_bin); + if (!($$= type_handler_long_blob.create_typecast_item(thd, $2, at))) MYSQL_YYABORT; } | simple_expr OR_OR_SYM simple_expr @@ -9724,6 +9807,24 @@ function_call_nonkeyword: if ($$ == NULL) MYSQL_YYABORT; } + | DATE_FORMAT_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DATE_FORMAT_SYM '(' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DECODE_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_decode(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } | EXTRACT_SYM '(' interval FROM expr ')' { $$=new (thd->mem_root) Item_extract(thd, $3, $5); @@ -9869,7 +9970,7 @@ function_call_nonkeyword: COLUMN_GET_SYM '(' expr ',' expr AS cast_type ')' { LEX *lex= Lex; - $$= create_func_dyncol_get(thd, $3, $5, $7.type(), + $$= create_func_dyncol_get(thd, $3, $5, $7.type_handler(), $7.length(), $7.dec(), lex->charset); if ($$ == NULL) @@ -9994,8 +10095,7 @@ function_call_conflict: } | REPLACE '(' expr ',' expr ',' expr ')' { - $$= new (thd->mem_root) Item_func_replace(thd, $3, $5, $7); - if ($$ == NULL) + if (!($$= Lex->make_item_func_replace(thd, $3, $5, $7))) MYSQL_YYABORT; } | REVERSE_SYM '(' expr ')' @@ -10153,7 +10253,7 @@ function_call_generic: { if (lex->current_select->inc_in_sum_expr()) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -10180,10 +10280,10 @@ function_call_generic: This will be revised with WL#2128 (SQL PATH) */ - builder= find_native_function_builder(thd, $1); + builder= find_native_function_builder(thd, &$1); if (builder) { - item= builder->create_func(thd, $1, $4); + item= builder->create_func(thd, &$1, $4); } else { @@ -10205,7 +10305,7 @@ function_call_generic: { builder= find_qualified_function_builder(thd); DBUG_ASSERT(builder); - item= builder->create_func(thd, $1, $4); + item= builder->create_func(thd, &$1, $4); } } @@ -10233,7 +10333,7 @@ function_call_generic: version() (a vendor can specify any schema). */ - if (!$1.str || check_db_name(&$1)) + if (!$1.str || check_db_name((LEX_STRING*) &$1)) my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str)); if (check_routine_name(&$3)) { @@ -10242,7 +10342,7 @@ function_call_generic: builder= find_qualified_function_builder(thd); DBUG_ASSERT(builder); - item= builder->create_with_db(thd, $1, $3, true, $5); + item= builder->create_with_db(thd, &$1, &$3, true, $5); if (! ($$= item)) { @@ -10594,7 +10694,7 @@ simple_window_func: window_name: ident { - $$= (LEX_STRING *) thd->memdup(&$1, sizeof(LEX_STRING)); + $$= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING)); if ($$ == NULL) MYSQL_YYABORT; } @@ -10616,7 +10716,7 @@ variable_aux: ident_or_text SET_VAR expr { Item_func_set_user_var *item; - $$= item= new (thd->mem_root) Item_func_set_user_var(thd, $1, $3); + $$= item= new (thd->mem_root) Item_func_set_user_var(thd, &$1, $3); if ($$ == NULL) MYSQL_YYABORT; LEX *lex= Lex; @@ -10625,7 +10725,7 @@ variable_aux: } | ident_or_text { - $$= new (thd->mem_root) Item_func_get_user_var(thd, $1); + $$= new (thd->mem_root) Item_func_get_user_var(thd, &$1); if ($$ == NULL) MYSQL_YYABORT; LEX *lex= Lex; @@ -10636,7 +10736,7 @@ variable_aux: /* disallow "SELECT @@global.global.variable" */ if ($3.str && $4.str && check_reserved_words(&$3)) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } if (!($$= get_system_var(thd, $2, $3, $4))) @@ -10679,7 +10779,7 @@ in_sum_expr: LEX *lex= Lex; if (lex->current_select->inc_in_sum_expr()) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -10692,34 +10792,34 @@ in_sum_expr: cast_type: BINARY opt_field_length - { $$.set(ITEM_CAST_CHAR, $2); Lex->charset= &my_charset_bin; } + { $$.set(&type_handler_long_blob, $2); Lex->charset= &my_charset_bin; } | CHAR_SYM opt_field_length { Lex->charset= thd->variables.collation_connection; } opt_binary - { $$.set(ITEM_CAST_CHAR, $2); } + { $$.set(&type_handler_long_blob, $2); } | NCHAR_SYM opt_field_length { Lex->charset= national_charset_info; - $$.set(ITEM_CAST_CHAR, $2, 0); + $$.set(&type_handler_long_blob, $2, 0); } | cast_type_numeric { $$= $1; Lex->charset= NULL; } | cast_type_temporal { $$= $1; Lex->charset= NULL; } ; cast_type_numeric: - INT_SYM { $$.set(ITEM_CAST_SIGNED_INT); } - | SIGNED_SYM { $$.set(ITEM_CAST_SIGNED_INT); } - | SIGNED_SYM INT_SYM { $$.set(ITEM_CAST_SIGNED_INT); } - | UNSIGNED { $$.set(ITEM_CAST_UNSIGNED_INT); } - | UNSIGNED INT_SYM { $$.set(ITEM_CAST_UNSIGNED_INT); } - | DECIMAL_SYM float_options { $$.set(ITEM_CAST_DECIMAL, $2); } - | DOUBLE_SYM opt_precision { $$.set(ITEM_CAST_DOUBLE, $2); } + INT_SYM { $$.set(&type_handler_longlong); } + | SIGNED_SYM { $$.set(&type_handler_longlong); } + | SIGNED_SYM INT_SYM { $$.set(&type_handler_longlong); } + | UNSIGNED { $$.set(&type_handler_ulonglong); } + | UNSIGNED INT_SYM { $$.set(&type_handler_ulonglong); } + | DECIMAL_SYM float_options { $$.set(&type_handler_newdecimal, $2); } + | DOUBLE_SYM opt_precision { $$.set(&type_handler_double, $2); } ; cast_type_temporal: - DATE_SYM { $$.set(ITEM_CAST_DATE); } - | TIME_SYM opt_field_length { $$.set(ITEM_CAST_TIME, 0, $2); } - | DATETIME opt_field_length { $$.set(ITEM_CAST_DATETIME, 0, $2); } + DATE_SYM { $$.set(&type_handler_newdate); } + | TIME_SYM opt_field_length { $$.set(&type_handler_time2, 0, $2); } + | DATETIME opt_field_length { $$.set(&type_handler_datetime2, 0, $2); } ; opt_expr_list: @@ -10762,16 +10862,6 @@ ident_list: } ; -opt_expr: - /* empty */ { $$= NULL; } - | expr { $$= $1; } - ; - -opt_else: - /* empty */ { $$= NULL; } - | ELSE expr { $$= $2; } - ; - when_list: WHEN_SYM expr THEN_SYM expr { @@ -10789,6 +10879,15 @@ when_list: } ; +when_list_opt_else: + when_list + | when_list ELSE expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + /* Equivalent to <table reference> in the SQL:2003 standard. */ /* Warning - may return NULL in case of incomplete SELECT */ table_ref: @@ -10798,7 +10897,7 @@ table_ref: LEX *lex= Lex; if (!($$= lex->current_select->nest_last_join(thd))) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -11053,6 +11152,7 @@ table_primary_derived: are no outer parentheses, add_table_to_list() will throw error in this case */ LEX *lex=Lex; + lex->check_automatic_up(UNSPECIFIED_TYPE); SELECT_LEX *sel= lex->current_select; SELECT_LEX_UNIT *unit= sel->master_unit(); lex->current_select= sel= unit->outer_select(); @@ -11068,17 +11168,13 @@ table_primary_derived: lex->pop_context(); lex->nest_level--; } - /*else if (($3->select_lex && - $3->select_lex->master_unit()->is_union() && - ($3->select_lex->master_unit()->first_select() == - $3->select_lex || !$3->lifted)) || $5)*/ else if ($5 != NULL) { /* Tables with or without joins within parentheses cannot have aliases, and we ruled out derived tables above. */ - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } else @@ -11143,7 +11239,7 @@ select_derived_union: { if ($1) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -11151,7 +11247,7 @@ select_derived_union: { if ($1) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -11207,7 +11303,7 @@ select_derived: MYSQL_YYABORT; if (!$2 && $$) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -11234,11 +11330,11 @@ select_derived2: if (!lex->expr_allows_subselect || lex->sql_command == (int)SQLCOM_PURGE) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || - mysql_new_select(lex, 1)) + mysql_new_select(lex, 1, NULL)) MYSQL_YYABORT; mysql_init_select(lex); lex->current_select->linkage= DERIVED_TABLE_TYPE; @@ -11332,7 +11428,7 @@ key_usage_element: ident { Select->add_index_hint(thd, $1.str, $1.length); } | PRIMARY_SYM - { Select->add_index_hint(thd, (char *)"PRIMARY", 7); } + { Select->add_index_hint(thd, "PRIMARY", 7); } ; key_usage_list: @@ -11408,7 +11504,7 @@ opt_table_alias: /* empty */ { $$=0; } | table_alias ident { - $$= (LEX_STRING*) thd->memdup(&$2,sizeof(LEX_STRING)); + $$= (LEX_CSTRING*) thd->memdup(&$2,sizeof(LEX_STRING)); if ($$ == NULL) MYSQL_YYABORT; } @@ -11544,7 +11640,7 @@ window_def: if (Select->add_window_def(thd, $1, lex->win_ref, Select->group_list, Select->order_list, - lex->win_frame )) + lex->win_frame)) MYSQL_YYABORT; } ; @@ -11561,7 +11657,7 @@ opt_window_ref: /* empty */ {} | ident { - thd->lex->win_ref= (LEX_STRING *) thd->memdup(&$1, sizeof(LEX_STRING)); + thd->lex->win_ref= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING)); if (thd->lex->win_ref == NULL) MYSQL_YYABORT; } @@ -11726,13 +11822,13 @@ order_clause: yet. */ SELECT_LEX *first_sl= unit->first_select(); - if (!unit->is_union() && + if (!unit->is_unit_op() && (first_sl->order_list.elements || first_sl->select_limit) && unit->add_fake_select_lex(thd)) MYSQL_YYABORT; } - if (sel->master_unit()->is_union() && !sel->braces) + if (sel->master_unit()->is_unit_op() && !sel->braces) { /* At this point we don't know yet whether this is the last @@ -11772,7 +11868,7 @@ limit_clause_init: LIMIT { SELECT_LEX *sel= Select; - if (sel->master_unit()->is_union() && !sel->braces) + if (sel->master_unit()->is_unit_op() && !sel->braces) { /* Move LIMIT that belongs to UNION to fake_select_lex */ Lex->current_select= sel->master_unit()->fake_select_lex; @@ -11825,32 +11921,21 @@ limit_options: ; limit_option: - ident + ident_with_tok_start { - Item_splocal *splocal; LEX *lex= thd->lex; Lex_input_stream *lip= & thd->m_parser_state->m_lip; - sp_variable *spv; - sp_pcontext *spc = lex->spcont; - if (spc && (spv = spc->find_variable($1, false))) - { - splocal= new (thd->mem_root) - Item_splocal(thd, $1, spv->offset, spv->sql_type(), - lip->get_tok_start() - lex->sphead->m_tmp_query, - lip->get_ptr() - lip->get_tok_start()); - if (splocal == NULL) - MYSQL_YYABORT; -#ifndef DBUG_OFF - splocal->m_sp= lex->sphead; -#endif - lex->safe_to_cache_query=0; - } - else - my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str)); - if (splocal->type() != Item::INT_ITEM) - my_yyabort_error((ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0))); - splocal->limit_clause_param= TRUE; - $$= splocal; + if (!($$= lex->create_item_limit(thd, &$1, + $1.m_pos, lip->get_tok_end()))) + MYSQL_YYABORT; + } + | ident_with_tok_start '.' ident + { + LEX *lex= thd->lex; + Lex_input_stream *lip= & thd->m_parser_state->m_lip; + if (!($$= lex->create_item_limit(thd, &$1, &$3, + $1.m_pos, lip->get_ptr()))) + MYSQL_YYABORT; } | param_marker { @@ -11896,23 +11981,26 @@ delete_limit_clause: Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); sel->explicit_limit= 1; } - | LIMIT ROWS_SYM EXAMINED_SYM { my_parse_error(thd, ER_SYNTAX_ERROR); MYSQL_YYABORT; } - | LIMIT limit_option ROWS_SYM EXAMINED_SYM { my_parse_error(thd, ER_SYNTAX_ERROR); MYSQL_YYABORT; } + | LIMIT ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; } + | LIMIT limit_option ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; } + ; + +opt_plus: + /* empty */ + | '+' ; int_num: - NUM { int error; $$= (int) my_strtoll10($1.str, (char**) 0, &error); } + opt_plus NUM { int error; $$= (int) my_strtoll10($2.str, (char**) 0, &error); } | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - | '-' LONG_NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } - ; ulong_num: - NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + opt_plus NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } - | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } - | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } - | DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } - | FLOAT_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | opt_plus LONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus FLOAT_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } ; real_ulong_num: @@ -11923,12 +12011,19 @@ real_ulong_num: | dec_num_error { MYSQL_YYABORT; } ; +longlong_num: + opt_plus NUM { int error; $$= (longlong) my_strtoll10($2.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + + ulonglong_num: - NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } - | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } - | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } - | DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } - | FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + opt_plus NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus LONG_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } ; real_ulonglong_num: @@ -11941,7 +12036,7 @@ real_ulonglong_num: dec_num_error: dec_num - { my_parse_error(thd, ER_ONLY_INTEGERS_ALLOWED); } + { thd->parse_error(ER_ONLY_INTEGERS_ALLOWED); } ; dec_num: @@ -11954,6 +12049,12 @@ choice: | DEFAULT { $$= HA_CHOICE_UNDEF; } ; +bool: + ulong_num { $$= $1 != 0; } + | TRUE_SYM { $$= 1; } + | FALSE_SYM { $$= 0; } + + procedure_clause: PROCEDURE_SYM ident /* Procedure name */ { @@ -11966,7 +12067,7 @@ procedure_clause: lex->proc_list.next= &lex->proc_list.first; Item_field *item= new (thd->mem_root) Item_field(thd, &lex->current_select->context, - NULL, NULL, $2.str); + NULL, NULL, &$2); if (item == NULL) MYSQL_YYABORT; if (add_proc_to_list(thd, item)) @@ -12003,7 +12104,7 @@ procedure_item: { if (add_proc_to_list(thd, $2)) MYSQL_YYABORT; - if (!$2->name) + if (!$2->name.str || $2->name.str == item_empty_name) $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); } ; @@ -12046,19 +12147,24 @@ select_var_ident: select_outvar select_outvar: '@' ident_or_text { - $$ = Lex->result ? new (thd->mem_root) my_var_user($2) : NULL; + $$ = Lex->result ? new (thd->mem_root) my_var_user(&$2) : NULL; } | ident_or_text { sp_variable *t; - if (!Lex->spcont || !(t= Lex->spcont->find_variable($1, false))) + if (!Lex->spcont || !(t= Lex->spcont->find_variable(&$1, false))) my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str)); $$ = Lex->result ? (new (thd->mem_root) - my_var_sp($1, t->offset, t->sql_type(), + my_var_sp(&$1, t->offset, t->type_handler(), Lex->sphead)) : NULL; } + | ident '.' ident + { + if (!($$= Lex->create_outvar(thd, &$1, &$3)) && Lex->result) + MYSQL_YYABORT; + } ; into: @@ -12156,31 +12262,29 @@ drop: { LEX *lex= thd->lex; sp_name *spname; - if ($4.str && check_db_name(&$4)) + if ($4.str && check_db_name((LEX_STRING*) &$4)) my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $4.str)); if (lex->sphead) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); lex->set_command(SQLCOM_DROP_FUNCTION, $3); - spname= new (thd->mem_root) sp_name($4, $6, true); + spname= new (thd->mem_root) sp_name(&$4, &$6, true); if (spname == NULL) MYSQL_YYABORT; - spname->init_qname(thd); lex->spname= spname; } | DROP FUNCTION_SYM opt_if_exists ident { LEX *lex= thd->lex; - LEX_STRING db= {0, 0}; + LEX_CSTRING db= {0, 0}; sp_name *spname; if (lex->sphead) my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); if (thd->db && lex->copy_db_to(&db.str, &db.length)) MYSQL_YYABORT; lex->set_command(SQLCOM_DROP_FUNCTION, $3); - spname= new (thd->mem_root) sp_name(db, $4, false); + spname= new (thd->mem_root) sp_name(&db, &$4, false); if (spname == NULL) MYSQL_YYABORT; - spname->init_qname(thd); lex->spname= spname; } | DROP PROCEDURE_SYM opt_if_exists sp_name @@ -12234,6 +12338,17 @@ drop: Lex->set_command(SQLCOM_DROP_SERVER, $3); Lex->server_options.reset($4); } + | DROP opt_temporary SEQUENCE_SYM opt_if_exists + + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_SEQUENCE, $2, $4); + lex->table_type= TABLE_TYPE_SEQUENCE; + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list + {} ; table_list: @@ -12452,6 +12567,16 @@ opt_equal: | equal {} ; +opt_with: + opt_equal {} + | WITH {} + ; + +opt_by: + opt_equal {} + | BY {} + ; + no_braces: '(' { @@ -12641,7 +12766,7 @@ table_wild_list: table_wild_one: ident opt_wild { - Table_ident *ti= new (thd->mem_root) Table_ident($1); + Table_ident *ti= new (thd->mem_root) Table_ident(&$1); if (ti == NULL) MYSQL_YYABORT; if (!Select->add_table_to_list(thd, @@ -12654,7 +12779,7 @@ table_wild_one: } | ident '.' ident opt_wild { - Table_ident *ti= new (thd->mem_root) Table_ident(thd, $1, $3, 0); + Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); if (ti == NULL) MYSQL_YYABORT; if (!Select->add_table_to_list(thd, @@ -12775,7 +12900,7 @@ show: { LEX *lex=Lex; lex->wild=0; - lex->ident=null_lex_str; + lex->ident= null_clex_str; mysql_init_select(lex); lex->current_select->parsing_place= SELECT_LIST; lex->create_info.init(); @@ -12798,7 +12923,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLES; - lex->select_lex.db= $3; + lex->select_lex.db= $3.str; if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)) MYSQL_YYABORT; } @@ -12806,7 +12931,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TRIGGERS; - lex->select_lex.db= $3; + lex->select_lex.db= $3.str; if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)) MYSQL_YYABORT; } @@ -12814,7 +12939,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_EVENTS; - lex->select_lex.db= $2; + lex->select_lex.db= $2.str; if (prepare_schema_table(thd, lex, 0, SCH_EVENTS)) MYSQL_YYABORT; } @@ -12822,7 +12947,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; - lex->select_lex.db= $3; + lex->select_lex.db= $3.str; if (prepare_schema_table(thd, lex, 0, SCH_TABLES)) MYSQL_YYABORT; } @@ -12830,7 +12955,7 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; - lex->select_lex.db= $3; + lex->select_lex.db= $3.str; if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)) MYSQL_YYABORT; } @@ -12862,8 +12987,8 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_FIELDS; - if ($5) - $4->change_db($5); + if ($5.str) + $4->change_db(&$5); if (prepare_schema_table(thd, lex, $4, SCH_COLUMNS)) MYSQL_YYABORT; } @@ -12890,8 +13015,8 @@ show_param: { LEX *lex= Lex; lex->sql_command= SQLCOM_SHOW_KEYS; - if ($4) - $3->change_db($4); + if ($4.str) + $3->change_db(&$4); if (prepare_schema_table(thd, lex, $3, SCH_STATISTICS)) MYSQL_YYABORT; } @@ -12998,7 +13123,15 @@ show_param: lex->sql_command = SQLCOM_SHOW_CREATE; if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) MYSQL_YYABORT; - lex->only_view= 1; + lex->table_type= TABLE_TYPE_VIEW; + } + | CREATE SEQUENCE_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) + MYSQL_YYABORT; + lex->table_type= TABLE_TYPE_SEQUENCE; } | MASTER_SYM STATUS_SYM { @@ -13012,7 +13145,7 @@ show_param: | SLAVE STATUS_SYM { LEX *lex= thd->lex; - lex->mi.connection_name= null_lex_str; + lex->mi.connection_name= null_clex_str; lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; lex->verbose= 0; } @@ -13097,12 +13230,12 @@ show_param: ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str, &in_plugin); if (!table || !table->old_format || !in_plugin) { - my_parse_error(thd, ER_SYNTAX_ERROR, $2); + thd->parse_error(ER_SYNTAX_ERROR, $2); MYSQL_YYABORT; } if (lex->wild && table->idx_field1 < 0) { - my_parse_error(thd, ER_SYNTAX_ERROR, $3); + thd->parse_error(ER_SYNTAX_ERROR, $3); MYSQL_YYABORT; } if (make_schema_select(thd, Lex->current_select, table)) @@ -13130,8 +13263,8 @@ opt_storage: ; opt_db: - /* empty */ { $$= 0; } - | from_or_in ident { $$= $2.str; } + /* empty */ { $$= null_clex_str; } + | from_or_in ident { $$= $2; } ; opt_full: @@ -13290,8 +13423,10 @@ opt_flush_lock: for (; tables; tables= tables->next_global) { tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); - tables->required_type= FRMTYPE_TABLE; /* Don't try to flush views. */ - tables->open_type= OT_BASE_ONLY; /* Ignore temporary tables. */ + /* Don't try to flush views. */ + tables->required_type= TABLE_TYPE_NORMAL; + /* Ignore temporary tables. */ + tables->open_type= OT_BASE_ONLY; } } ; @@ -13303,7 +13438,7 @@ flush_lock: { if (Lex->query_tables == NULL) // Table list can't be empty { - my_parse_error(thd, ER_NO_TABLES_USED); + thd->parse_error(ER_NO_TABLES_USED); MYSQL_YYABORT; } Lex->type|= REFRESH_FOR_EXPORT; @@ -13344,7 +13479,7 @@ flush_option: | LOGS_SYM { Lex->type|= REFRESH_LOG; - Lex->relay_log_connection_name= empty_lex_str; + Lex->relay_log_connection_name= empty_clex_str; } | STATUS_SYM { Lex->type|= REFRESH_STATUS; } @@ -13368,7 +13503,7 @@ flush_option: ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str); if (!table || !table->reset_table) { - my_parse_error(thd, ER_SYNTAX_ERROR, $2); + thd->parse_error(ER_SYNTAX_ERROR, $2); MYSQL_YYABORT; } Lex->view_list.push_back((LEX_STRING*) @@ -13682,7 +13817,7 @@ field_or_var: simple_ident_nospvar {$$= $1;} | '@' ident_or_text { - $$= new (thd->mem_root) Item_user_var_as_out_param(thd, $2); + $$= new (thd->mem_root) Item_user_var_as_out_param(thd, &$2); if ($$ == NULL) MYSQL_YYABORT; } @@ -13714,37 +13849,16 @@ load_data_set_elem: text_literal: TEXT_STRING { - LEX_STRING tmp; - CHARSET_INFO *cs_con= thd->variables.collation_connection; - CHARSET_INFO *cs_cli= thd->variables.character_set_client; - uint repertoire= thd->lex->text_string_is_7bit && - my_charset_is_ascii_based(cs_cli) ? - MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; - if (thd->charset_is_collation_connection || - (repertoire == MY_REPERTOIRE_ASCII && - my_charset_is_ascii_based(cs_con))) - tmp= $1; - else - { - if (thd->convert_string(&tmp, cs_con, $1.str, $1.length, cs_cli)) - MYSQL_YYABORT; - } - $$= new (thd->mem_root) Item_string(thd, tmp.str, tmp.length, - cs_con, - DERIVATION_COERCIBLE, - repertoire); - if ($$ == NULL) + if (!($$= thd->make_string_literal($1))) MYSQL_YYABORT; } | NCHAR_STRING { - uint repertoire= Lex->text_string_is_7bit ? - MY_REPERTOIRE_ASCII : MY_REPERTOIRE_UNICODE30; DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info)); $$= new (thd->mem_root) Item_string(thd, $1.str, $1.length, national_charset_info, DERIVATION_COERCIBLE, - repertoire); + $1.repertoire()); if ($$ == NULL) MYSQL_YYABORT; } @@ -13827,17 +13941,10 @@ hex_or_bin_String: param_marker: PARAM_MARKER { - LEX *lex= thd->lex; - Lex_input_stream *lip= YYLIP; - Item_param *item; - if (! lex->parsing_options.allows_variable) - my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0))); - const char *query_start= lex->sphead ? lex->sphead->m_tmp_query - : thd->query(); - item= new (thd->mem_root) Item_param(thd, lip->get_tok_start() - - query_start); - if (!($$= item) || lex->param_list.push_back(item, thd->mem_root)) - my_yyabort_error((ER_OUT_OF_RESOURCES, MYF(0))); + if (!($$= Lex->add_placeholder(thd, ¶m_clex_str, + YYLIP->get_tok_start(), + YYLIP->get_tok_start() + 1))) + MYSQL_YYABORT; } ; @@ -14028,7 +14135,7 @@ with_list_element: query_name opt_with_column_list { - $2= new List<LEX_STRING> (Lex->with_column_list); + $2= new List<LEX_CSTRING> (Lex->with_column_list); if ($2 == NULL) MYSQL_YYABORT; Lex->with_column_list.empty(); @@ -14055,13 +14162,13 @@ opt_with_column_list: with_column_list: ident { - Lex->with_column_list.push_back((LEX_STRING*) - thd->memdup(&$1, sizeof(LEX_STRING))); + Lex->with_column_list.push_back((LEX_CSTRING*) + thd->memdup(&$1, sizeof(LEX_CSTRING))); } | with_column_list ',' ident { - Lex->with_column_list.push_back((LEX_STRING*) - thd->memdup(&$3, sizeof(LEX_STRING))); + Lex->with_column_list.push_back((LEX_CSTRING*) + thd->memdup(&$3, sizeof(LEX_CSTRING))); } ; @@ -14069,7 +14176,7 @@ with_column_list: query_name: ident { - $$= (LEX_STRING *) thd->memdup(&$1, sizeof(LEX_STRING)); + $$= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING)); if ($$ == NULL) MYSQL_YYABORT; } @@ -14091,7 +14198,7 @@ table_wild: { SELECT_LEX *sel= Select; $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), - NullS, $1.str, "*"); + NullS, $1.str, &star_clex_str); if ($$ == NULL) MYSQL_YYABORT; sel->with_wild++; @@ -14103,7 +14210,7 @@ table_wild: NullS : $1.str; $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), schema, - $3.str,"*"); + $3.str, &star_clex_str); if ($$ == NULL) MYSQL_YYABORT; sel->with_wild++; @@ -14117,66 +14224,26 @@ order_ident: simple_ident: ident { - LEX *lex= thd->lex; Lex_input_stream *lip= YYLIP; - sp_variable *spv; - sp_pcontext *spc = lex->spcont; - if (spc && (spv = spc->find_variable($1, false))) - { - /* We're compiling a stored procedure and found a variable */ - if (! lex->parsing_options.allows_variable) - my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0))); - - Item_splocal *splocal; - splocal= new (thd->mem_root) - Item_splocal(thd, $1, spv->offset, spv->sql_type(), - lip->get_tok_start_prev() - lex->sphead->m_tmp_query, - lip->get_tok_end() - lip->get_tok_start_prev()); - if (splocal == NULL) - MYSQL_YYABORT; -#ifndef DBUG_OFF - splocal->m_sp= lex->sphead; -#endif - $$= splocal; - lex->safe_to_cache_query=0; - } - else - { - SELECT_LEX *sel=Select; - if ((sel->parsing_place != IN_HAVING) || - (sel->get_in_sum_expr() > 0)) - { - $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), - NullS, NullS, $1.str); - } - else - { - $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), - NullS, NullS, $1.str); - } - if ($$ == NULL) - MYSQL_YYABORT; - } + if (!($$= Lex->create_item_ident(thd, &$1, + lip->get_tok_start_prev(), + lip->get_tok_end()))) + MYSQL_YYABORT; + } + | simple_ident_q2 + | ident '.' ident + { + LEX *lex= thd->lex; + if (!($$= lex->create_item_ident(thd, &$1, &$3, + $1.m_pos, YYLIP->get_tok_end()))) + MYSQL_YYABORT; } - | simple_ident_q { $$= $1; } ; simple_ident_nospvar: ident { - SELECT_LEX *sel=Select; - if ((sel->parsing_place != IN_HAVING) || - (sel->get_in_sum_expr() > 0)) - { - $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), - NullS, NullS, $1.str); - } - else - { - $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), - NullS, NullS, $1.str); - } - if ($$ == NULL) + if (!($$= Lex->create_item_ident_nosp(thd, &$1))) MYSQL_YYABORT; } | simple_ident_q { $$= $1; } @@ -14185,125 +14252,21 @@ simple_ident_nospvar: simple_ident_q: ident '.' ident { - LEX *lex= thd->lex; - - /* - FIXME This will work ok in simple_ident_nospvar case because - we can't meet simple_ident_nospvar in trigger now. But it - should be changed in future. - */ - if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && - (!my_strcasecmp(system_charset_info, $1.str, "NEW") || - !my_strcasecmp(system_charset_info, $1.str, "OLD"))) - { - Item_trigger_field *trg_fld; - bool new_row= ($1.str[0]=='N' || $1.str[0]=='n'); - - if (lex->trg_chistics.event == TRG_EVENT_INSERT && - !new_row) - my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT")); - - if (lex->trg_chistics.event == TRG_EVENT_DELETE && - new_row) - my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE")); - - DBUG_ASSERT(!new_row || - (lex->trg_chistics.event == TRG_EVENT_INSERT || - lex->trg_chistics.event == TRG_EVENT_UPDATE)); - const bool tmp_read_only= - !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE); - trg_fld= new (thd->mem_root) - Item_trigger_field(thd, Lex->current_context(), - new_row ? - Item_trigger_field::NEW_ROW: - Item_trigger_field::OLD_ROW, - $3.str, - SELECT_ACL, - tmp_read_only); - if (trg_fld == NULL) - MYSQL_YYABORT; - - /* - Let us add this item to list of all Item_trigger_field objects - in trigger. - */ - lex->trg_table_fields.link_in_list(trg_fld, - &trg_fld->next_trg_field); - - $$= trg_fld; - } - else - { - SELECT_LEX *sel= lex->current_select; - if (sel->no_table_names_allowed) - { - my_error(ER_TABLENAME_NOT_ALLOWED_HERE, - MYF(0), $1.str, thd->where); - } - if ((sel->parsing_place != IN_HAVING) || - (sel->get_in_sum_expr() > 0)) - { - $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), - NullS, $1.str, $3.str); - } - else - { - $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), - NullS, $1.str, $3.str); - } - if ($$ == NULL) - MYSQL_YYABORT; - } + if (!($$= Lex->create_item_ident_nospvar(thd, &$1, &$3))) + MYSQL_YYABORT; } - | '.' ident '.' ident - { - LEX *lex= thd->lex; - SELECT_LEX *sel= lex->current_select; - if (sel->no_table_names_allowed) - { - my_error(ER_TABLENAME_NOT_ALLOWED_HERE, - MYF(0), $2.str, thd->where); - } - if ((sel->parsing_place != IN_HAVING) || - (sel->get_in_sum_expr() > 0)) - { - $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), - NullS, $2.str, $4.str); + | simple_ident_q2 + ; - } - else - { - $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), - NullS, $2.str, $4.str); - } - if ($$ == NULL) +simple_ident_q2: + '.' ident '.' ident + { + if (!($$= Lex->create_item_ident(thd, &null_clex_str, &$2, &$4))) MYSQL_YYABORT; } | ident '.' ident '.' ident { - LEX *lex= thd->lex; - SELECT_LEX *sel= lex->current_select; - const char* schema= (thd->client_capabilities & CLIENT_NO_SCHEMA ? - NullS : $1.str); - if (sel->no_table_names_allowed) - { - my_error(ER_TABLENAME_NOT_ALLOWED_HERE, - MYF(0), $3.str, thd->where); - } - if ((sel->parsing_place != IN_HAVING) || - (sel->get_in_sum_expr() > 0)) - { - $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), - schema, - $3.str, $5.str); - } - else - { - $$= new (thd->mem_root) Item_ref(thd, Lex->current_context(), - schema, - $3.str, $5.str); - } - if ($$ == NULL) + if (!($$= Lex->create_item_ident(thd, &$1, &$3, &$5))) MYSQL_YYABORT; } ; @@ -14333,20 +14296,20 @@ field_ident: table_ident: ident { - $$= new (thd->mem_root) Table_ident($1); + $$= new (thd->mem_root) Table_ident(&$1); if ($$ == NULL) MYSQL_YYABORT; } | ident '.' ident { - $$= new (thd->mem_root) Table_ident(thd, $1, $3, 0); + $$= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); if ($$ == NULL) MYSQL_YYABORT; } | '.' ident { /* For Delphi */ - $$= new (thd->mem_root) Table_ident($2); + $$= new (thd->mem_root) Table_ident(&$2); if ($$ == NULL) MYSQL_YYABORT; } @@ -14355,13 +14318,13 @@ table_ident: table_ident_opt_wild: ident opt_wild { - $$= new (thd->mem_root) Table_ident($1); + $$= new (thd->mem_root) Table_ident(&$1); if ($$ == NULL) MYSQL_YYABORT; } | ident '.' ident opt_wild { - $$= new (thd->mem_root) Table_ident(thd, $1, $3, 0); + $$= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); if ($$ == NULL) MYSQL_YYABORT; } @@ -14370,8 +14333,8 @@ table_ident_opt_wild: table_ident_nodb: ident { - LEX_STRING db={(char*) any_db,3}; - $$= new (thd->mem_root) Table_ident(thd, db, $1, 0); + LEX_CSTRING db={(char*) any_db,3}; + $$= new (thd->mem_root) Table_ident(thd, &db, &$1, 0); if ($$ == NULL) MYSQL_YYABORT; } @@ -14396,9 +14359,12 @@ IDENT_sys: } else { - if (thd->convert_with_error(system_charset_info, &$$, + LEX_STRING to; + if (thd->convert_with_error(system_charset_info, &to, thd->charset(), $1.str, $1.length)) MYSQL_YYABORT; + $$.str= to.str; + $$.length= to.length; } } ; @@ -14410,9 +14376,12 @@ TEXT_STRING_sys: $$= $1; else { - if (thd->convert_string(&$$, system_charset_info, + LEX_STRING to; + if (thd->convert_string(&to, system_charset_info, $1.str, $1.length, thd->charset())) MYSQL_YYABORT; + $$.str= to.str; + $$.length= to.length; } } ; @@ -14424,10 +14393,13 @@ TEXT_STRING_literal: $$= $1; else { - if (thd->convert_string(&$$, thd->variables.collation_connection, + LEX_STRING to; + if (thd->convert_string(&to, thd->variables.collation_connection, $1.str, $1.length, thd->charset())) MYSQL_YYABORT; - } + $$.str= to.str; + $$.length= to.length; + } } ; @@ -14438,22 +14410,45 @@ TEXT_STRING_filesystem: $$= $1; else { - if (thd->convert_string(&$$, + LEX_STRING to; + if (thd->convert_string(&to, thd->variables.character_set_filesystem, $1.str, $1.length, thd->charset())) MYSQL_YYABORT; + $$.str= to.str; + $$.length= to.length; } } ; ident: - IDENT_sys { $$=$1; } + IDENT_sys + { + (LEX_CSTRING &)$$= $1; + $$.m_pos= (char *) YYLIP->get_tok_start_prev(); + } | keyword { $$.str= thd->strmake($1.str, $1.length); if ($$.str == NULL) MYSQL_YYABORT; $$.length= $1.length; + $$.m_pos= (char *) YYLIP->get_tok_start_prev(); + } + ; + +ident_with_tok_start: + IDENT_sys + { + (LEX_CSTRING &)$$= $1; + $$.m_pos= (char *) YYLIP->get_tok_start(); + } + | keyword + { + if (!($$.str= thd->strmake($1.str, $1.length))) + MYSQL_YYABORT; + $$.length= $1.length; + $$.m_pos= (char *) YYLIP->get_tok_start(); } ; @@ -14477,10 +14472,10 @@ ident_or_text: user_maybe_role: ident_or_text { - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + if (!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) MYSQL_YYABORT; $$->user = $1; - $$->host= null_lex_str; // User or Role, see get_current_user() + $$->host= null_clex_str; // User or Role, see get_current_user() $$->reset_auth(); if (check_string_char_length(&$$->user, ER_USERNAME, @@ -14490,7 +14485,7 @@ user_maybe_role: } | ident_or_text '@' ident_or_text { - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + if (!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) MYSQL_YYABORT; $$->user = $1; $$->host=$3; $$->reset_auth(); @@ -14507,7 +14502,7 @@ user_maybe_role: It's OK to use in-place lowercase as long as the character set is utf8. */ - my_casedn_str(system_charset_info, $$->host.str); + my_casedn_str(system_charset_info, (char*) $$->host.str); } else { @@ -14523,8 +14518,8 @@ user_maybe_role: if (!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER)))) MYSQL_YYABORT; $$->user= current_user; - $$->plugin= empty_lex_str; - $$->auth= empty_lex_str; + $$->plugin= empty_clex_str; + $$->auth= empty_clex_str; } ; @@ -14541,27 +14536,23 @@ user: user_maybe_role /* Keyword that we allow for identifiers (except SP labels) */ keyword: keyword_sp {} + | keyword_sp_verb_clause{} | ASCII_SYM {} | BACKUP_SYM {} - | BEGIN_SYM {} | BINLOG_SYM {} | BYTE_SYM {} | CACHE_SYM {} | CHARSET {} | CHECKSUM_SYM {} | CHECKPOINT_SYM {} - | CLOSE_SYM {} | COLUMN_ADD_SYM {} | COLUMN_CHECK_SYM {} | COLUMN_CREATE_SYM {} | COLUMN_DELETE_SYM {} | COLUMN_GET_SYM {} | COMMENT_SYM {} - | COMMIT_SYM {} | CONTAINS_SYM {} | DEALLOCATE_SYM {} - | DO_SYM {} - | END {} | EXAMINED_SYM {} | EXCLUDE_SYM {} | EXECUTE_SYM {} @@ -14570,13 +14561,11 @@ keyword: | FOLLOWING_SYM {} | FORMAT_SYM {} | GET_SYM {} - | HANDLER_SYM {} | HELP_SYM {} | HOST_SYM {} | INSTALL_SYM {} | LANGUAGE_SYM {} | NO_SYM {} - | OPEN_SYM {} | OPTION {} | OPTIONS_SYM {} | OTHERS_SYM {} @@ -14587,14 +14576,10 @@ keyword: | PRECEDING_SYM {} | PREPARE_SYM {} | REMOVE_SYM {} - | REPAIR {} | RESET_SYM {} | RESTORE_SYM {} - | ROLLBACK_SYM {} - | SAVEPOINT_SYM {} | SECURITY_SYM {} | SERVER_SYM {} - | SHUTDOWN {} | SIGNED_SYM {} | SOCKET_SYM {} | SLAVE {} @@ -14604,7 +14589,6 @@ keyword: | STOP_SYM {} | STORED_SYM {} | TIES_SYM {} - | TRUNCATE_SYM {} | UNICODE_SYM {} | UNINSTALL_SYM {} | UNBOUNDED_SYM {} @@ -14620,6 +14604,73 @@ keyword: * conflicts. */ keyword_sp: + keyword_sp_data_type + | keyword_sp_not_data_type + ; + + +/* + Keywords that start a statement. + Generally allowed as identifiers (e.g. table, column names) + - not allowed as SP label names + - not allowed as variable names in Oracle-style assignments: + xxx:=10 +*/ +keyword_sp_verb_clause: + BEGIN_SYM { /* Compound. Reserved in Oracle */ } + | CLOSE_SYM { /* Verb clause. Reserved in Oracle */ } + | COMMIT_SYM { /* Verb clause. Reserved in Oracle */ } + | DO_SYM { /* Verb clause */ } + | END { /* Compound. Reserved in Oracle */ } + | HANDLER_SYM { /* Verb clause */ } + | OPEN_SYM { /* Verb clause. Reserved in Oracle */ } + | REPAIR { /* Verb clause */ } + | ROLLBACK_SYM { /* Verb clause. Reserved in Oracle */ } + | SAVEPOINT_SYM { /* Verb clause. Reserved in Oracle */ } + | SHUTDOWN { /* Verb clause */ } + | TRUNCATE_SYM { /* Verb clause. Reserved in Oracle */ } + ; + + +/* + These keywords are generally allowed as identifiers, + but not allowed as non-delimited SP variable names in sql_mode=ORACLE. +*/ +keyword_sp_data_type: + BIT_SYM {} + | BOOLEAN_SYM {} /* PLSQL-R */ + | BOOL_SYM {} + | CLOB {} + | DATE_SYM {} /* Oracle-R, PLSQL-R */ + | DATETIME {} + | ENUM {} + | FIXED_SYM {} + | GEOMETRYCOLLECTION {} + | GEOMETRY_SYM {} + | JSON_SYM {} + | LINESTRING {} + | MEDIUM_SYM {} + | MULTILINESTRING {} + | MULTIPOINT {} + | MULTIPOLYGON {} + | NATIONAL_SYM {} + | NCHAR_SYM {} + | NUMBER_SYM {} /* Oracle-R, PLSQL-R */ + | NVARCHAR_SYM {} + | POINT_SYM {} + | POLYGON {} + | RAW {} /* Oracle-R */ + | ROW_SYM {} + | SERIAL_SYM {} + | TEXT_SYM {} + | TIMESTAMP {} + | TIME_SYM {} /* Oracle-R */ + | VARCHAR2 {} /* Oracle-R, PLSQL-R */ + | YEAR_SYM {} + ; + + +keyword_sp_not_data_type: ACTION {} | ADDDATE_SYM {} | ADMIN_SYM {} @@ -14637,10 +14688,7 @@ keyword_sp: | AUTO_SYM {} | AVG_ROW_LENGTH {} | AVG_SYM {} - | BIT_SYM {} | BLOCK_SYM {} - | BOOL_SYM {} - | BOOLEAN_SYM {} | BTREE_SYM {} | CASCADED {} | CATALOG_NAME_SYM {} @@ -14675,11 +14723,12 @@ keyword_sp: */ | CURRENT_SYM {} | CURSOR_NAME_SYM {} + | CYCLE_SYM {} | DATA_SYM {} | DATAFILE_SYM {} - | DATETIME {} - | DATE_SYM {} + | DATE_FORMAT_SYM {} | DAY_SYM {} + | DECODE_SYM {} | DEFINER_SYM {} | DELAY_KEY_WRITE_SYM {} | DES_KEY_FILE {} @@ -14691,8 +14740,8 @@ keyword_sp: | DUMPFILE {} | DUPLICATE_SYM {} | DYNAMIC_SYM {} + | ELSIF_SYM {} | ENDS_SYM {} - | ENUM {} | ENGINE_SYM {} | ENGINES_SYM {} | ERROR_SYM {} @@ -14701,6 +14750,7 @@ keyword_sp: | EVENT_SYM {} | EVENTS_SYM {} | EVERY_SYM {} + | EXCEPTION_SYM {} | EXCHANGE_SYM {} | EXPANSION_SYM {} | EXPORT_SYM {} @@ -14713,14 +14763,12 @@ keyword_sp: | FULL {} | FILE_SYM {} | FIRST_SYM {} - | FIXED_SYM {} | GENERAL {} | GENERATED_SYM {} - | GEOMETRY_SYM {} - | GEOMETRYCOLLECTION {} | GET_FORMAT {} | GRANTS {} | GLOBAL_SYM {} + | GOTO_SYM {} | HASH_SYM {} | HARD_SYM {} | HOSTS_SYM {} @@ -14728,6 +14776,7 @@ keyword_sp: | ID_SYM {} | IDENTIFIED_SYM {} | IGNORE_SERVER_IDS_SYM {} + | INCREMENT_SYM {} | IMMEDIATE_SYM {} /* SQL-2003-R */ | INVOKER_SYM {} | IMPORT {} @@ -14736,16 +14785,16 @@ keyword_sp: | IO_SYM {} | IPC_SYM {} | ISOLATION {} + | ISOPEN_SYM {} | ISSUER_SYM {} - | JSON_SYM {} | INSERT_METHOD {} | KEY_BLOCK_SIZE {} | LAST_VALUE {} | LAST_SYM {} + | LASTVAL_SYM {} | LEAVES {} | LESS_SYM {} | LEVEL_SYM {} - | LINESTRING {} | LIST_SYM {} | LOCAL_SYM {} | LOCKS_SYM {} @@ -14779,34 +14828,34 @@ keyword_sp: | MAX_STATEMENT_TIME_SYM {} | MAX_UPDATES_PER_HOUR {} | MAX_USER_CONNECTIONS_SYM {} - | MEDIUM_SYM {} | MEMORY_SYM {} | MERGE_SYM {} | MESSAGE_TEXT_SYM {} | MICROSECOND_SYM {} | MIGRATE_SYM {} | MINUTE_SYM {} + | MINVALUE_SYM {} | MIN_ROWS {} | MODIFY_SYM {} | MODE_SYM {} | MONTH_SYM {} - | MULTILINESTRING {} - | MULTIPOINT {} - | MULTIPOLYGON {} | MUTEX_SYM {} | MYSQL_SYM {} | MYSQL_ERRNO_SYM {} | NAME_SYM {} | NAMES_SYM {} - | NATIONAL_SYM {} - | NCHAR_SYM {} | NEXT_SYM {} + | NEXTVAL_SYM {} | NEW_SYM {} + | NOCACHE_SYM {} + | NOCYCLE_SYM {} + | NOMINVALUE_SYM {} + | NOMAXVALUE_SYM {} | NO_WAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} - | NUMBER_SYM {} - | NVARCHAR_SYM {} + | NOTFOUND_SYM {} + | OF_SYM {} /* SQL-1999-R, Oracle-R */ | OFFSET_SYM {} | OLD_PASSWORD_SYM {} | ONE_SYM {} @@ -14822,10 +14871,9 @@ keyword_sp: | PHASE_SYM {} | PLUGIN_SYM {} | PLUGINS_SYM {} - | POINT_SYM {} - | POLYGON {} | PRESERVE_SYM {} | PREV_SYM {} + | PREVIOUS_SYM {} | PRIVILEGES {} | PROCESS {} | PROCESSLIST_SYM {} @@ -14835,6 +14883,7 @@ keyword_sp: | QUARTER_SYM {} | QUERY_SYM {} | QUICK {} + | RAISE_SYM {} | READ_ONLY_SYM {} | REBUILD_SYM {} | RECOVER_SYM {} @@ -14851,23 +14900,27 @@ keyword_sp: | REPEATABLE_SYM {} | REPLICATION {} | RESOURCES {} + | RESTART_SYM {} | RESUME_SYM {} | RETURNED_SQLSTATE_SYM {} | RETURNS_SYM {} + | REUSE_SYM {} /* Oracle-R */ | REVERSE_SYM {} | ROLE_SYM {} | ROLLUP_SYM {} | ROUTINE_SYM {} + | ROWCOUNT_SYM {} + | ROWTYPE_SYM {} | ROW_COUNT_SYM {} | ROW_FORMAT_SYM {} - | ROW_SYM {} | RTREE_SYM {} | SCHEDULE_SYM {} | SCHEMA_NAME_SYM {} | SECOND_SYM {} - | SERIAL_SYM {} + | SEQUENCE_SYM {} | SERIALIZABLE_SYM {} | SESSION_SYM {} + | SETVAL_SYM {} | SIMPLE_SYM {} | SHARE_SYM {} | SLAVE_POS_SYM {} @@ -14900,15 +14953,12 @@ keyword_sp: | TABLESPACE {} | TEMPORARY {} | TEMPTABLE_SYM {} - | TEXT_SYM {} | THAN_SYM {} | TRANSACTION_SYM {} | TRANSACTIONAL_SYM {} | TRIGGERS_SYM {} - | TIMESTAMP {} | TIMESTAMP_ADD {} | TIMESTAMP_DIFF {} - | TIME_SYM {} | TYPES_SYM {} | TYPE_SYM {} | UDF_RETURNS_SYM {} @@ -14932,8 +14982,7 @@ keyword_sp: | WORK_SYM {} | X509_SYM {} | XML_SYM {} - | YEAR_SYM {} - | VIA_SYM {} + | VIA_SYM {} ; /* @@ -14947,22 +14996,15 @@ set: SET { LEX *lex=Lex; - lex->sql_command= SQLCOM_SET_OPTION; - mysql_init_select(lex); - lex->option_type=OPT_SESSION; + lex->set_stmt_init(); lex->var_list.empty(); - lex->autocommit= 0; sp_create_assignment_lex(thd, yychar == YYEMPTY); } start_option_value_list {} | SET STATEMENT_SYM { - LEX *lex= Lex; - mysql_init_select(lex); - lex->option_type= OPT_SESSION; - lex->sql_command= SQLCOM_SET_OPTION; - lex->autocommit= 0; + Lex->set_stmt_init(); } set_stmt_option_value_following_option_type_list { @@ -15092,7 +15134,7 @@ option_value_following_option_type: if ($1.var && $1.var != trg_new_row_fake_var) { /* It is a system variable. */ - if (set_system_variable(thd, &$1, lex->option_type, $3)) + if (lex->set_system_variable(&$1, lex->option_type, $3)) MYSQL_YYABORT; } else @@ -15101,7 +15143,7 @@ option_value_following_option_type: Not in trigger assigning value to new row, and option_type preceding local variable is illegal. */ - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } } @@ -15109,42 +15151,30 @@ option_value_following_option_type: /* Option values without preceding option_type. */ option_value_no_option_type: - internal_variable_name equal set_expr_or_default + ident equal set_expr_or_default { - LEX *lex= Lex; - - if ($1.var == trg_new_row_fake_var) - { - /* We are in trigger and assigning value to field of new row */ - if (set_trigger_new_row(thd, &$1.base_name, $3)) - MYSQL_YYABORT; - } - else if ($1.var) - { - /* It is a system variable. */ - if (set_system_variable(thd, &$1, lex->option_type, $3)) - MYSQL_YYABORT; - } - else - { - sp_pcontext *spc= lex->spcont; - sp_variable *spv= spc->find_variable($1.base_name, false); - - /* It is a local variable. */ - if (set_local_variable(thd, spv, $3)) - MYSQL_YYABORT; - } + struct sys_var_with_base var; + if (Lex->init_internal_variable(&var, &$1) || + Lex->set_variable(&var, $3)) + MYSQL_YYABORT; } - | '@' ident_or_text equal expr + | ident '.' ident equal set_expr_or_default { - Item_func_set_user_var *item; - item= new (thd->mem_root) Item_func_set_user_var(thd, $2, $4); - if (item == NULL) + DBUG_ASSERT(Lex->var_list.is_empty()); + if (Lex->set_variable(&$1, &$3, $5)) MYSQL_YYABORT; - set_var_user *var= new (thd->mem_root) set_var_user(item); - if (var == NULL) + } + | DEFAULT '.' ident equal set_expr_or_default + { + struct sys_var_with_base var; + if (Lex->init_default_internal_variable(&var, $3) || + Lex->set_variable(&var, $5)) + MYSQL_YYABORT; + } + | '@' ident_or_text equal expr + { + if (Lex->set_user_variable(thd, &$2, $4)) MYSQL_YYABORT; - Lex->var_list.push_back(var, thd->mem_root); } | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default { @@ -15155,7 +15185,7 @@ option_value_no_option_type: if (find_sys_var_null_base(thd, &tmp)) MYSQL_YYABORT; } - if (set_system_variable(thd, &tmp, $3, $6)) + if (Lex->set_system_variable(&tmp, $3, $6)) MYSQL_YYABORT; } | charset old_or_new_charset_name_or_default @@ -15176,15 +15206,11 @@ option_value_no_option_type: { LEX *lex= Lex; sp_pcontext *spc= lex->spcont; - LEX_STRING names; - - names.str= (char *)"names"; - names.length= 5; - if (spc && spc->find_variable(names, false)) + LEX_CSTRING names= { STRING_WITH_LEN("names") }; + if (spc && spc->find_variable(&names, false)) my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); else - my_parse_error(thd, ER_SYNTAX_ERROR); - + thd->parse_error(); MYSQL_YYABORT; } | NAMES_SYM charset_name_or_default opt_collate @@ -15261,77 +15287,18 @@ option_value_no_option_type: internal_variable_name: ident { - sp_pcontext *spc= thd->lex->spcont; - sp_variable *spv; - - /* Best effort lookup for system variable. */ - if (!spc || !(spv = spc->find_variable($1, false))) - { - struct sys_var_with_base tmp= {NULL, $1}; - - /* Not an SP local variable */ - if (find_sys_var_null_base(thd, &tmp)) - MYSQL_YYABORT; - - $$= tmp; - } - else - { - /* - Possibly an SP local variable (or a shadowed sysvar). - Will depend on the context of the SET statement. - */ - $$.var= NULL; - $$.base_name= $1; - } + if (Lex->init_internal_variable(&$$, &$1)) + MYSQL_YYABORT; } | ident '.' ident { - LEX *lex= Lex; - if (check_reserved_words(&$1)) - { - my_parse_error(thd, ER_SYNTAX_ERROR); + if (Lex->init_internal_variable(&$$, &$1, &$3)) MYSQL_YYABORT; - } - if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && - (!my_strcasecmp(system_charset_info, $1.str, "NEW") || - !my_strcasecmp(system_charset_info, $1.str, "OLD"))) - { - if ($1.str[0]=='O' || $1.str[0]=='o') - my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "OLD", "")); - if (lex->trg_chistics.event == TRG_EVENT_DELETE) - { - my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), - "NEW", "on DELETE"); - MYSQL_YYABORT; - } - if (lex->trg_chistics.action_time == TRG_ACTION_AFTER) - my_yyabort_error((ER_TRG_CANT_CHANGE_ROW, MYF(0), "NEW", "after ")); - /* This special combination will denote field of NEW row */ - $$.var= trg_new_row_fake_var; - $$.base_name= $3; - } - else - { - sys_var *tmp=find_sys_var(thd, $3.str, $3.length); - if (!tmp) - MYSQL_YYABORT; - if (!tmp->is_struct()) - my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); - $$.var= tmp; - $$.base_name= $1; - } } | DEFAULT '.' ident { - sys_var *tmp=find_sys_var(thd, $3.str, $3.length); - if (!tmp) + if (Lex->init_default_internal_variable(&$$, $3)) MYSQL_YYABORT; - if (!tmp->is_struct()) - my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), $3.str); - $$.var= tmp; - $$.base_name.str= (char*) "default"; - $$.base_name.length= 7; } ; @@ -15352,7 +15319,7 @@ transaction_access_mode: set_var *var= (new (thd->mem_root) set_var(thd, lex->option_type, find_sys_var(thd, "tx_read_only"), - &null_lex_str, + &null_clex_str, item)); if (var == NULL) MYSQL_YYABORT; @@ -15370,7 +15337,7 @@ isolation_level: set_var *var= (new (thd->mem_root) set_var(thd, lex->option_type, find_sys_var(thd, "tx_isolation"), - &null_lex_str, + &null_clex_str, item)); if (var == NULL) MYSQL_YYABORT; @@ -15395,15 +15362,15 @@ opt_for_user: { LEX *lex= thd->lex; sp_pcontext *spc= lex->spcont; - LEX_STRING pw= { C_STRING_WITH_LEN("password") }; + LEX_CSTRING pw= { STRING_WITH_LEN("password") }; - if (spc && spc->find_variable(pw, false)) + if (spc && spc->find_variable(&pw, false)) my_yyabort_error((ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str)); if (!(lex->definer= (LEX_USER*) thd->calloc(sizeof(LEX_USER)))) MYSQL_YYABORT; lex->definer->user= current_user; - lex->definer->plugin= empty_lex_str; - lex->definer->auth= empty_lex_str; + lex->definer->plugin= empty_clex_str; + lex->definer->auth= empty_clex_str; } | FOR_SYM user equal { Lex->definer= $2; } ; @@ -15562,7 +15529,7 @@ handler: ; handler_read_or_scan: - handler_scan_function { Lex->ident= null_lex_str; } + handler_scan_function { Lex->ident= null_clex_str; } | ident handler_rkey_function { Lex->ident= $1; } ; @@ -15612,25 +15579,13 @@ revoke_command: } | grant_privileges ON FUNCTION_SYM grant_ident FROM user_and_role_list { - LEX *lex= Lex; - if (lex->columns.elements) - { - my_parse_error(thd, ER_SYNTAX_ERROR); + if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_FUNCTION)) MYSQL_YYABORT; - } - lex->sql_command= SQLCOM_REVOKE; - lex->type= TYPE_ENUM_FUNCTION; } | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list { - LEX *lex= Lex; - if (lex->columns.elements) - { - my_parse_error(thd, ER_SYNTAX_ERROR); + if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_PROCEDURE)) MYSQL_YYABORT; - } - lex->sql_command= SQLCOM_REVOKE; - lex->type= TYPE_ENUM_PROCEDURE; } | ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list { @@ -15674,26 +15629,14 @@ grant_command: | grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list opt_require_clause opt_grant_options { - LEX *lex= Lex; - if (lex->columns.elements) - { - my_parse_error(thd, ER_SYNTAX_ERROR); + if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_FUNCTION)) MYSQL_YYABORT; - } - lex->sql_command= SQLCOM_GRANT; - lex->type= TYPE_ENUM_FUNCTION; } | grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list opt_require_clause opt_grant_options { - LEX *lex= Lex; - if (lex->columns.elements) - { - my_parse_error(thd, ER_SYNTAX_ERROR); + if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_PROCEDURE)) MYSQL_YYABORT; - } - lex->sql_command= SQLCOM_GRANT; - lex->type= TYPE_ENUM_PROCEDURE; } | PROXY_SYM ON user TO_SYM grant_list opt_grant_option { @@ -15750,13 +15693,13 @@ grant_role: CHARSET_INFO *cs= system_charset_info; /* trim end spaces (as they'll be lost in mysql.user anyway) */ $1.length= cs->cset->lengthsp(cs, $1.str, $1.length); - $1.str[$1.length] = '\0'; + ((char*) $1.str)[$1.length] = '\0'; if ($1.length == 0) my_yyabort_error((ER_INVALID_ROLE, MYF(0), "")); - if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + if (!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) MYSQL_YYABORT; - $$->user = $1; - $$->host= empty_lex_str; + $$->user= $1; + $$->host= empty_clex_str; $$->reset_auth(); if (check_string_char_length(&$$->user, ER_USERNAME, @@ -15966,7 +15909,7 @@ grant_user: { $$= $1; $1->plugin= $4; - $1->auth= empty_lex_str; + $1->auth= empty_clex_str; } | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys { @@ -16148,11 +16091,6 @@ opt_release: | NO_SYM RELEASE_SYM { $$= TVL_NO; } ; -opt_savepoint: - /* empty */ {} - | SAVEPOINT_SYM {} - ; - commit: COMMIT_SYM opt_work opt_chain opt_release { @@ -16175,13 +16113,18 @@ rollback: lex->tx_chain= $3; lex->tx_release= $4; } - | ROLLBACK_SYM opt_work - TO_SYM opt_savepoint ident + | ROLLBACK_SYM opt_work TO_SYM SAVEPOINT_SYM ident { LEX *lex=Lex; lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; lex->ident= $5; } + | ROLLBACK_SYM opt_work TO_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; + lex->ident= $4; + } ; savepoint: @@ -16206,6 +16149,14 @@ release: UNIONS : glue selects together */ +unit_type_decl: + UNION_SYM + { $$= UNION_TYPE; } + | INTERSECT_SYM + { $$= INTERSECT_TYPE; } + | EXCEPT_SYM + { $$= EXCEPT_TYPE; } + union_clause: /* empty */ {} @@ -16213,9 +16164,9 @@ union_clause: ; union_list: - UNION_SYM union_option + unit_type_decl union_option { - if (add_select_to_union_list(Lex, (bool)$2, TRUE)) + if (Lex->add_select_to_union_list((bool)$2, $1, TRUE)) MYSQL_YYABORT; } union_list_part2 @@ -16229,9 +16180,9 @@ union_list: ; union_list_view: - UNION_SYM union_option + unit_type_decl union_option { - if (add_select_to_union_list(Lex, (bool)$2, TRUE)) + if (Lex->add_select_to_union_list((bool)$2, $1, TRUE)) MYSQL_YYABORT; } query_expression_body_view @@ -16270,9 +16221,9 @@ order_or_limit: Start a UNION, for non-top level query expressions. */ union_head_non_top: - UNION_SYM union_option + unit_type_decl union_option { - if (add_select_to_union_list(Lex, (bool)$2, FALSE)) + if (Lex->add_select_to_union_list((bool)$2, $1, FALSE)) MYSQL_YYABORT; } ; @@ -16330,7 +16281,7 @@ subselect_start: if (!lex->expr_allows_subselect || lex->sql_command == (int)SQLCOM_PURGE) { - my_parse_error(thd, ER_SYNTAX_ERROR); + thd->parse_error(); MYSQL_YYABORT; } /* @@ -16340,7 +16291,7 @@ subselect_start: (SELECT .. ) UNION ... becomes SELECT * FROM ((SELECT ...) UNION ...) */ - if (mysql_new_select(Lex, 1)) + if (mysql_new_select(Lex, 1, NULL)) MYSQL_YYABORT; } ; @@ -16349,6 +16300,7 @@ subselect_end: { LEX *lex=Lex; + lex->check_automatic_up(UNSPECIFIED_TYPE); lex->pop_context(); SELECT_LEX *child= lex->current_select; lex->current_select = lex->current_select->return_after_parsing(); @@ -16410,38 +16362,6 @@ query_expression_option: /************************************************************************** - CREATE VIEW | TRIGGER | PROCEDURE statements. - -**************************************************************************/ - -view_or_trigger_or_sp_or_event: - definer definer_tail - {} - | no_definer no_definer_tail - {} - | view_algorithm definer_opt view_tail - {} - ; - -definer_tail: - view_tail - | trigger_tail - | sp_tail - | sf_tail - | event_tail - ; - -no_definer_tail: - view_tail - | trigger_tail - | sp_tail - | sf_tail - | udf_tail - | event_tail - ; - -/************************************************************************** - DEFINER clause support. **************************************************************************/ @@ -16482,39 +16402,19 @@ definer: **************************************************************************/ view_algorithm: - ALGORITHM_SYM '=' UNDEFINED_SYM - { Lex->create_view_algorithm= DTYPE_ALGORITHM_UNDEFINED; } - | ALGORITHM_SYM '=' MERGE_SYM - { Lex->create_view_algorithm= VIEW_ALGORITHM_MERGE; } - | ALGORITHM_SYM '=' TEMPTABLE_SYM - { Lex->create_view_algorithm= VIEW_ALGORITHM_TMPTABLE; } + ALGORITHM_SYM '=' UNDEFINED_SYM { $$= DTYPE_ALGORITHM_UNDEFINED; } + | ALGORITHM_SYM '=' MERGE_SYM { $$= VIEW_ALGORITHM_MERGE; } + | ALGORITHM_SYM '=' TEMPTABLE_SYM { $$= VIEW_ALGORITHM_TMPTABLE; } ; -view_suid: - /* empty */ - { Lex->create_view_suid= VIEW_SUID_DEFAULT; } - | SQL_SYM SECURITY_SYM DEFINER_SYM - { Lex->create_view_suid= VIEW_SUID_DEFINER; } - | SQL_SYM SECURITY_SYM INVOKER_SYM - { Lex->create_view_suid= VIEW_SUID_INVOKER; } +opt_view_suid: + /* empty */ { $$= VIEW_SUID_DEFAULT; } + | view_suid { $$= $1; } ; -view_tail: - view_suid VIEW_SYM opt_if_not_exists table_ident - { - LEX *lex= thd->lex; - if (lex->add_create_options_with_check($3)) - MYSQL_YYABORT; - lex->sql_command= SQLCOM_CREATE_VIEW; - /* first table in list is target VIEW name */ - if (!lex->select_lex.add_table_to_list(thd, $4, NULL, - TL_OPTION_UPDATING, - TL_IGNORE, - MDL_EXCLUSIVE)) - MYSQL_YYABORT; - lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; - } - view_list_opt AS view_select +view_suid: + SQL_SYM SECURITY_SYM DEFINER_SYM { $$= VIEW_SUID_DEFINER; } + | SQL_SYM SECURITY_SYM INVOKER_SYM { $$= VIEW_SUID_INVOKER; } ; view_list_opt: @@ -16542,18 +16442,20 @@ view_select: { LEX *lex= Lex; lex->parsing_options.allows_variable= FALSE; - lex->create_view_select.str= (char *) YYLIP->get_cpp_ptr(); + lex->create_view->select.str= (char *) YYLIP->get_cpp_ptr(); } opt_with_clause query_expression_body_view view_check_option { LEX *lex= Lex; - size_t len= YYLIP->get_cpp_ptr() - lex->create_view_select.str; + size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str; uint not_used; - void *create_view_select= thd->memdup(lex->create_view_select.str, len); - lex->create_view_select.length= len; - lex->create_view_select.str= (char *) create_view_select; - trim_whitespace(thd->charset(), &lex->create_view_select, + void *create_view_select= thd->memdup(lex->create_view->select.str, len); + lex->create_view->select.length= len; + lex->create_view->select.str= (char *) create_view_select; + trim_whitespace(thd->charset(), + &lex->create_view->select, ¬_used); + lex->create_view->check= $4; lex->parsing_options.allows_variable= TRUE; lex->current_select->set_with_clause($2); } @@ -16571,14 +16473,10 @@ query_expression_body_view: ; view_check_option: - /* empty */ - { Lex->create_view_check= VIEW_CHECK_NONE; } - | WITH CHECK_SYM OPTION - { Lex->create_view_check= VIEW_CHECK_CASCADED; } - | WITH CASCADED CHECK_SYM OPTION - { Lex->create_view_check= VIEW_CHECK_CASCADED; } - | WITH LOCAL_SYM CHECK_SYM OPTION - { Lex->create_view_check= VIEW_CHECK_LOCAL; } + /* empty */ { $$= VIEW_CHECK_NONE; } + | WITH CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; } + | WITH CASCADED CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; } + | WITH LOCAL_SYM CHECK_SYM OPTION { $$= VIEW_CHECK_LOCAL; } ; /************************************************************************** @@ -16610,25 +16508,24 @@ trigger_follows_precedes_clause: ; trigger_tail: - TRIGGER_SYM remember_name opt_if_not_exists { - if (Lex->add_create_options_with_check($3)) + if (Lex->add_create_options_with_check($2)) MYSQL_YYABORT; } sp_name trg_action_time trg_event ON - remember_name /* $9 */ - { /* $10 */ + remember_name /* $8 */ + { /* $9 */ Lex->raw_trg_on_table_name_begin= YYLIP->get_tok_start(); } - table_ident /* $11 */ + table_ident /* $10 */ FOR_SYM - remember_name /* $13 */ - { /* $14 */ + remember_name /* $12 */ + { /* $13 */ Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start(); } EACH_SYM @@ -16636,28 +16533,28 @@ trigger_tail: { Lex->trg_chistics.ordering_clause_begin= YYLIP->get_cpp_ptr(); } - trigger_follows_precedes_clause /* $18 */ - { /* $19 */ + trigger_follows_precedes_clause /* $17 */ + { /* $18 */ LEX *lex= thd->lex; Lex_input_stream *lip= YYLIP; if (lex->sphead) my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER")); - lex->stmt_definition_begin= $2; - lex->ident.str= $9; - lex->ident.length= $13 - $9; - lex->spname= $5; - (*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($18); + lex->stmt_definition_begin= $1; + lex->ident.str= $8; + lex->ident.length= $12 - $8; + lex->spname= $4; + (*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($17); lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr(); - if (!make_sp_head(thd, $5, TYPE_ENUM_TRIGGER)) + if (!lex->make_sp_head(thd, $4, &sp_handler_trigger)) MYSQL_YYABORT; lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); } - sp_proc_stmt /* $20 */ - { /* $21 */ + sp_proc_stmt /* $19 */ + { /* $20 */ LEX *lex= Lex; sp_head *sp= lex->sphead; @@ -16673,8 +16570,8 @@ trigger_tail: sp_proc_stmt alternatives are not saving/restoring LEX, so lex->query_tables can be wiped out. */ - if (!lex->select_lex.add_table_to_list(thd, $11, - (LEX_STRING*) 0, + if (!lex->select_lex.add_table_to_list(thd, $10, + (LEX_CSTRING*) 0, TL_OPTION_UPDATING, TL_READ_NO_INSERT, MDL_SHARED_NO_WRITE)) @@ -16689,162 +16586,81 @@ trigger_tail: **************************************************************************/ udf_tail: - AGGREGATE_SYM udf_tail2 { thd->lex->udf.type= UDFTYPE_AGGREGATE; } - | udf_tail2 { thd->lex->udf.type= UDFTYPE_FUNCTION; } - ; - -udf_tail2: - FUNCTION_SYM opt_if_not_exists ident + opt_if_not_exists ident RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys { LEX *lex= thd->lex; - if (lex->add_create_options_with_check($2)) + if (lex->add_create_options_with_check($1)) MYSQL_YYABORT; - if (is_native_function(thd, & $3)) - my_yyabort_error((ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $3.str)); + if (is_native_function(thd, & $2)) + my_yyabort_error((ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $2.str)); lex->sql_command= SQLCOM_CREATE_FUNCTION; - lex->udf.name= $3; - lex->udf.returns= (Item_result) $5; - lex->udf.dl= $7.str; + lex->udf.name= $2; + lex->udf.returns= (Item_result) $4; + lex->udf.dl= $6.str; } ; -sf_tail: - FUNCTION_SYM /* $1 */ - opt_if_not_exists /* $2 */ - sp_name /* $3 */ - '(' /* $4 */ - { /* $5 */ - LEX *lex= Lex; - Lex_input_stream *lip= YYLIP; - const char* tmp_param_begin; - - if (lex->add_create_options_with_check($2)) - MYSQL_YYABORT; - lex->spname= $3; - - if (lex->sphead) - my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "FUNCTION")); - - if (!make_sp_head(thd, $3, TYPE_ENUM_FUNCTION)) - MYSQL_YYABORT; - tmp_param_begin= lip->get_cpp_tok_start(); - tmp_param_begin++; - lex->sphead->m_param_begin= tmp_param_begin; - } - sp_fdparam_list /* $6 */ - ')' /* $7 */ - { /* $8 */ - Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start(); - } - RETURNS_SYM /* $9 */ - { /* $10 */ +sf_return_type: + RETURNS_SYM + { LEX *lex= Lex; - lex->init_last_field(&lex->sphead->m_return_field_def, NULL, + lex->init_last_field(&lex->sphead->m_return_field_def, + &empty_clex_str, thd->variables.collation_database); } - type_with_opt_collate /* $11 */ - { /* $12 */ - if (Lex->sphead->fill_field_definition(thd, Lex, Lex->last_field)) + type_with_opt_collate + { + if (Lex->sphead->fill_field_definition(thd, Lex->last_field)) + MYSQL_YYABORT; + } + ; + +sf_tail: + opt_if_not_exists + sp_name + { + Lex->sql_command= SQLCOM_CREATE_SPFUNCTION; + if (!Lex->make_sp_head_no_recursive(thd, $1, $2, + &sp_handler_function)) MYSQL_YYABORT; } - sp_c_chistics /* $13 */ - { /* $14 */ + sp_parenthesized_fdparam_list + sf_return_type + sp_c_chistics + { LEX *lex= thd->lex; Lex_input_stream *lip= YYLIP; + lex->sphead->set_chistics(lex->sp_chistics); lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); } - sp_proc_stmt_in_returns_clause /* $15 */ + sp_proc_stmt_in_returns_clause { - LEX *lex= thd->lex; - sp_head *sp= lex->sphead; - - if (sp->is_not_allowed_in_function("function")) + if (Lex->sp_body_finalize_function(thd)) MYSQL_YYABORT; - - lex->sql_command= SQLCOM_CREATE_SPFUNCTION; - sp->set_stmt_end(thd); - if (!(sp->m_flags & sp_head::HAS_RETURN)) - my_yyabort_error((ER_SP_NORETURN, MYF(0), sp->m_qname.str)); - if (is_native_function(thd, & sp->m_name)) - { - /* - This warning will be printed when - [1] A client query is parsed, - [2] A stored function is loaded by db_load_routine. - Printing the warning for [2] is intentional, to cover the - following scenario: - - A user define a SF 'foo' using MySQL 5.N - - An application uses select foo(), and works. - - MySQL 5.{N+1} defines a new native function 'foo', as - part of a new feature. - - MySQL 5.{N+1} documentation is updated, and should mention - that there is a potential incompatible change in case of - existing stored function named 'foo'. - - The user deploys 5.{N+1}. At this point, 'select foo()' - means something different, and the user code is most likely - broken (it's only safe if the code is 'select db.foo()'). - With a warning printed when the SF is loaded (which has to - occur before the call), the warning will provide a hint - explaining the root cause of a later failure of 'select foo()'. - With no warning printed, the user code will fail with no - apparent reason. - Printing a warning each time db_load_routine is executed for - an ambiguous function is annoying, since that can happen a lot, - but in practice should not happen unless there *are* name - collisions. - If a collision exists, it should not be silenced but fixed. - */ - push_warning_printf(thd, - Sql_condition::WARN_LEVEL_NOTE, - ER_NATIVE_FCT_NAME_COLLISION, - ER_THD(thd, ER_NATIVE_FCT_NAME_COLLISION), - sp->m_name.str); - } - sp->restore_thd_mem_root(thd); } ; sp_tail: - PROCEDURE_SYM opt_if_not_exists sp_name + opt_if_not_exists sp_name { - if (Lex->add_create_options_with_check($2)) - MYSQL_YYABORT; - - if (Lex->sphead) - my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "PROCEDURE")); - - if (!make_sp_head(thd, $3, TYPE_ENUM_PROCEDURE)) + Lex->sql_command= SQLCOM_CREATE_PROCEDURE; + if (!Lex->make_sp_head_no_recursive(thd, $1, $2, + &sp_handler_procedure)) MYSQL_YYABORT; - Lex->spname= $3; - } - '(' - { - const char* tmp_param_begin; - - tmp_param_begin= YYLIP->get_cpp_tok_start(); - tmp_param_begin++; - Lex->sphead->m_param_begin= tmp_param_begin; - } - sp_pdparam_list - ')' - { - Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start(); } + sp_parenthesized_pdparam_list sp_c_chistics { + Lex->sphead->set_chistics(Lex->sp_chistics); Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start()); } sp_proc_stmt { - LEX *lex= Lex; - sp_head *sp= lex->sphead; - - sp->set_stmt_end(thd); - lex->sql_command= SQLCOM_CREATE_PROCEDURE; - sp->restore_thd_mem_root(thd); + if (Lex->sp_body_finalize_procedure(thd)) + MYSQL_YYABORT; } ; @@ -16942,7 +16758,7 @@ install: { LEX *lex= Lex; lex->sql_command= SQLCOM_INSTALL_PLUGIN; - lex->comment= null_lex_str; + lex->comment= null_clex_str; lex->ident= $3; } ; @@ -16958,7 +16774,7 @@ uninstall: { LEX *lex= Lex; lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; - lex->comment= null_lex_str; + lex->comment= null_clex_str; lex->ident= $3; } ; diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy new file mode 100644 index 00000000000..3f531f07e54 --- /dev/null +++ b/sql/sql_yacc_ora.yy @@ -0,0 +1,17034 @@ +/* + Copyright (c) 2000, 2015, Oracle and/or its affiliates. + Copyright (c) 2010, 2016, MariaDB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ + +/* sql_yacc.yy */ + +/** + @defgroup Parser Parser + @{ +*/ + +%{ +#define YYLIP (& thd->m_parser_state->m_lip) +#define YYPS (& thd->m_parser_state->m_yacc) +#define YYCSCL (thd->variables.character_set_client) + +#define MYSQL_YACC +#define YYINITDEPTH 100 +#define YYMAXDEPTH 3200 /* Because of 64K stack */ +#define Lex (thd->lex) + +#define Select Lex->current_select +#include <my_global.h> +#include "sql_priv.h" +#include "sql_parse.h" /* comp_*_creator */ +#include "sql_table.h" /* primary_key_name */ +#include "sql_partition.h" /* mem_alloc_error, partition_info, HASH_PARTITION */ +#include "sql_acl.h" /* *_ACL */ +#include "sql_class.h" /* Key_part_spec, enum_filetype, Diag_condition_item_name */ +#include "slave.h" +#include "lex_symbol.h" +#include "item_create.h" +#include "sp_head.h" +#include "sp_rcontext.h" +#include "sp.h" +#include "sql_show.h" +#include "sql_alter.h" // Sql_cmd_alter_table* +#include "sql_truncate.h" // Sql_cmd_truncate_table +#include "sql_admin.h" // Sql_cmd_analyze/Check..._table +#include "sql_partition_admin.h" // Sql_cmd_alter_table_*_part. +#include "sql_handler.h" // Sql_cmd_handler_* +#include "sql_signal.h" +#include "sql_get_diagnostics.h" // Sql_cmd_get_diagnostics +#include "sql_cte.h" +#include "sql_window.h" +#include "item_windowfunc.h" +#include "event_parse_data.h" +#include "create_options.h" +#include <myisam.h> +#include <myisammrg.h> +#include "keycaches.h" +#include "set_var.h" +#include "rpl_mi.h" +#include "lex_token.h" +#include "sql_lex.h" +#include "sql_sequence.h" + +/* this is to get the bison compilation windows warnings out */ +#ifdef _MSC_VER +/* warning C4065: switch statement contains 'default' but no 'case' labels */ +#pragma warning (disable : 4065) +#endif + +int yylex(void *yylval, void *yythd); + +#define yyoverflow(A,B,C,D,E,F) \ + { \ + ulong val= *(F); \ + if (my_yyoverflow((B), (D), &val)) \ + { \ + yyerror(thd, (char*) (A)); \ + return 2; \ + } \ + else \ + { \ + *(F)= (YYSIZE_T)val; \ + } \ + } + +#define MYSQL_YYABORT \ + do \ + { \ + LEX::cleanup_lex_after_parse_error(thd); \ + YYABORT; \ + } while (0) + +#define MYSQL_YYABORT_UNLESS(A) \ + if (!(A)) \ + { \ + thd->parse_error(); \ + MYSQL_YYABORT; \ + } + +#define my_yyabort_error(A) \ + do { my_error A; MYSQL_YYABORT; } while(0) + +#ifndef DBUG_OFF +#define YYDEBUG 1 +#else +#define YYDEBUG 0 +#endif + + +/** + @brief Bison callback to report a syntax/OOM error + + This function is invoked by the bison-generated parser + when a syntax error, a parse error or an out-of-memory + condition occurs. This function is not invoked when the + parser is requested to abort by semantic action code + by means of YYABORT or YYACCEPT macros. This is why these + macros should not be used (use MYSQL_YYABORT/MYSQL_YYACCEPT + instead). + + The parser will abort immediately after invoking this callback. + + This function is not for use in semantic actions and is internal to + the parser, as it performs some pre-return cleanup. + In semantic actions, please use thd->parse_error() or my_error to + push an error into the error stack and MYSQL_YYABORT + to abort from the parser. +*/ + +void ORAerror(THD *thd, const char *s) +{ + /* + Restore the original LEX if it was replaced when parsing + a stored procedure. We must ensure that a parsing error + does not leave any side effects in the THD. + */ + LEX::cleanup_lex_after_parse_error(thd); + + /* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */ + if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0) + s= ER_THD(thd, ER_SYNTAX_ERROR); + thd->parse_error(s, 0); +} + + + + +#define bincmp_collation(X,Y) \ + do \ + { \ + if (Lex->set_bincmp(X,Y)) \ + MYSQL_YYABORT; \ + } while(0) + +%} +%union { + int num; + ulong ulong_num; + ulonglong ulonglong_number; + longlong longlong_number; + uint sp_instr_addr; + + /* structs */ + LEX_CSTRING lex_str; + LEX_SYMBOL symbol; + Lex_string_with_metadata_st lex_string_with_metadata; + struct sys_var_with_base variable; + Lex_string_with_pos_st lex_string_with_pos; + Lex_spblock_st spblock; + Lex_spblock_handlers_st spblock_handlers; + Lex_length_and_dec_st Lex_length_and_dec; + Lex_cast_type_st Lex_cast_type; + Lex_field_type_st Lex_field_type; + Lex_dyncol_type_st Lex_dyncol_type; + Lex_for_loop_st for_loop; + Lex_for_loop_bounds_st for_loop_bounds; + struct + { + LEX_CSTRING name; + uint offset; + } sp_cursor_name_and_offset; + + /* pointers */ + Create_field *create_field; + Spvar_definition *spvar_definition; + Row_definition_list *spvar_definition_list; + const Type_handler *type_handler; + CHARSET_INFO *charset; + Condition_information_item *cond_info_item; + DYNCALL_CREATE_DEF *dyncol_def; + Diagnostics_information *diag_info; + Item *item; + Item_num *item_num; + Item_param *item_param; + Key_part_spec *key_part; + LEX *lex; + sp_assignment_lex *assignment_lex; + class sp_lex_cursor *sp_cursor_stmt; + LEX_CSTRING *lex_str_ptr; + LEX_USER *lex_user; + List<Condition_information_item> *cond_info_list; + List<DYNCALL_CREATE_DEF> *dyncol_def_list; + List<Item> *item_list; + List<sp_assignment_lex> *sp_assignment_lex_list; + List<Statement_information_item> *stmt_info_list; + List<String> *string_list; + List<LEX_CSTRING> *lex_str_list; + Statement_information_item *stmt_info_item; + String *string; + TABLE_LIST *table_list; + Table_ident *table; + Qualified_column_ident *qualified_column_ident; + char *simple_string; + const char *const_simple_string; + chooser_compare_func_creator boolfunc2creator; + class my_var *myvar; + class sp_condition_value *spcondvalue; + class sp_head *sphead; + class sp_name *spname; + class sp_variable *spvar; + class With_clause *with_clause; + class Virtual_column_info *virtual_column; + + handlerton *db_type; + st_select_lex *select_lex; + struct p_elem_val *p_elem_value; + class Window_frame *window_frame; + class Window_frame_bound *window_frame_bound; + udf_func *udf; + st_trg_execution_order trg_execution_order; + + /* enums */ + enum enum_view_suid view_suid; + enum sub_select_type unit_type; + enum Condition_information_item::Name cond_info_item_name; + enum enum_diag_condition_item_name diag_condition_item_name; + enum Diagnostics_information::Which_area diag_area; + enum Field::geometry_type geom_type; + enum enum_fk_option m_fk_option; + enum Item_udftype udf_type; + enum Key::Keytype key_type; + enum Statement_information_item::Name stmt_info_item_name; + enum enum_filetype filetype; + enum enum_tx_isolation tx_isolation; + enum enum_var_type var_type; + enum enum_yes_no_unknown m_yes_no_unk; + enum ha_choice choice; + enum ha_key_alg key_alg; + enum ha_rkey_function ha_rkey_mode; + enum index_hint_type index_hint; + enum interval_type interval, interval_time_st; + enum row_type row_type; + enum sp_variable::enum_mode spvar_mode; + enum thr_lock_type lock_type; + enum enum_mysql_timestamp_type date_time_type; + enum Window_frame_bound::Bound_precedence_type bound_precedence_type; + enum Window_frame::Frame_units frame_units; + enum Window_frame::Frame_exclusion frame_exclusion; + enum trigger_order_type trigger_action_order_type; + DDL_options_st object_ddl_options; +} + +%{ +bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); +%} + +%pure-parser /* We have threads */ +%parse-param { THD *thd } +%lex-param { THD *thd } +/* + Currently there are 104 shift/reduce conflicts. + We should not introduce new conflicts any more. +*/ +%expect 104 + +/* + Comments for TOKENS. + For each token, please include in the same line a comment that contains + the following tags: + SQL-2011-N : Non Reserved keyword as per SQL-2011 + SQL-2003-R : Reserved keyword as per SQL-2003 + SQL-2003-N : Non Reserved keyword as per SQL-2003 + SQL-1999-R : Reserved keyword as per SQL-1999 + SQL-1999-N : Non Reserved keyword as per SQL-1999 + MYSQL : MySQL extention (unspecified) + MYSQL-FUNC : MySQL extention, function + INTERNAL : Not a real token, lex optimization + OPERATOR : SQL operator + FUTURE-USE : Reserved for future use + + This makes the code grep-able, and helps maintenance. +*/ + +%token ABORT_SYM /* INTERNAL (used in lex) */ +%token ACCESSIBLE_SYM +%token ACTION /* SQL-2003-N */ +%token ADD /* SQL-2003-R */ +%token ADMIN_SYM /* SQL-2003-N */ +%token ADDDATE_SYM /* MYSQL-FUNC */ +%token AFTER_SYM /* SQL-2003-N */ +%token AGAINST +%token AGGREGATE_SYM +%token ALGORITHM_SYM +%token ALL /* SQL-2003-R */ +%token ALTER /* SQL-2003-R */ +%token ALWAYS_SYM +%token ANALYZE_SYM +%token AND_AND_SYM /* OPERATOR */ +%token AND_SYM /* SQL-2003-R */ +%token ANY_SYM /* SQL-2003-R */ +%token AS /* SQL-2003-R */ +%token ASC /* SQL-2003-N */ +%token ASCII_SYM /* MYSQL-FUNC */ +%token ASENSITIVE_SYM /* FUTURE-USE */ +%token AT_SYM /* SQL-2003-R */ +%token ATOMIC_SYM /* SQL-2003-R */ +%token AUTHORS_SYM +%token AUTOEXTEND_SIZE_SYM +%token AUTO_INC +%token AUTO_SYM +%token AVG_ROW_LENGTH +%token AVG_SYM /* SQL-2003-N */ +%token BACKUP_SYM +%token BEFORE_SYM /* SQL-2003-N */ +%token BEGIN_SYM /* SQL-2003-R */ +%token BETWEEN_SYM /* SQL-2003-R */ +%token BIGINT /* SQL-2003-R */ +%token BINARY /* SQL-2003-R */ +%token BINLOG_SYM +%token BIN_NUM +%token BIT_AND /* MYSQL-FUNC */ +%token BIT_OR /* MYSQL-FUNC */ +%token BIT_SYM /* MYSQL-FUNC */ +%token BIT_XOR /* MYSQL-FUNC */ +%token BLOB_SYM /* SQL-2003-R */ +%token BLOCK_SYM +%token BOOLEAN_SYM /* SQL-2003-R */ +%token BOOL_SYM +%token BOTH /* SQL-2003-R */ +%token BTREE_SYM +%token BY /* SQL-2003-R */ +%token BYTE_SYM +%token CACHE_SYM +%token CALL_SYM /* SQL-2003-R */ +%token CASCADE /* SQL-2003-N */ +%token CASCADED /* SQL-2003-R */ +%token CASE_SYM /* SQL-2003-R */ +%token CAST_SYM /* SQL-2003-R */ +%token CATALOG_NAME_SYM /* SQL-2003-N */ +%token CHAIN_SYM /* SQL-2003-N */ +%token CHANGE +%token CHANGED +%token CHARSET +%token CHAR_SYM /* SQL-2003-R */ +%token CHECKPOINT_SYM +%token CHECKSUM_SYM +%token CHECK_SYM /* SQL-2003-R */ +%token CIPHER_SYM +%token CLASS_ORIGIN_SYM /* SQL-2003-N */ +%token CLIENT_SYM +%token CLOSE_SYM /* SQL-2003-R */ +%token CLOB /* SQL-2003-R */ +%token COALESCE /* SQL-2003-N */ +%token CODE_SYM +%token COLLATE_SYM /* SQL-2003-R */ +%token COLLATION_SYM /* SQL-2003-N */ +%token COLUMNS +%token COLUMN_ADD_SYM +%token COLUMN_CHECK_SYM +%token COLUMN_CREATE_SYM +%token COLUMN_DELETE_SYM +%token COLUMN_GET_SYM +%token COLUMN_SYM /* SQL-2003-R */ +%token COLUMN_NAME_SYM /* SQL-2003-N */ +%token COMMENT_SYM +%token COMMITTED_SYM /* SQL-2003-N */ +%token COMMIT_SYM /* SQL-2003-R */ +%token COMPACT_SYM +%token COMPLETION_SYM +%token COMPRESSED_SYM +%token CONCURRENT +%token CONDITION_SYM /* SQL-2003-R, SQL-2008-R */ +%token CONNECTION_SYM +%token CONSISTENT_SYM +%token CONSTRAINT /* SQL-2003-R */ +%token CONSTRAINT_CATALOG_SYM /* SQL-2003-N */ +%token CONSTRAINT_NAME_SYM /* SQL-2003-N */ +%token CONSTRAINT_SCHEMA_SYM /* SQL-2003-N */ +%token CONTAINS_SYM /* SQL-2003-N */ +%token CONTEXT_SYM +%token CONTINUE_SYM /* SQL-2003-R */ +%token CONTRIBUTORS_SYM +%token CONVERT_SYM /* SQL-2003-N */ +%token COUNT_SYM /* SQL-2003-N */ +%token CPU_SYM +%token CREATE /* SQL-2003-R */ +%token CROSS /* SQL-2003-R */ +%token CUBE_SYM /* SQL-2003-R */ +%token CUME_DIST_SYM +%token CURDATE /* MYSQL-FUNC */ +%token CURRENT_SYM /* SQL-2003-R */ +%token CURRENT_USER /* SQL-2003-R */ +%token CURRENT_ROLE /* SQL-2003-R */ +%token CURRENT_POS_SYM +%token CURSOR_SYM /* SQL-2003-R */ +%token CURSOR_NAME_SYM /* SQL-2003-N */ +%token CURTIME /* MYSQL-FUNC */ +%token CYCLE_SYM +%token DATABASE +%token DATABASES +%token DATAFILE_SYM +%token DATA_SYM /* SQL-2003-N */ +%token DATETIME +%token DATE_ADD_INTERVAL /* MYSQL-FUNC */ +%token DATE_FORMAT_SYM /* MYSQL-FUNC */ +%token DATE_SUB_INTERVAL /* MYSQL-FUNC */ +%token DATE_SYM /* SQL-2003-R */ +%token DAY_HOUR_SYM +%token DAY_MICROSECOND_SYM +%token DAY_MINUTE_SYM +%token DAY_SECOND_SYM +%token DAY_SYM /* SQL-2003-R */ +%token DEALLOCATE_SYM /* SQL-2003-R */ +%token DECIMAL_NUM +%token DECIMAL_SYM /* SQL-2003-R */ +%token DECLARE_SYM /* SQL-2003-R */ +%token DECODE_SYM /* Oracle function, non-reserved */ +%token DEFAULT /* SQL-2003-R */ +%token DEFINER_SYM +%token DELAYED_SYM +%token DELAY_KEY_WRITE_SYM +%token DELETE_SYM /* SQL-2003-R */ +%token DENSE_RANK_SYM +%token DESC /* SQL-2003-N */ +%token DESCRIBE /* SQL-2003-R */ +%token DES_KEY_FILE +%token DETERMINISTIC_SYM /* SQL-2003-R */ +%token DIAGNOSTICS_SYM /* SQL-2003-N */ +%token DIRECTORY_SYM +%token DISABLE_SYM +%token DISCARD +%token DISK_SYM +%token DISTINCT /* SQL-2003-R */ +%token DIV_SYM +%token DOUBLE_SYM /* SQL-2003-R */ +%token DO_DOMAIN_IDS_SYM +%token DO_SYM +%token DOT_DOT_SYM +%token DROP /* SQL-2003-R */ +%token DUAL_SYM +%token DUMPFILE +%token DUPLICATE_SYM +%token DYNAMIC_SYM /* SQL-2003-R */ +%token EACH_SYM /* SQL-2003-R */ +%token ELSE /* SQL-2003-R */ +%token ELSEIF_SYM +%token ELSIF_SYM /* Oracle, reserved in PL/SQL*/ +%token ENABLE_SYM +%token ENCLOSED +%token END /* SQL-2003-R */ +%token ENDS_SYM +%token END_OF_INPUT /* INTERNAL */ +%token ENGINES_SYM +%token ENGINE_SYM +%token ENUM +%token EQUAL_SYM /* OPERATOR */ +%token ERROR_SYM +%token ERRORS +%token ESCAPED +%token ESCAPE_SYM /* SQL-2003-R */ +%token EVENTS_SYM +%token EVENT_SYM +%token EVERY_SYM /* SQL-2003-N */ +%token EXCHANGE_SYM +%token EXAMINED_SYM +%token EXCEPT_SYM /* SQL-2003-R */ +%token EXCLUDE_SYM /* SQL-2011-N */ +%token EXECUTE_SYM /* SQL-2003-R */ +%token EXCEPTION_SYM /* SQL-2003-N, Oracle-PLSQL-R */ +%token EXISTS /* SQL-2003-R */ +%token EXIT_SYM +%token EXPANSION_SYM +%token EXPORT_SYM +%token EXTENDED_SYM +%token EXTENT_SIZE_SYM +%token EXTRACT_SYM /* SQL-2003-N */ +%token FALSE_SYM /* SQL-2003-R */ +%token FAST_SYM +%token FAULTS_SYM +%token FETCH_SYM /* SQL-2003-R */ +%token FILE_SYM +%token FIRST_VALUE_SYM /* SQL-2011 */ +%token FIRST_SYM /* SQL-2003-N */ +%token FIXED_SYM +%token FLOAT_NUM +%token FLOAT_SYM /* SQL-2003-R */ +%token FLUSH_SYM +%token FOLLOWS_SYM /* MYSQL trigger*/ +%token FOLLOWING_SYM /* SQL-2011-N */ +%token FORCE_SYM +%token FOREIGN /* SQL-2003-R */ +%token FOR_SYM /* SQL-2003-R */ +%token FORMAT_SYM +%token FOUND_SYM /* SQL-2003-R */ +%token FROM +%token FULL /* SQL-2003-R */ +%token FULLTEXT_SYM +%token FUNCTION_SYM /* SQL-2003-R */ +%token GE +%token GENERAL +%token GENERATED_SYM +%token GEOMETRYCOLLECTION +%token GEOMETRY_SYM +%token GET_FORMAT /* MYSQL-FUNC */ +%token GET_SYM /* SQL-2003-R */ +%token GLOBAL_SYM /* SQL-2003-R */ +%token GOTO_SYM /* Oracle, reserved in PL/SQL*/ +%token GRANT /* SQL-2003-R */ +%token GRANTS +%token GROUP_SYM /* SQL-2003-R */ +%token GROUP_CONCAT_SYM +%token LAG_SYM /* SQL-2011 */ +%token LEAD_SYM /* SQL-2011 */ +%token HANDLER_SYM +%token HARD_SYM +%token HASH_SYM +%token HAVING /* SQL-2003-R */ +%token HELP_SYM +%token HEX_NUM +%token HEX_STRING +%token HIGH_PRIORITY +%token HOST_SYM +%token HOSTS_SYM +%token HOUR_MICROSECOND_SYM +%token HOUR_MINUTE_SYM +%token HOUR_SECOND_SYM +%token HOUR_SYM /* SQL-2003-R */ +%token ID_SYM /* MYSQL */ +%token IDENT +%token IDENTIFIED_SYM +%token IDENT_QUOTED +%token IF_SYM +%token IGNORE_DOMAIN_IDS_SYM +%token IGNORE_SYM +%token IGNORE_SERVER_IDS_SYM +%token IMMEDIATE_SYM /* SQL-2003-R */ +%token IMPORT +%token INCREMENT_SYM +%token INDEXES +%token INDEX_SYM +%token INFILE +%token INITIAL_SIZE_SYM +%token INNER_SYM /* SQL-2003-R */ +%token INOUT_SYM /* SQL-2003-R */ +%token INSENSITIVE_SYM /* SQL-2003-R */ +%token INSERT /* SQL-2003-R */ +%token INSERT_METHOD +%token INSTALL_SYM +%token INTERSECT_SYM /* SQL-2003-R */ +%token INTERVAL_SYM /* SQL-2003-R */ +%token INTO /* SQL-2003-R */ +%token INT_SYM /* SQL-2003-R */ +%token INVOKER_SYM +%token IN_SYM /* SQL-2003-R */ +%token IO_SYM +%token IPC_SYM +%token IS /* SQL-2003-R */ +%token ISOLATION /* SQL-2003-R */ +%token ISOPEN_SYM /* Oracle-N */ +%token ISSUER_SYM +%token ITERATE_SYM +%token JOIN_SYM /* SQL-2003-R */ +%token JSON_SYM +%token KEYS +%token KEY_BLOCK_SIZE +%token KEY_SYM /* SQL-2003-N */ +%token KILL_SYM +%token LANGUAGE_SYM /* SQL-2003-R */ +%token LAST_SYM /* SQL-2003-N */ +%token LAST_VALUE +%token LASTVAL_SYM /* PostgreSQL sequence function */ +%token LE /* OPERATOR */ +%token LEADING /* SQL-2003-R */ +%token LEAVES +%token LEAVE_SYM +%token LEFT /* SQL-2003-R */ +%token LESS_SYM +%token LEVEL_SYM +%token LEX_HOSTNAME +%token LIKE /* SQL-2003-R */ +%token LIMIT +%token LINEAR_SYM +%token LINES +%token LINESTRING +%token LIST_SYM +%token LOAD +%token LOCAL_SYM /* SQL-2003-R */ +%token LOCATOR_SYM /* SQL-2003-N */ +%token LOCKS_SYM +%token LOCK_SYM +%token LOGFILE_SYM +%token LOGS_SYM +%token LONGBLOB +%token LONGTEXT +%token LONG_NUM +%token LONG_SYM +%token LOOP_SYM +%token LOW_PRIORITY +%token MASTER_CONNECT_RETRY_SYM +%token MASTER_DELAY_SYM +%token MASTER_GTID_POS_SYM +%token MASTER_HOST_SYM +%token MASTER_LOG_FILE_SYM +%token MASTER_LOG_POS_SYM +%token MASTER_PASSWORD_SYM +%token MASTER_PORT_SYM +%token MASTER_SERVER_ID_SYM +%token MASTER_SSL_CAPATH_SYM +%token MASTER_SSL_CA_SYM +%token MASTER_SSL_CERT_SYM +%token MASTER_SSL_CIPHER_SYM +%token MASTER_SSL_CRL_SYM +%token MASTER_SSL_CRLPATH_SYM +%token MASTER_SSL_KEY_SYM +%token MASTER_SSL_SYM +%token MASTER_SSL_VERIFY_SERVER_CERT_SYM +%token MASTER_SYM +%token MASTER_USER_SYM +%token MASTER_USE_GTID_SYM +%token MASTER_HEARTBEAT_PERIOD_SYM +%token MATCH /* SQL-2003-R */ +%token MAX_CONNECTIONS_PER_HOUR +%token MAX_QUERIES_PER_HOUR +%token MAX_ROWS +%token MAX_SIZE_SYM +%token MAX_SYM /* SQL-2003-N */ +%token MAX_UPDATES_PER_HOUR +%token MAX_STATEMENT_TIME_SYM +%token MAX_USER_CONNECTIONS_SYM +%token MAXVALUE_SYM /* SQL-2003-N */ +%token MEDIUMBLOB +%token MEDIUMINT +%token MEDIUMTEXT +%token MEDIUM_SYM +%token MEMORY_SYM +%token MERGE_SYM /* SQL-2003-R */ +%token MESSAGE_TEXT_SYM /* SQL-2003-N */ +%token MICROSECOND_SYM /* MYSQL-FUNC */ +%token MIGRATE_SYM +%token MINUTE_MICROSECOND_SYM +%token MINUTE_SECOND_SYM +%token MINUTE_SYM /* SQL-2003-R */ +%token MINVALUE_SYM +%token MIN_ROWS +%token MIN_SYM /* SQL-2003-N */ +%token MODE_SYM +%token MODIFIES_SYM /* SQL-2003-R */ +%token MODIFY_SYM +%token MOD_SYM /* SQL-2003-N */ +%token MONTH_SYM /* SQL-2003-R */ +%token MULTILINESTRING +%token MULTIPOINT +%token MULTIPOLYGON +%token MUTEX_SYM +%token MYSQL_SYM +%token MYSQL_ERRNO_SYM +%token NAMES_SYM /* SQL-2003-N */ +%token NAME_SYM /* SQL-2003-N */ +%token NATIONAL_SYM /* SQL-2003-R */ +%token NATURAL /* SQL-2003-R */ +%token NCHAR_STRING +%token NCHAR_SYM /* SQL-2003-R */ +%token NE /* OPERATOR */ +%token NEG +%token NEW_SYM /* SQL-2003-R */ +%token NEXT_SYM /* SQL-2003-N */ +%token NEXTVAL_SYM /* PostgreSQL sequence function */ +%token NOCACHE_SYM +%token NOCYCLE_SYM +%token NODEGROUP_SYM +%token NONE_SYM /* SQL-2003-R */ +%token NOT2_SYM +%token NOT_SYM /* SQL-2003-R */ +%token NOTFOUND_SYM /* Oracle-R */ +%token NOW_SYM +%token NO_SYM /* SQL-2003-R */ +%token NOMAXVALUE_SYM +%token NOMINVALUE_SYM +%token NO_WAIT_SYM +%token NO_WRITE_TO_BINLOG +%token NTILE_SYM +%token NULL_SYM /* SQL-2003-R */ +%token NUM +%token NUMBER_SYM /* SQL-2003-N */ +%token NUMERIC_SYM /* SQL-2003-R */ +%token NTH_VALUE_SYM /* SQL-2011 */ +%token NVARCHAR_SYM +%token OF_SYM /* SQL-1992-R, Oracle-R */ +%token OFFSET_SYM +%token OLD_PASSWORD_SYM +%token ON /* SQL-2003-R */ +%token ONE_SYM +%token ONLY_SYM /* SQL-2003-R */ +%token ONLINE_SYM +%token OPEN_SYM /* SQL-2003-R */ +%token OPTIMIZE +%token OPTIONS_SYM +%token OPTION /* SQL-2003-N */ +%token OPTIONALLY +%token OR2_SYM +%token ORDER_SYM /* SQL-2003-R */ +%token OR_OR_SYM /* OPERATOR */ +%token OR_SYM /* SQL-2003-R */ +%token OTHERS_SYM /* SQL-2011-N */ +%token OUTER +%token OUTFILE +%token OUT_SYM /* SQL-2003-R */ +%token OVER_SYM +%token OWNER_SYM +%token PACK_KEYS_SYM +%token PAGE_SYM +%token PAGE_CHECKSUM_SYM +%token PARAM_MARKER +%token PARSER_SYM +%token PARSE_VCOL_EXPR_SYM +%token PARTIAL /* SQL-2003-N */ +%token PARTITION_SYM /* SQL-2003-R */ +%token PARTITIONS_SYM +%token PARTITIONING_SYM +%token PASSWORD_SYM +%token PERCENT_RANK_SYM +%token PERSISTENT_SYM +%token PHASE_SYM +%token PLUGINS_SYM +%token PLUGIN_SYM +%token POINT_SYM +%token POLYGON +%token PORT_SYM +%token POSITION_SYM /* SQL-2003-N */ +%token PRECEDES_SYM /* MYSQL */ +%token PRECEDING_SYM /* SQL-2011-N */ +%token PRECISION /* SQL-2003-R */ +%token PREPARE_SYM /* SQL-2003-R */ +%token PRESERVE_SYM +%token PREV_SYM +%token PREVIOUS_SYM +%token PRIMARY_SYM /* SQL-2003-R */ +%token PRIVILEGES /* SQL-2003-N */ +%token PROCEDURE_SYM /* SQL-2003-R */ +%token PROCESS +%token PROCESSLIST_SYM +%token PROFILE_SYM +%token PROFILES_SYM +%token PROXY_SYM +%token PURGE +%token QUARTER_SYM +%token QUERY_SYM +%token QUICK +%token RAISE_SYM /* Oracle-PLSQL-R */ +%token RANGE_SYM /* SQL-2003-R */ +%token RANK_SYM +%token RAW /* Oracle */ +%token READS_SYM /* SQL-2003-R */ +%token READ_ONLY_SYM +%token READ_SYM /* SQL-2003-N */ +%token READ_WRITE_SYM +%token REAL /* SQL-2003-R */ +%token REBUILD_SYM +%token RECOVER_SYM +%token RECURSIVE_SYM +%token REDOFILE_SYM +%token REDO_BUFFER_SIZE_SYM +%token REDUNDANT_SYM +%token REFERENCES /* SQL-2003-R */ +%token REGEXP +%token RELAY +%token RELAYLOG_SYM +%token RELAY_LOG_FILE_SYM +%token RELAY_LOG_POS_SYM +%token RELAY_THREAD +%token RELEASE_SYM /* SQL-2003-R */ +%token RELOAD +%token REMOVE_SYM +%token RENAME +%token REORGANIZE_SYM +%token REPAIR +%token REPEATABLE_SYM /* SQL-2003-N */ +%token REPEAT_SYM /* MYSQL-FUNC */ +%token REPLACE /* MYSQL-FUNC */ +%token REPLICATION +%token REQUIRE_SYM +%token RESET_SYM +%token RESTART_SYM +%token RESIGNAL_SYM /* SQL-2003-R */ +%token RESOURCES +%token RESTORE_SYM +%token RESTRICT +%token RESUME_SYM +%token RETURNED_SQLSTATE_SYM /* SQL-2003-N */ +%token RETURNING_SYM +%token RETURNS_SYM /* SQL-2003-R */ +%token RETURN_SYM /* SQL-2003-R */ +%token REUSE_SYM /* Oracle-R */ +%token REVERSE_SYM +%token REVOKE /* SQL-2003-R */ +%token RIGHT /* SQL-2003-R */ +%token ROLE_SYM +%token ROLLBACK_SYM /* SQL-2003-R */ +%token ROLLUP_SYM /* SQL-2003-R */ +%token ROUTINE_SYM /* SQL-2003-N */ +%token ROWCOUNT_SYM /* Oracle-N */ +%token ROW_SYM /* SQL-2003-R */ +%token ROWS_SYM /* SQL-2003-R */ +%token ROWTYPE_SYM /* Oracle-PLSQL-R */ +%token ROW_COUNT_SYM /* SQL-2003-N */ +%token ROW_FORMAT_SYM +%token ROW_NUMBER_SYM +%token RTREE_SYM +%token SAVEPOINT_SYM /* SQL-2003-R */ +%token SCHEDULE_SYM +%token SCHEMA_NAME_SYM /* SQL-2003-N */ +%token SECOND_MICROSECOND_SYM +%token SECOND_SYM /* SQL-2003-R */ +%token SECURITY_SYM /* SQL-2003-N */ +%token SELECT_SYM /* SQL-2003-R */ +%token SENSITIVE_SYM /* FUTURE-USE */ +%token SEPARATOR_SYM +%token SEQUENCE_SYM +%token SERIALIZABLE_SYM /* SQL-2003-N */ +%token SERIAL_SYM +%token SESSION_SYM /* SQL-2003-N */ +%token SERVER_SYM +%token SERVER_OPTIONS +%token SET /* SQL-2003-R */ +%token SETVAL_SYM /* PostgreSQL sequence function */ +%token SET_VAR +%token SHARE_SYM +%token SHIFT_LEFT /* OPERATOR */ +%token SHIFT_RIGHT /* OPERATOR */ +%token SHOW +%token SHUTDOWN +%token SIGNAL_SYM /* SQL-2003-R */ +%token SIGNED_SYM +%token SIMPLE_SYM /* SQL-2003-N */ +%token SLAVE +%token SLAVES +%token SLAVE_POS_SYM +%token SLOW +%token SMALLINT /* SQL-2003-R */ +%token SNAPSHOT_SYM +%token SOCKET_SYM +%token SOFT_SYM +%token SONAME_SYM +%token SOUNDS_SYM +%token SOURCE_SYM +%token SPATIAL_SYM +%token SPECIFIC_SYM /* SQL-2003-R */ +%token SQLEXCEPTION_SYM /* SQL-2003-R */ +%token SQLSTATE_SYM /* SQL-2003-R */ +%token SQLWARNING_SYM /* SQL-2003-R */ +%token SQL_BIG_RESULT +%token SQL_BUFFER_RESULT +%token SQL_CACHE_SYM +%token SQL_CALC_FOUND_ROWS +%token SQL_NO_CACHE_SYM +%token SQL_SMALL_RESULT +%token SQL_SYM /* SQL-2003-R */ +%token SQL_THREAD +%token REF_SYSTEM_ID_SYM +%token SSL_SYM +%token STARTING +%token STARTS_SYM +%token START_SYM /* SQL-2003-R */ +%token STATEMENT_SYM +%token STATS_AUTO_RECALC_SYM +%token STATS_PERSISTENT_SYM +%token STATS_SAMPLE_PAGES_SYM +%token STATUS_SYM +%token STDDEV_SAMP_SYM /* SQL-2003-N */ +%token STD_SYM +%token STOP_SYM +%token STORAGE_SYM +%token STORED_SYM +%token STRAIGHT_JOIN +%token STRING_SYM +%token SUBCLASS_ORIGIN_SYM /* SQL-2003-N */ +%token SUBDATE_SYM +%token SUBJECT_SYM +%token SUBPARTITIONS_SYM +%token SUBPARTITION_SYM +%token SUBSTRING /* SQL-2003-N */ +%token SUM_SYM /* SQL-2003-N */ +%token SUPER_SYM +%token SUSPEND_SYM +%token SWAPS_SYM +%token SWITCHES_SYM +%token SYSDATE +%token TABLES +%token TABLESPACE +%token TABLE_REF_PRIORITY +%token TABLE_SYM /* SQL-2003-R */ +%token TABLE_CHECKSUM_SYM +%token TABLE_NAME_SYM /* SQL-2003-N */ +%token TEMPORARY /* SQL-2003-N */ +%token TEMPTABLE_SYM +%token TERMINATED +%token TEXT_STRING +%token TEXT_SYM +%token THAN_SYM +%token THEN_SYM /* SQL-2003-R */ +%token TIES_SYM /* SQL-2011-N */ +%token TIMESTAMP /* SQL-2003-R */ +%token TIMESTAMP_ADD +%token TIMESTAMP_DIFF +%token TIME_SYM /* SQL-2003-R */ +%token TINYBLOB +%token TINYINT +%token TINYTEXT +%token TO_SYM /* SQL-2003-R */ +%token TRAILING /* SQL-2003-R */ +%token TRANSACTION_SYM +%token TRANSACTIONAL_SYM +%token TRIGGERS_SYM +%token TRIGGER_SYM /* SQL-2003-R */ +%token TRIM /* SQL-2003-N */ +%token TRUE_SYM /* SQL-2003-R */ +%token TRUNCATE_SYM +%token TYPES_SYM +%token TYPE_SYM /* SQL-2003-N */ +%token UDF_RETURNS_SYM +%token ULONGLONG_NUM +%token UNBOUNDED_SYM /* SQL-2011-N */ +%token UNCOMMITTED_SYM /* SQL-2003-N */ +%token UNDEFINED_SYM +%token UNDERSCORE_CHARSET +%token UNDOFILE_SYM +%token UNDO_BUFFER_SIZE_SYM +%token UNDO_SYM /* FUTURE-USE */ +%token UNICODE_SYM +%token UNINSTALL_SYM +%token UNION_SYM /* SQL-2003-R */ +%token UNIQUE_SYM +%token UNKNOWN_SYM /* SQL-2003-R */ +%token UNLOCK_SYM +%token UNSIGNED +%token UNTIL_SYM +%token UPDATE_SYM /* SQL-2003-R */ +%token UPGRADE_SYM +%token USAGE /* SQL-2003-N */ +%token USER_SYM /* SQL-2003-R */ +%token USE_FRM +%token USE_SYM +%token USING /* SQL-2003-R */ +%token UTC_DATE_SYM +%token UTC_TIMESTAMP_SYM +%token UTC_TIME_SYM +%token VALUES /* SQL-2003-R */ +%token VALUE_SYM /* SQL-2003-R */ +%token VARBINARY +%token VARCHAR /* SQL-2003-R */ +%token VARCHAR2 /* Oracle */ +%token VARIABLES +%token VARIANCE_SYM +%token VARYING /* SQL-2003-R */ +%token VAR_SAMP_SYM +%token VIA_SYM +%token VIEW_SYM /* SQL-2003-N */ +%token VIRTUAL_SYM +%token WAIT_SYM +%token WARNINGS +%token WEEK_SYM +%token WEIGHT_STRING_SYM +%token WHEN_SYM /* SQL-2003-R */ +%token WHERE /* SQL-2003-R */ +%token WINDOW_SYM +%token WHILE_SYM +%token WITH /* SQL-2003-R */ +%token WITH_CUBE_SYM /* INTERNAL */ +%token WITH_ROLLUP_SYM /* INTERNAL */ +%token WORK_SYM /* SQL-2003-N */ +%token WRAPPER_SYM +%token WRITE_SYM /* SQL-2003-N */ +%token X509_SYM +%token XA_SYM +%token XML_SYM +%token XOR +%token YEAR_MONTH_SYM +%token YEAR_SYM /* SQL-2003-R */ +%token ZEROFILL + +%token IMPOSSIBLE_ACTION /* To avoid warning for yyerrlab1 */ + +%left JOIN_SYM INNER_SYM STRAIGHT_JOIN CROSS LEFT RIGHT +/* A dummy token to force the priority of table_ref production in a join. */ +%left TABLE_REF_PRIORITY +%left SET_VAR +%left OR_OR_SYM OR_SYM OR2_SYM +%left XOR +%left AND_SYM AND_AND_SYM +%left BETWEEN_SYM CASE_SYM WHEN_SYM THEN_SYM ELSE +%left '=' EQUAL_SYM GE '>' LE '<' NE IS LIKE REGEXP IN_SYM +%left '|' +%left '&' +%left SHIFT_LEFT SHIFT_RIGHT +%left '-' '+' +%left '*' '/' DIV_SYM MOD_SYM +%left '^' +%left NEG '~' +%right NOT_SYM NOT2_SYM +%right BINARY COLLATE_SYM +%left INTERVAL_SYM + +%type <lex_str> + IDENT IDENT_QUOTED DECIMAL_NUM FLOAT_NUM NUM LONG_NUM + HEX_NUM HEX_STRING + LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident_or_text + IDENT_sys TEXT_STRING_sys TEXT_STRING_literal + opt_component key_cache_name + sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty + opt_constraint constraint opt_ident + label_declaration_oracle labels_declaration_oracle + ident_directly_assignable + sp_decl_ident + sp_block_label opt_place opt_db + +%type <lex_string_with_metadata> + TEXT_STRING + NCHAR_STRING + +%type <lex_str_ptr> + opt_table_alias + +%type <lex_string_with_pos> + ident ident_with_tok_start + +%type <table> + table_ident table_ident_nodb references xid + table_ident_opt_wild create_like + +%type <qualified_column_ident> + optionally_qualified_column_ident + +%type <simple_string> + remember_name remember_end remember_tok_start + wild_and_where + colon_with_pos + +%type <const_simple_string> + field_length opt_field_length opt_field_length_default_1 + +%type <string> + text_string hex_or_bin_String opt_gconcat_separator + +%type <type_handler> int_type real_type + +%type <Lex_field_type> type_with_opt_collate field_type + sp_param_type_with_opt_collate + sp_param_field_type + sp_param_field_type_string + field_type_numeric + field_type_string + field_type_lob + field_type_temporal + field_type_misc + +%type <Lex_dyncol_type> opt_dyncol_type dyncol_type + numeric_dyncol_type temporal_dyncol_type string_dyncol_type + +%type <create_field> field_spec column_def + +%type <geom_type> spatial_type + +%type <num> + order_dir lock_option + udf_type opt_local opt_no_write_to_binlog + opt_temporary all_or_any opt_distinct + opt_ignore_leaves fulltext_options union_option + opt_not + select_derived_init transaction_access_mode_types + opt_natural_language_mode opt_query_expansion + opt_ev_status opt_ev_on_completion ev_on_completion opt_ev_comment + ev_alter_on_schedule_completion opt_ev_rename_to opt_ev_sql_stmt + optional_flush_tables_arguments + opt_time_precision kill_type kill_option int_num + opt_default_time_precision + case_stmt_body opt_bin_mod + opt_if_exists_table_element opt_if_not_exists_table_element + opt_recursive + +%type <object_ddl_options> + create_or_replace + opt_if_not_exists + opt_if_exists + +/* + Bit field of MYSQL_START_TRANS_OPT_* flags. +*/ +%type <num> opt_start_transaction_option_list +%type <num> start_transaction_option_list +%type <num> start_transaction_option + +%type <m_yes_no_unk> + opt_chain opt_release + +%type <m_fk_option> + delete_option + +%type <ulong_num> + ulong_num real_ulong_num merge_insert_types + ws_nweights + ws_level_flag_desc ws_level_flag_reverse ws_level_flags + opt_ws_levels ws_level_list ws_level_list_item ws_level_number + ws_level_range ws_level_list_or_range bool + +%type <ulonglong_number> + ulonglong_num real_ulonglong_num size_number + +%type <longlong_number> + longlong_num + +%type <choice> choice + +%type <lock_type> + replace_lock_option opt_low_priority insert_lock_option load_data_lock + +%type <item> + literal text_literal insert_ident order_ident temporal_literal + simple_ident expr sum_expr in_sum_expr + variable variable_aux bool_pri + predicate bit_expr parenthesized_expr + table_wild simple_expr column_default_non_parenthesized_expr udf_expr + expr_or_default set_expr_or_default + geometry_function signed_literal expr_or_literal + opt_escape + sp_opt_default + simple_ident_nospvar simple_ident_q simple_ident_q2 + field_or_var limit_option + part_func_expr + window_func_expr + window_func + simple_window_func + explicit_cursor_attr + function_call_keyword + function_call_nonkeyword + function_call_generic + function_call_conflict kill_expr + signal_allowed_expr + simple_target_specification + condition_number + reset_lex_expr + +%type <item_param> param_marker + +%type <item_num> + NUM_literal + +%type <item_list> + expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else + ident_list ident_list_arg opt_expr_list + decode_when_list + +%type <sp_cursor_stmt> + sp_cursor_stmt_lex + sp_cursor_stmt + +%type <assignment_lex> + assignment_source_lex + assignment_source_expr + for_loop_bound_expr + +%type <sp_assignment_lex_list> + cursor_actual_parameters + opt_parenthesized_cursor_actual_parameters + +%type <var_type> + option_type opt_var_type opt_var_ident_type + +%type <key_type> + opt_unique constraint_key_type fulltext spatial + +%type <key_alg> + btree_or_rtree opt_key_algorithm_clause opt_USING_key_algorithm + +%type <string_list> + using_list opt_use_partition use_partition + +%type <key_part> + key_part + +%type <table_list> + join_table_list join_table + table_factor table_ref esc_table_ref + table_primary_ident table_primary_derived + select_derived derived_table_list + select_derived_union + derived_query_specification +%type <date_time_type> date_time_type; +%type <interval> interval + +%type <interval_time_st> interval_time_stamp + +%type <db_type> storage_engines known_storage_engines + +%type <row_type> row_types + +%type <tx_isolation> isolation_types + +%type <ha_rkey_mode> handler_rkey_mode + +%type <Lex_cast_type> cast_type cast_type_numeric cast_type_temporal + +%type <Lex_length_and_dec> precision opt_precision float_options + opt_field_length_default_sp_param_varchar + opt_field_length_default_sp_param_char + +%type <symbol> keyword keyword_sp + keyword_directly_assignable + keyword_directly_not_assignable + sp_decl_ident_keyword + keyword_sp_data_type + keyword_sp_not_data_type + +%type <lex_user> user grant_user grant_role user_or_role current_role + admin_option_for_role user_maybe_role + +%type <charset> + opt_collate + charset_name + charset_or_alias + charset_name_or_default + old_or_new_charset_name + old_or_new_charset_name_or_default + collation_name + collation_name_or_default + opt_load_data_charset + UNDERSCORE_CHARSET + +%type <variable> internal_variable_name + internal_variable_name_directly_assignable + +%type <select_lex> subselect + get_select_lex get_select_lex_derived + query_specification + query_term_union_not_ready + query_term_union_ready + query_expression_body + select_paren_derived + +%type <boolfunc2creator> comp_op + +%type <dyncol_def> dyncall_create_element + +%type <dyncol_def_list> dyncall_create_list + +%type <myvar> select_outvar + +%type <virtual_column> opt_check_constraint check_constraint virtual_column_func + column_default_expr +%type <unit_type> unit_type_decl + +%type <NONE> + analyze_stmt_command + query verb_clause create change select do drop insert replace insert2 + insert_values update delete truncate rename compound_statement + show describe load alter optimize keycache preload flush + reset purge commit rollback savepoint release + slave master_def master_defs master_file_def slave_until_opts + repair analyze opt_with_admin opt_with_admin_option + analyze_table_list analyze_table_elem_spec + opt_persistent_stat_clause persistent_stat_spec + persistent_column_stat_spec persistent_index_stat_spec + table_column_list table_index_list table_index_name + check start checksum + field_list field_list_item kill key_def constraint_def + keycache_list keycache_list_or_parts assign_to_keycache + assign_to_keycache_parts + preload_list preload_list_or_parts preload_keys preload_keys_parts + select_item_list select_item values_list no_braces + opt_limit_clause delete_limit_clause fields opt_values values + procedure_list procedure_list2 procedure_item + field_def handler opt_generated_always + opt_ignore opt_column opt_restrict + grant revoke set lock unlock string_list field_options + opt_binary table_lock_list table_lock + ref_list opt_match_clause opt_on_update_delete use + opt_delete_options opt_delete_option varchar nchar nvarchar + opt_outer table_list table_name table_alias_ref_list table_alias_ref + opt_attribute opt_attribute_list attribute column_list column_list_id + opt_column_list grant_privileges grant_ident grant_list grant_option + object_privilege object_privilege_list user_list user_and_role_list + rename_list table_or_tables + clear_privileges flush_options flush_option + opt_flush_lock flush_lock flush_options_list + equal optional_braces + opt_mi_check_type opt_to mi_check_types + table_to_table_list table_to_table opt_table_list opt_as + handler_rkey_function handler_read_or_scan + single_multi table_wild_list table_wild_one opt_wild + union_clause union_list + subselect_start opt_and charset + subselect_end select_var_list select_var_list_init help + opt_extended_describe shutdown + opt_format_json + prepare prepare_src execute deallocate + statement sp_suid + sp_c_chistics sp_a_chistics sp_chistic sp_c_chistic xa + opt_field_or_var_spec fields_or_vars opt_load_data_set_spec + view_list_opt view_list view_select + trigger_tail sp_tail sf_tail event_tail + udf_tail create_function_tail + install uninstall partition_entry binlog_base64_event + normal_key_options normal_key_opts all_key_opt + spatial_key_options fulltext_key_options normal_key_opt + fulltext_key_opt spatial_key_opt fulltext_key_opts spatial_key_opts + keep_gcc_happy + key_using_alg + part_column_list + server_def server_options_list server_option + definer_opt no_definer definer get_diagnostics + parse_vcol_expr vcol_opt_specifier vcol_opt_attribute + vcol_opt_attribute_list vcol_attribute + opt_serial_attribute opt_serial_attribute_list serial_attribute + explainable_command + set_assign + sf_tail_standalone + sp_tail_standalone +END_OF_INPUT + +%type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt +%type <NONE> sp_proc_stmt_statement sp_proc_stmt_return +%type <NONE> sp_proc_stmt_compound_ok +%type <NONE> sp_proc_stmt_if +%type <NONE> sp_labeled_control sp_unlabeled_control +%type <NONE> sp_labeled_block sp_unlabeled_block +%type <NONE> sp_labelable_stmt +%type <NONE> sp_proc_stmt_continue +%type <NONE> sp_proc_stmt_exit +%type <NONE> sp_proc_stmt_leave +%type <NONE> sp_proc_stmt_iterate +%type <NONE> sp_proc_stmt_goto +%type <NONE> sp_proc_stmt_open sp_proc_stmt_fetch sp_proc_stmt_close +%type <NONE> case_stmt_specification +%type <NONE> loop_body while_body repeat_body + +%type <num> view_algorithm view_check_option +%type <view_suid> view_suid opt_view_suid + +%type <num> sp_decl_idents sp_decl_idents_init_vars +%type <num> sp_handler_type sp_hcond_list +%type <spcondvalue> sp_cond sp_hcond sqlstate signal_value opt_signal_value +%type <spblock> sp_decl_body_list opt_sp_decl_body_list +%type <spblock> sp_decl_non_handler sp_decl_non_handler_list +%type <spblock> sp_decl_handler sp_decl_handler_list opt_sp_decl_handler_list +%type <spblock_handlers> sp_block_statements_and_exceptions +%type <sp_instr_addr> sp_instr_addr +%type <sp_cursor_name_and_offset> sp_cursor_name_and_offset +%type <num> opt_exception_clause exception_handlers +%type <spname> sp_name opt_sp_name +%type <spvar> sp_param_name sp_param_name_and_type +%type <for_loop> sp_for_loop_index_and_bounds +%type <for_loop_bounds> sp_for_loop_bounds +%type <num> opt_sp_for_loop_direction +%type <spvar_mode> sp_opt_inout +%type <index_hint> index_hint_type +%type <num> index_hint_clause normal_join inner_join +%type <filetype> data_or_xml + +%type <NONE> signal_stmt resignal_stmt raise_stmt +%type <diag_condition_item_name> signal_condition_information_item_name + +%type <trg_execution_order> trigger_follows_precedes_clause; +%type <trigger_action_order_type> trigger_action_order; + +%type <diag_area> which_area; +%type <diag_info> diagnostics_information; +%type <stmt_info_item> statement_information_item; +%type <stmt_info_item_name> statement_information_item_name; +%type <stmt_info_list> statement_information; +%type <cond_info_item> condition_information_item; +%type <cond_info_item_name> condition_information_item_name; +%type <cond_info_list> condition_information; + +%type <spvar_definition> row_field_name row_field_definition +%type <spvar_definition_list> row_field_definition_list row_type_body + +%type <NONE> opt_window_clause window_def_list window_def window_spec +%type <lex_str_ptr> window_name +%type <NONE> opt_window_ref opt_window_frame_clause +%type <frame_units> window_frame_units; +%type <NONE> window_frame_extent; +%type <frame_exclusion> opt_window_frame_exclusion; +%type <window_frame_bound> window_frame_start window_frame_bound; + + +%type <NONE> + '-' '+' '*' '/' '%' '(' ')' + ',' '!' '{' '}' '&' '|' AND_SYM OR_SYM OR_OR_SYM BETWEEN_SYM CASE_SYM + THEN_SYM WHEN_SYM DIV_SYM MOD_SYM OR2_SYM AND_AND_SYM DELETE_SYM + ROLE_SYM + +%type <with_clause> opt_with_clause with_clause + +%type <lex_str_ptr> query_name + +%type <lex_str_list> opt_with_column_list + +%% + + +/* + Indentation of grammar rules: + +rule: <-- starts at col 1 + rule1a rule1b rule1c <-- starts at col 11 + { <-- starts at col 11 + code <-- starts at col 13, indentation is 2 spaces + } + | rule2a rule2b + { + code + } + ; <-- on a line by itself, starts at col 9 + + Also, please do not use any <TAB>, but spaces. + Having a uniform indentation in this file helps + code reviews, patches, merges, and make maintenance easier. + Tip: grep [[:cntrl:]] sql_yacc.yy + Thanks. +*/ + +query: + END_OF_INPUT + { + if (!thd->bootstrap && + (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT))) + my_yyabort_error((ER_EMPTY_QUERY, MYF(0))); + + thd->lex->sql_command= SQLCOM_EMPTY_QUERY; + YYLIP->found_semicolon= NULL; + } + | verb_clause + { + Lex_input_stream *lip = YYLIP; + + if ((thd->client_capabilities & CLIENT_MULTI_QUERIES) && + lip->multi_statements && + ! lip->eof()) + { + /* + We found a well formed query, and multi queries are allowed: + - force the parser to stop after the ';' + - mark the start of the next query for the next invocation + of the parser. + */ + lip->next_state= MY_LEX_END; + lip->found_semicolon= lip->get_ptr(); + } + else + { + /* Single query, terminated. */ + lip->found_semicolon= NULL; + } + } + ';' + opt_end_of_input + | verb_clause END_OF_INPUT + { + /* Single query, not terminated. */ + YYLIP->found_semicolon= NULL; + } + ; + +opt_end_of_input: + /* empty */ + | END_OF_INPUT + ; + +verb_clause: + statement + | compound_statement + ; + +/* Verb clauses, except begin and compound_statement */ +statement: + alter + | analyze + | analyze_stmt_command + | binlog_base64_event + | call + | change + | check + | checksum + | commit + | create + | deallocate + | delete + | describe + | do + | drop + | execute + | flush + | get_diagnostics + | grant + | handler + | help + | insert + | install + | keep_gcc_happy + | keycache + | kill + | load + | lock + | optimize + | parse_vcol_expr + | partition_entry + | preload + | prepare + | purge + | raise_stmt + | release + | rename + | repair + | replace + | reset + | resignal_stmt + | revoke + | rollback + | savepoint + | select + | set + | set_assign + | signal_stmt + | show + | shutdown + | slave + | start + | truncate + | uninstall + | unlock + | update + | use + | xa + ; + +deallocate: + deallocate_or_drop PREPARE_SYM ident + { + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; + lex->prepared_stmt_name= $3; + } + ; + +deallocate_or_drop: + DEALLOCATE_SYM + | DROP + ; + +prepare: + PREPARE_SYM ident FROM prepare_src + { + LEX *lex= thd->lex; + if (lex->table_or_sp_used()) + my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), + "PREPARE..FROM")); + lex->sql_command= SQLCOM_PREPARE; + lex->prepared_stmt_name= $2; + } + ; + +prepare_src: + { Lex->expr_allows_subselect= false; } + expr + { + Lex->prepared_stmt_code= $2; + Lex->expr_allows_subselect= true; + } + ; + +execute: + EXECUTE_SYM ident + { + LEX *lex= thd->lex; + lex->sql_command= SQLCOM_EXECUTE; + lex->prepared_stmt_name= $2; + } + execute_using + {} + | EXECUTE_SYM IMMEDIATE_SYM prepare_src + { + if (Lex->table_or_sp_used()) + my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), + "EXECUTE IMMEDIATE")); + Lex->sql_command= SQLCOM_EXECUTE_IMMEDIATE; + } + execute_using + {} + ; + +execute_using: + /* nothing */ + | USING { Lex->expr_allows_subselect= false; } + execute_var_list + { + if (Lex->table_or_sp_used()) + my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), + "EXECUTE..USING")); + Lex->expr_allows_subselect= true; + } + ; + +execute_var_list: + execute_var_list ',' execute_var_ident + | execute_var_ident + ; + +execute_var_ident: + expr_or_default + { + if (Lex->prepared_stmt_params.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +/* help */ + +help: + HELP_SYM + { + if (Lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HELP")); + } + ident_or_text + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_HELP; + lex->help_arg= $3.str; + } + ; + +/* change master */ + +change: + CHANGE MASTER_SYM optional_connection_name TO_SYM + { + Lex->sql_command = SQLCOM_CHANGE_MASTER; + } + master_defs + {} + ; + +master_defs: + master_def + | master_defs ',' master_def + ; + +master_def: + MASTER_HOST_SYM '=' TEXT_STRING_sys + { + Lex->mi.host = $3.str; + } + | MASTER_USER_SYM '=' TEXT_STRING_sys + { + Lex->mi.user = $3.str; + } + | MASTER_PASSWORD_SYM '=' TEXT_STRING_sys + { + Lex->mi.password = $3.str; + } + | MASTER_PORT_SYM '=' ulong_num + { + Lex->mi.port = $3; + } + | MASTER_CONNECT_RETRY_SYM '=' ulong_num + { + Lex->mi.connect_retry = $3; + } + | MASTER_DELAY_SYM '=' ulong_num + { + if ($3 > MASTER_DELAY_MAX) + { + my_error(ER_MASTER_DELAY_VALUE_OUT_OF_RANGE, MYF(0), + (ulong) $3, (ulong) MASTER_DELAY_MAX); + } + else + Lex->mi.sql_delay = $3; + } + | MASTER_SSL_SYM '=' ulong_num + { + Lex->mi.ssl= $3 ? + LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE; + } + | MASTER_SSL_CA_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_ca= $3.str; + } + | MASTER_SSL_CAPATH_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_capath= $3.str; + } + | MASTER_SSL_CERT_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_cert= $3.str; + } + | MASTER_SSL_CIPHER_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_cipher= $3.str; + } + | MASTER_SSL_KEY_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_key= $3.str; + } + | MASTER_SSL_VERIFY_SERVER_CERT_SYM '=' ulong_num + { + Lex->mi.ssl_verify_server_cert= $3 ? + LEX_MASTER_INFO::LEX_MI_ENABLE : LEX_MASTER_INFO::LEX_MI_DISABLE; + } + | MASTER_SSL_CRL_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_crl= $3.str; + } + | MASTER_SSL_CRLPATH_SYM '=' TEXT_STRING_sys + { + Lex->mi.ssl_crlpath= $3.str; + } + + | MASTER_HEARTBEAT_PERIOD_SYM '=' NUM_literal + { + Lex->mi.heartbeat_period= (float) $3->val_real(); + if (Lex->mi.heartbeat_period > SLAVE_MAX_HEARTBEAT_PERIOD || + Lex->mi.heartbeat_period < 0.0) + my_yyabort_error((ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE, MYF(0), + SLAVE_MAX_HEARTBEAT_PERIOD)); + + if (Lex->mi.heartbeat_period > slave_net_timeout) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX, + ER_THD(thd, ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MAX)); + } + if (Lex->mi.heartbeat_period < 0.001) + { + if (Lex->mi.heartbeat_period != 0.0) + { + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN, + ER_THD(thd, ER_SLAVE_HEARTBEAT_VALUE_OUT_OF_RANGE_MIN)); + Lex->mi.heartbeat_period= 0.0; + } + Lex->mi.heartbeat_opt= LEX_MASTER_INFO::LEX_MI_DISABLE; + } + Lex->mi.heartbeat_opt= LEX_MASTER_INFO::LEX_MI_ENABLE; + } + | IGNORE_SERVER_IDS_SYM '=' '(' ignore_server_id_list ')' + { + Lex->mi.repl_ignore_server_ids_opt= LEX_MASTER_INFO::LEX_MI_ENABLE; + } + | DO_DOMAIN_IDS_SYM '=' '(' do_domain_id_list ')' + { + Lex->mi.repl_do_domain_ids_opt= LEX_MASTER_INFO::LEX_MI_ENABLE; + } + | IGNORE_DOMAIN_IDS_SYM '=' '(' ignore_domain_id_list ')' + { + Lex->mi.repl_ignore_domain_ids_opt= LEX_MASTER_INFO::LEX_MI_ENABLE; + } + | + master_file_def + ; + +ignore_server_id_list: + /* Empty */ + | ignore_server_id + | ignore_server_id_list ',' ignore_server_id + ; + +ignore_server_id: + ulong_num + { + insert_dynamic(&Lex->mi.repl_ignore_server_ids, (uchar*) &($1)); + } + ; + +do_domain_id_list: + /* Empty */ + | do_domain_id + | do_domain_id_list ',' do_domain_id + ; + +do_domain_id: + ulong_num + { + insert_dynamic(&Lex->mi.repl_do_domain_ids, (uchar*) &($1)); + } + ; + +ignore_domain_id_list: + /* Empty */ + | ignore_domain_id + | ignore_domain_id_list ',' ignore_domain_id + ; + +ignore_domain_id: + ulong_num + { + insert_dynamic(&Lex->mi.repl_ignore_domain_ids, (uchar*) &($1)); + } + ; + +master_file_def: + MASTER_LOG_FILE_SYM '=' TEXT_STRING_sys + { + Lex->mi.log_file_name = $3.str; + } + | MASTER_LOG_POS_SYM '=' ulonglong_num + { + /* + If the user specified a value < BIN_LOG_HEADER_SIZE, adjust it + instead of causing subsequent errors. + We need to do it in this file, because only there we know that + MASTER_LOG_POS has been explicitly specified. On the contrary + in change_master() (sql_repl.cc) we cannot distinguish between 0 + (MASTER_LOG_POS explicitly specified as 0) and 0 (unspecified), + whereas we want to distinguish (specified 0 means "read the binlog + from 0" (4 in fact), unspecified means "don't change the position + (keep the preceding value)"). + */ + Lex->mi.pos= MY_MAX(BIN_LOG_HEADER_SIZE, $3); + } + | RELAY_LOG_FILE_SYM '=' TEXT_STRING_sys + { + Lex->mi.relay_log_name = $3.str; + } + | RELAY_LOG_POS_SYM '=' ulong_num + { + Lex->mi.relay_log_pos = $3; + /* Adjust if < BIN_LOG_HEADER_SIZE (same comment as Lex->mi.pos) */ + Lex->mi.relay_log_pos= MY_MAX(BIN_LOG_HEADER_SIZE, Lex->mi.relay_log_pos); + } + | MASTER_USE_GTID_SYM '=' CURRENT_POS_SYM + { + if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid")); + Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_CURRENT_POS; + } + | MASTER_USE_GTID_SYM '=' SLAVE_POS_SYM + { + if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid")); + Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_SLAVE_POS; + } + | MASTER_USE_GTID_SYM '=' NO_SYM + { + if (Lex->mi.use_gtid_opt != LEX_MASTER_INFO::LEX_GTID_UNCHANGED) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "MASTER_use_gtid")); + Lex->mi.use_gtid_opt= LEX_MASTER_INFO::LEX_GTID_NO; + } + ; + +optional_connection_name: + /* empty */ + { + LEX *lex= thd->lex; + lex->mi.connection_name= null_clex_str; + } + | connection_name + ; + +connection_name: + TEXT_STRING_sys + { + Lex->mi.connection_name= $1; +#ifdef HAVE_REPLICATION + if (check_master_connection_name(&$1)) + my_yyabort_error((ER_WRONG_ARGUMENTS, MYF(0), "MASTER_CONNECTION_NAME")); +#endif + } + ; + +/* create a table */ + +create: + create_or_replace opt_temporary TABLE_SYM opt_if_not_exists table_ident + { + LEX *lex= thd->lex; + lex->create_info.init(); + if (lex->set_command_with_check(SQLCOM_CREATE_TABLE, $2, $1 | $4)) + MYSQL_YYABORT; + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + lex->alter_info.reset(); + /* + For CREATE TABLE we should not open the table even if it exists. + If the table exists, we should either not create it or replace it + */ + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + lex->create_info.default_table_charset= NULL; + lex->name= null_clex_str; + lex->create_last_non_select_table= lex->last_table(); + } + create_body + { + LEX *lex= thd->lex; + lex->current_select= &lex->select_lex; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.use_default_db_type(thd); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_USING_OTHER_HANDLER, + ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), + hton_name(lex->create_info.db_type)->str, + $5->table.str); + } + create_table_set_open_action_and_adjust_tables(lex); + } + | create_or_replace opt_temporary SEQUENCE_SYM opt_if_not_exists table_ident + { + LEX *lex= thd->lex; + lex->create_info.init(); + if (lex->set_command_with_check(SQLCOM_CREATE_SEQUENCE, $2, $1 | $4)) + MYSQL_YYABORT; + + if (!lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + + /* + For CREATE TABLE, an non-existing table is not an error. + Instruct open_tables() to just take an MDL lock if the + table does not exist. + */ + lex->alter_info.reset(); + lex->query_tables->open_strategy= TABLE_LIST::OPEN_STUB; + lex->name= null_clex_str; + lex->create_last_non_select_table= lex->last_table(); + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition())) + MYSQL_YYABORT; + } + opt_sequence opt_create_table_options + { + LEX *lex= thd->lex; + + if (lex->create_info.seq_create_info->check_and_adjust(1)) + { + my_error(ER_SEQUENCE_INVALID_DATA, MYF(0), + lex->select_lex.table_list.first->db, + lex->select_lex.table_list.first->table_name); + MYSQL_YYABORT; + } + + /* No fields specified, generate them */ + if (prepare_sequence_fields(thd, &lex->alter_info.create_list)) + MYSQL_YYABORT; + + /* CREATE SEQUENCE always creates a sequence */ + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= 1; + + lex->current_select= &lex->select_lex; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.use_default_db_type(thd); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_WARN_USING_OTHER_HANDLER, + ER_THD(thd, ER_WARN_USING_OTHER_HANDLER), + hton_name(lex->create_info.db_type)->str, + $5->table.str); + } + create_table_set_open_action_and_adjust_tables(lex); + } + | create_or_replace opt_unique INDEX_SYM opt_if_not_exists ident + opt_key_algorithm_clause + ON table_ident + { + if (Lex->add_create_index_prepare($8)) + MYSQL_YYABORT; + if (Lex->add_create_index($2, &$5, $6, $1 | $4)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options + opt_index_lock_algorithm { } + | create_or_replace fulltext INDEX_SYM opt_if_not_exists ident + ON table_ident + { + if (Lex->add_create_index_prepare($7)) + MYSQL_YYABORT; + if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) + MYSQL_YYABORT; + } + '(' key_list ')' fulltext_key_options + opt_index_lock_algorithm { } + | create_or_replace spatial INDEX_SYM opt_if_not_exists ident + ON table_ident + { + if (Lex->add_create_index_prepare($7)) + MYSQL_YYABORT; + if (Lex->add_create_index($2, &$5, HA_KEY_ALG_UNDEF, $1 | $4)) + MYSQL_YYABORT; + } + '(' key_list ')' spatial_key_options + opt_index_lock_algorithm { } + | create_or_replace DATABASE opt_if_not_exists ident + { + Lex->create_info.default_table_charset= NULL; + Lex->create_info.used_fields= 0; + } + opt_create_database_options + { + LEX *lex=Lex; + if (lex->set_command_with_check(SQLCOM_CREATE_DB, 0, $1 | $3)) + MYSQL_YYABORT; + lex->name= $4; + } + | create_or_replace definer_opt opt_view_suid VIEW_SYM + opt_if_not_exists table_ident + { + if (Lex->add_create_view(thd, $1 | $5, + DTYPE_ALGORITHM_UNDEFINED, $3, $6)) + MYSQL_YYABORT; + } + view_list_opt AS view_select + { } + | create_or_replace view_algorithm definer_opt opt_view_suid VIEW_SYM + opt_if_not_exists table_ident + { + if (Lex->add_create_view(thd, $1 | $6, $2, $4, $7)) + MYSQL_YYABORT; + } + view_list_opt AS view_select + { } + | create_or_replace definer_opt TRIGGER_SYM + { Lex->create_info.set($1); } + trigger_tail + { } + | create_or_replace definer_opt PROCEDURE_SYM + { Lex->create_info.set($1); } + sp_tail_standalone + { } + | create_or_replace definer_opt EVENT_SYM + { Lex->create_info.set($1); } + event_tail + { } + | create_or_replace definer FUNCTION_SYM + { Lex->create_info.set($1); } + sf_tail_standalone + { } + | create_or_replace no_definer FUNCTION_SYM + { Lex->create_info.set($1); } + create_function_tail + { } + | create_or_replace no_definer AGGREGATE_SYM FUNCTION_SYM + { + Lex->create_info.set($1); + Lex->udf.type= UDFTYPE_AGGREGATE; + } + udf_tail + { } + | create_or_replace USER_SYM opt_if_not_exists clear_privileges grant_list + opt_require_clause opt_resource_options + { + if (Lex->set_command_with_check(SQLCOM_CREATE_USER, $1 | $3)) + MYSQL_YYABORT; + } + | create_or_replace ROLE_SYM opt_if_not_exists + clear_privileges role_list opt_with_admin + { + if (Lex->set_command_with_check(SQLCOM_CREATE_ROLE, $1 | $3)) + MYSQL_YYABORT; + } + | CREATE LOGFILE_SYM GROUP_SYM logfile_group_info + { + Lex->alter_tablespace_info->ts_cmd_type= CREATE_LOGFILE_GROUP; + } + | CREATE TABLESPACE tablespace_info + { + Lex->alter_tablespace_info->ts_cmd_type= CREATE_TABLESPACE; + } + | create_or_replace { Lex->set_command(SQLCOM_CREATE_SERVER, $1); } + server_def + { } + ; + +create_function_tail: + sf_tail_standalone { } + | udf_tail { Lex->udf.type= UDFTYPE_FUNCTION; } + ; + +opt_sequence: + /* empty */ { } + | sequence_defs + ; + +sequence_defs: + sequence_def + | sequence_defs sequence_def + ; + +sequence_def: + MINVALUE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->min_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | NO_SYM MINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | NOMINVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_min_value) + MYSQL_YYABORT; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_min_value; + } + | MAXVALUE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->max_value= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | NO_SYM MAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | NOMAXVALUE_SYM + { + if (Lex->create_info.seq_create_info->used_fields & seq_field_used_max_value) + MYSQL_YYABORT; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_max_value; + } + | START_SYM opt_with longlong_num + { + Lex->create_info.seq_create_info->start= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_start; + } + | INCREMENT_SYM opt_by longlong_num + { + Lex->create_info.seq_create_info->increment= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_increment; + } + | CACHE_SYM opt_equal longlong_num + { + Lex->create_info.seq_create_info->cache= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_cache; + } + | NOCACHE_SYM + { + Lex->create_info.seq_create_info->cache= 0; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_cache; + } + | CYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 1; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_cycle; + } + | NOCYCLE_SYM + { + Lex->create_info.seq_create_info->cycle= 0; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_cycle; + } + | RESTART_SYM + { + if (Lex->sql_command != SQLCOM_ALTER_SEQUENCE) + { + thd->parse_error(ER_SYNTAX_ERROR, "RESTART"); + YYABORT; + } + Lex->create_info.seq_create_info->used_fields|= seq_field_used_restart; + } + | RESTART_SYM opt_with longlong_num + { + if (Lex->sql_command != SQLCOM_ALTER_SEQUENCE) + { + thd->parse_error(ER_SYNTAX_ERROR, "RESTART"); + YYABORT; + } + Lex->create_info.seq_create_info->restart= $3; + Lex->create_info.seq_create_info->used_fields|= seq_field_used_restart | seq_field_used_restart_value; + } + ; + +server_def: + SERVER_SYM opt_if_not_exists ident_or_text + { + if (Lex->add_create_options_with_check($2)) + MYSQL_YYABORT; + Lex->server_options.reset($3); + } + FOREIGN DATA_SYM WRAPPER_SYM ident_or_text + OPTIONS_SYM '(' server_options_list ')' + { Lex->server_options.scheme= $8; } + ; + +server_options_list: + server_option + | server_options_list ',' server_option + ; + +server_option: + USER_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.username.str == 0); + Lex->server_options.username= $2; + } + | HOST_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.host.str == 0); + Lex->server_options.host= $2; + my_casedn_str(system_charset_info, + (char*) Lex->server_options.host.str); + } + | DATABASE TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.db.str == 0); + Lex->server_options.db= $2; + } + | OWNER_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.owner.str == 0); + Lex->server_options.owner= $2; + } + | PASSWORD_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.password.str == 0); + Lex->server_options.password= $2; + } + | SOCKET_SYM TEXT_STRING_sys + { + MYSQL_YYABORT_UNLESS(Lex->server_options.socket.str == 0); + Lex->server_options.socket= $2; + } + | PORT_SYM ulong_num + { + Lex->server_options.port= $2; + } + ; + +event_tail: + remember_name opt_if_not_exists sp_name + { + LEX *lex=Lex; + + lex->stmt_definition_begin= $1; + if (lex->add_create_options_with_check($2)) + MYSQL_YYABORT; + if (!(lex->event_parse_data= Event_parse_data::new_instance(thd))) + MYSQL_YYABORT; + lex->event_parse_data->identifier= $3; + lex->event_parse_data->on_completion= + Event_parse_data::ON_COMPLETION_DROP; + + lex->sql_command= SQLCOM_CREATE_EVENT; + /* We need that for disallowing subqueries */ + } + ON SCHEDULE_SYM ev_schedule_time + opt_ev_on_completion + opt_ev_status + opt_ev_comment + DO_SYM ev_sql_stmt + { + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ + Lex->sql_command= SQLCOM_CREATE_EVENT; + } + ; + +ev_schedule_time: + EVERY_SYM expr interval + { + Lex->event_parse_data->item_expression= $2; + Lex->event_parse_data->interval= $3; + } + ev_starts + ev_ends + | AT_SYM expr + { + Lex->event_parse_data->item_execute_at= $2; + } + ; + +opt_ev_status: + /* empty */ { $$= 0; } + | ENABLE_SYM + { + Lex->event_parse_data->status= Event_parse_data::ENABLED; + Lex->event_parse_data->status_changed= true; + $$= 1; + } + | DISABLE_SYM ON SLAVE + { + Lex->event_parse_data->status= Event_parse_data::SLAVESIDE_DISABLED; + Lex->event_parse_data->status_changed= true; + $$= 1; + } + | DISABLE_SYM + { + Lex->event_parse_data->status= Event_parse_data::DISABLED; + Lex->event_parse_data->status_changed= true; + $$= 1; + } + ; + +ev_starts: + /* empty */ + { + Item *item= new (thd->mem_root) Item_func_now_local(thd, 0); + if (item == NULL) + MYSQL_YYABORT; + Lex->event_parse_data->item_starts= item; + } + | STARTS_SYM expr + { + Lex->event_parse_data->item_starts= $2; + } + ; + +ev_ends: + /* empty */ + | ENDS_SYM expr + { + Lex->event_parse_data->item_ends= $2; + } + ; + +opt_ev_on_completion: + /* empty */ { $$= 0; } + | ev_on_completion + ; + +ev_on_completion: + ON COMPLETION_SYM opt_not PRESERVE_SYM + { + Lex->event_parse_data->on_completion= $3 + ? Event_parse_data::ON_COMPLETION_DROP + : Event_parse_data::ON_COMPLETION_PRESERVE; + $$= 1; + } + ; + +opt_ev_comment: + /* empty */ { $$= 0; } + | COMMENT_SYM TEXT_STRING_sys + { + Lex->comment= Lex->event_parse_data->comment= $2; + $$= 1; + } + ; + +ev_sql_stmt: + { + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + + /* + This stops the following : + - CREATE EVENT ... DO CREATE EVENT ...; + - ALTER EVENT ... DO CREATE EVENT ...; + - CREATE EVENT ... DO ALTER EVENT DO ....; + - CREATE PROCEDURE ... BEGIN CREATE EVENT ... END| + This allows: + - CREATE EVENT ... DO DROP EVENT yyy; + - CREATE EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO ALTER EVENT yyy; + (the nested ALTER EVENT can have anything but DO clause) + - ALTER EVENT ... DO DROP EVENT yyy; + - CREATE PROCEDURE ... BEGIN ALTER EVENT ... END| + (the nested ALTER EVENT can have anything but DO clause) + - CREATE PROCEDURE ... BEGIN DROP EVENT ... END| + */ + if (lex->sphead) + my_yyabort_error((ER_EVENT_RECURSION_FORBIDDEN, MYF(0))); + + if (!lex->make_sp_head(thd, lex->event_parse_data->identifier, + &sp_handler_procedure)) + MYSQL_YYABORT; + + lex->sphead->set_body_start(thd, lip->get_cpp_ptr()); + } + sp_proc_stmt + { + LEX *lex= thd->lex; + + /* return back to the original memory root ASAP */ + lex->sphead->set_stmt_end(thd); + lex->sphead->restore_thd_mem_root(thd); + + lex->event_parse_data->body_changed= TRUE; + } + ; + +clear_privileges: + /* Nothing */ + { + LEX *lex=Lex; + lex->users_list.empty(); + lex->columns.empty(); + lex->grant= lex->grant_tot_col= 0; + lex->all_privileges= 0; + lex->select_lex.db= 0; + lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; + bzero((char *)&(lex->mqh),sizeof(lex->mqh)); + } + ; + +sp_name: + ident '.' ident + { + if (!($$= Lex->make_sp_name(thd, &$1, &$3))) + MYSQL_YYABORT; + } + | ident + { + if (!($$= Lex->make_sp_name(thd, &$1))) + MYSQL_YYABORT; + } + ; + +opt_sp_name: + /* Empty */ { $$= NULL; } + | sp_name { $$= $1; } + ; + +sp_a_chistics: + /* Empty */ {} + | sp_a_chistics sp_chistic {} + ; + +sp_c_chistics: + /* Empty */ {} + | sp_c_chistics sp_c_chistic {} + ; + +/* Characteristics for both create and alter */ +sp_chistic: + COMMENT_SYM TEXT_STRING_sys + { Lex->sp_chistics.comment= $2; } + | LANGUAGE_SYM SQL_SYM + { /* Just parse it, we only have one language for now. */ } + | NO_SYM SQL_SYM + { Lex->sp_chistics.daccess= SP_NO_SQL; } + | CONTAINS_SYM SQL_SYM + { Lex->sp_chistics.daccess= SP_CONTAINS_SQL; } + | READS_SYM SQL_SYM DATA_SYM + { Lex->sp_chistics.daccess= SP_READS_SQL_DATA; } + | MODIFIES_SYM SQL_SYM DATA_SYM + { Lex->sp_chistics.daccess= SP_MODIFIES_SQL_DATA; } + | sp_suid + {} + ; + +/* Create characteristics */ +sp_c_chistic: + sp_chistic { } + | opt_not DETERMINISTIC_SYM { Lex->sp_chistics.detistic= ! $1; } + ; + +sp_suid: + SQL_SYM SECURITY_SYM DEFINER_SYM + { + Lex->sp_chistics.suid= SP_IS_SUID; + } + | SQL_SYM SECURITY_SYM INVOKER_SYM + { + Lex->sp_chistics.suid= SP_IS_NOT_SUID; + } + ; + +call: + CALL_SYM sp_name + { + if (Lex->call_statement_start(thd, $2)) + MYSQL_YYABORT; + } + opt_sp_cparam_list {} + ; + +/* CALL parameters */ +opt_sp_cparam_list: + /* Empty */ + | '(' opt_sp_cparams ')' + ; + +opt_sp_cparams: + /* Empty */ + | sp_cparams + ; + +sp_cparams: + sp_cparams ',' expr + { + Lex->value_list.push_back($3, thd->mem_root); + } + | expr + { + Lex->value_list.push_back($1, thd->mem_root); + } + ; + +/* Stored FUNCTION parameter declaration list */ +sp_fdparam_list: + /* Empty */ + | sp_fdparams + ; + +sp_fdparams: + sp_fdparams ',' sp_param_name_and_type + | sp_param_name_and_type + ; + +sp_param_name: + ident + { + if (!($$= Lex->sp_param_init(&$1))) + MYSQL_YYABORT; + } + ; + +sp_param_name_and_type: + sp_param_name sp_param_type_with_opt_collate + { + if (Lex->sp_param_fill_definition($$= $1)) + MYSQL_YYABORT; + } + | sp_param_name sp_decl_ident '.' ident '%' TYPE_SYM + { + if (Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $2, $4)) + MYSQL_YYABORT; + } + | sp_param_name sp_decl_ident '.' ident '.' ident '%' TYPE_SYM + { + if (Lex->sphead->spvar_fill_type_reference(thd, $$= $1, $2, $4, $6)) + MYSQL_YYABORT; + } + | sp_param_name sp_decl_ident '%' ROWTYPE_SYM + { + if (Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $2)) + MYSQL_YYABORT; + } + | sp_param_name sp_decl_ident '.' ident '%' ROWTYPE_SYM + { + if (Lex->sphead->spvar_fill_table_rowtype_reference(thd, $$= $1, $2, $4)) + MYSQL_YYABORT; + } + | sp_param_name ROW_SYM row_type_body + { + if (Lex->sphead->spvar_fill_row(thd, $$= $1, $3)) + MYSQL_YYABORT; + } + ; + +/* Stored PROCEDURE parameter declaration list */ +sp_pdparam_list: + /* Empty */ + | sp_pdparams + ; + +sp_pdparams: + sp_pdparams ',' sp_pdparam + | sp_pdparam + ; + +sp_pdparam: + sp_param_name sp_opt_inout sp_param_type_with_opt_collate + { + $1->mode= $2; + if (Lex->sp_param_fill_definition($1)) + MYSQL_YYABORT; + } + | sp_param_name sp_opt_inout sp_decl_ident '.' ident '%' TYPE_SYM + { + $1->mode= $2; + if (Lex->sphead->spvar_fill_type_reference(thd, $1, $3, $5)) + MYSQL_YYABORT; + } + | sp_param_name sp_opt_inout sp_decl_ident '.' ident '.' ident '%' TYPE_SYM + { + $1->mode= $2; + if (Lex->sphead->spvar_fill_type_reference(thd, $1, $3, $5, $7)) + MYSQL_YYABORT; + } + | sp_param_name sp_opt_inout sp_decl_ident '%' ROWTYPE_SYM + { + $1->mode= $2; + if (Lex->sphead->spvar_fill_table_rowtype_reference(thd, $1, $3)) + MYSQL_YYABORT; + } + | sp_param_name sp_opt_inout sp_decl_ident '.' ident '%' ROWTYPE_SYM + { + $1->mode= $2; + if (Lex->sphead->spvar_fill_table_rowtype_reference(thd, $1, $3, $5)) + MYSQL_YYABORT; + } + | sp_param_name sp_opt_inout ROW_SYM row_type_body + { + $1->mode= $2; + if (Lex->sphead->spvar_fill_row(thd, $1, $4)) + MYSQL_YYABORT; + } + ; + +sp_opt_inout: + /* Empty */ { $$= sp_variable::MODE_IN; } + | IN_SYM { $$= sp_variable::MODE_IN; } + | OUT_SYM { $$= sp_variable::MODE_OUT; } + | INOUT_SYM { $$= sp_variable::MODE_INOUT; } + | IN_SYM OUT_SYM { $$= sp_variable::MODE_INOUT; } + ; + +sp_parenthesized_fdparam_list: + '(' + { + Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start() + 1; + } + sp_fdparam_list + ')' + { + Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start(); + } + ; + +sp_parenthesized_pdparam_list: + '(' + { + Lex->sphead->m_param_begin= YYLIP->get_cpp_tok_start() + 1; + } + sp_pdparam_list + ')' + { + Lex->sphead->m_param_end= YYLIP->get_cpp_tok_start(); + } + ; + +sp_no_param: + /* Empty */ + { + Lex->sphead->m_param_begin= Lex->sphead->m_param_end= + YYLIP->get_cpp_tok_start() + 1; + } + ; + +opt_sp_parenthesized_fdparam_list: + sp_no_param + | sp_parenthesized_fdparam_list + ; + +opt_sp_parenthesized_pdparam_list: + sp_no_param + | sp_parenthesized_pdparam_list + ; + +sp_proc_stmts: + /* Empty */ {} + | sp_proc_stmts sp_proc_stmt ';' + ; + +sp_proc_stmts1: + sp_proc_stmt ';' {} + | sp_proc_stmts1 sp_proc_stmt ';' + ; + +sp_proc_stmts1_implicit_block: + { + Lex->sp_block_init(thd); + } + sp_proc_stmts1 + { + if (Lex->sp_block_finalize(thd)) + MYSQL_YYABORT; + } + ; + +opt_sp_decl_body_list: + /* Empty */ + { + $$.init(); + } + | sp_decl_body_list { $$= $1; } + ; + +sp_decl_body_list: + sp_decl_non_handler_list + { + if (Lex->sphead->sp_add_instr_cpush_for_cursors(thd, Lex->spcont)) + MYSQL_YYABORT; + } + opt_sp_decl_handler_list + { + $$.join($1, $3); + } + | sp_decl_handler_list + ; + +sp_decl_non_handler_list: + sp_decl_non_handler ';' { $$= $1; } + | sp_decl_non_handler_list sp_decl_non_handler ';' + { + $$.join($1, $2); + } + ; + +sp_decl_handler_list: + sp_decl_handler ';' { $$= $1; } + | sp_decl_handler_list sp_decl_handler ';' + { + $$.join($1, $2); + } + ; + +opt_sp_decl_handler_list: + /* Empty*/ { $$.init(); } + | sp_decl_handler_list + ; + +optionally_qualified_column_ident: + sp_decl_ident + { + if (!($$= new (thd->mem_root) Qualified_column_ident(&$1))) + MYSQL_YYABORT; + } + | sp_decl_ident '.' ident + { + if (!($$= new (thd->mem_root) Qualified_column_ident(&$1, &$3))) + MYSQL_YYABORT; + } + | sp_decl_ident '.' ident '.' ident + { + if (!($$= new (thd->mem_root) Qualified_column_ident(thd, + &$1, &$3, &$5))) + MYSQL_YYABORT; + } + ; + +row_field_name: + ident_directly_assignable + { + if (check_string_char_length(&$1, 0, NAME_CHAR_LEN, + system_charset_info, 1)) + my_yyabort_error((ER_TOO_LONG_IDENT, MYF(0), $1.str)); + if (!($$= new (thd->mem_root) Spvar_definition())) + MYSQL_YYABORT; + Lex->init_last_field($$, &$1, thd->variables.collation_database); + } + ; + +row_field_definition: + row_field_name type_with_opt_collate + ; + +row_field_definition_list: + row_field_definition + { + if (!($$= new (thd->mem_root) Row_definition_list())) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | row_field_definition_list ',' row_field_definition + { + uint unused; + if ($1->find_row_field_by_name(&$3->field_name, &unused)) + my_yyabort_error((ER_DUP_FIELDNAME, MYF(0), $3->field_name.str)); + $$= $1; + $$->push_back($3, thd->mem_root); + } + ; + +row_type_body: + '(' row_field_definition_list ')' { $$= $2; } + ; + +sp_decl_idents_init_vars: + sp_decl_idents + { + Lex->sp_variable_declarations_init(thd, $1); + } + ; + +sp_decl_non_handler: + sp_decl_idents_init_vars + type_with_opt_collate + sp_opt_default + { + if (Lex->sp_variable_declarations_finalize(thd, $1, + &Lex->last_field[0], $3)) + MYSQL_YYABORT; + $$.init_using_vars($1); + } + | sp_decl_idents_init_vars + optionally_qualified_column_ident '%' TYPE_SYM + sp_opt_default + { + if (Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $2, $5)) + MYSQL_YYABORT; + $$.init_using_vars($1); + } + | sp_decl_idents_init_vars + optionally_qualified_column_ident '%' ROWTYPE_SYM + sp_opt_default + { + if (Lex->sp_variable_declarations_rowtype_finalize(thd, $1, $2, $5)) + MYSQL_YYABORT; + $$.init_using_vars($1); + } + | sp_decl_idents_init_vars + ROW_SYM row_type_body + sp_opt_default + { + if (Lex->sp_variable_declarations_row_finalize(thd, $1, $3, $4)) + MYSQL_YYABORT; + $$.init_using_vars($1); + } + | ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond + { + if (Lex->spcont->declare_condition(thd, &$1, $4)) + MYSQL_YYABORT; + $$.vars= $$.hndlrs= $$.curs= 0; + $$.conds= 1; + } + | ident_directly_assignable EXCEPTION_SYM + { + sp_condition_value *spcond= new (thd->mem_root) + sp_condition_value_user_defined(); + if (!spcond || + Lex->spcont->declare_condition(thd, &$1, spcond)) + MYSQL_YYABORT; + $$.vars= $$.hndlrs= $$.curs= 0; + $$.conds= 1; + } + | CURSOR_SYM ident_directly_assignable + { + Lex->sp_block_init(thd); + } + opt_parenthesized_cursor_formal_parameters + IS sp_cursor_stmt + { + sp_pcontext *param_ctx= Lex->spcont; + if (Lex->sp_block_finalize(thd)) + MYSQL_YYABORT; + if (Lex->sp_declare_cursor(thd, &$2, $6, param_ctx, false)) + MYSQL_YYABORT; + $$.vars= $$.conds= $$.hndlrs= 0; + $$.curs= 1; + } + ; + +sp_decl_handler: + sp_handler_type HANDLER_SYM FOR_SYM + { + if (Lex->sp_handler_declaration_init(thd, $1)) + MYSQL_YYABORT; + } + sp_hcond_list sp_proc_stmt + { + if (Lex->sp_handler_declaration_finalize(thd, $1)) + MYSQL_YYABORT; + $$.vars= $$.conds= $$.curs= 0; + $$.hndlrs= 1; + } + ; + +opt_parenthesized_cursor_formal_parameters: + /* Empty */ + | '(' sp_fdparams ')' + ; + + +sp_cursor_stmt_lex: + { + DBUG_ASSERT(thd->lex->sphead); + if (!($$= new (thd->mem_root) sp_lex_cursor(thd, thd->lex))) + MYSQL_YYABORT; + } + ; + +sp_cursor_stmt: + sp_cursor_stmt_lex + { + DBUG_ASSERT(thd->free_list == NULL); + Lex->sphead->reset_lex(thd, $1); + } + select + { + DBUG_ASSERT(Lex == $1); + if ($1->stmt_finalize(thd) || + $1->sphead->restore_lex(thd)) + MYSQL_YYABORT; + $$= $1; + } + ; + +sp_handler_type: + EXIT_SYM { $$= sp_handler::EXIT; } + | CONTINUE_SYM { $$= sp_handler::CONTINUE; } + /*| UNDO_SYM { QQ No yet } */ + ; + +sp_hcond_list: + sp_hcond_element + { $$= 1; } + | sp_hcond_list ',' sp_hcond_element + { $$+= 1; } + ; + +sp_hcond_element: + sp_hcond + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont->parent_context(); + + if (ctx->check_duplicate_handler($1)) + my_yyabort_error((ER_SP_DUP_HANDLER, MYF(0))); + + sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction(); + i->add_condition($1); + } + ; + +sp_cond: + ulong_num + { /* mysql errno */ + if ($1 == 0) + my_yyabort_error((ER_WRONG_VALUE, MYF(0), "CONDITION", "0")); + $$= new (thd->mem_root) sp_condition_value($1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | sqlstate + ; + +sqlstate: + SQLSTATE_SYM opt_value TEXT_STRING_literal + { /* SQLSTATE */ + + /* + An error is triggered: + - if the specified string is not a valid SQLSTATE, + - or if it represents the completion condition -- it is not + allowed to SIGNAL, or declare a handler for the completion + condition. + */ + if (!is_sqlstate_valid(&$3) || is_sqlstate_completion($3.str)) + my_yyabort_error((ER_SP_BAD_SQLSTATE, MYF(0), $3.str)); + $$= new (thd->mem_root) sp_condition_value($3.str); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_value: + /* Empty */ {} + | VALUE_SYM {} + ; + +sp_hcond: + sp_cond + { + $$= $1; + } + | ident /* CONDITION name */ + { + $$= Lex->spcont->find_declared_or_predefined_condition(&$1); + if ($$ == NULL) + my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str)); + } + | SQLWARNING_SYM /* SQLSTATEs 01??? */ + { + $$= new (thd->mem_root) sp_condition_value(sp_condition_value::WARNING); + if ($$ == NULL) + MYSQL_YYABORT; + } + | not FOUND_SYM /* SQLSTATEs 02??? */ + { + $$= new (thd->mem_root) sp_condition_value(sp_condition_value::NOT_FOUND); + if ($$ == NULL) + MYSQL_YYABORT; + } + | OTHERS_SYM /* All other SQLSTATEs */ + { + $$= new (thd->mem_root) sp_condition_value(sp_condition_value::EXCEPTION); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + + +raise_stmt: + RAISE_SYM opt_set_signal_information + { + if (Lex->add_resignal_statement(thd, NULL)) + MYSQL_YYABORT; + } + | RAISE_SYM signal_value opt_set_signal_information + { + if (Lex->add_signal_statement(thd, $2)) + MYSQL_YYABORT; + } + ; + +signal_stmt: + SIGNAL_SYM signal_value opt_set_signal_information + { + if (Lex->add_signal_statement(thd, $2)) + MYSQL_YYABORT; + } + ; + +signal_value: + ident + { + LEX *lex= Lex; + sp_condition_value *cond; + + /* SIGNAL foo cannot be used outside of stored programs */ + if (lex->spcont == NULL) + my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str)); + cond= lex->spcont->find_declared_or_predefined_condition(&$1); + if (cond == NULL) + my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str)); + if (!cond->has_sql_state()) + my_yyabort_error((ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0))); + $$= cond; + } + | sqlstate + { $$= $1; } + ; + +opt_signal_value: + /* empty */ + { $$= NULL; } + | signal_value + { $$= $1; } + ; + +opt_set_signal_information: + /* empty */ + { + thd->m_parser_state->m_yacc.m_set_signal_info.clear(); + } + | SET signal_information_item_list + ; + +signal_information_item_list: + signal_condition_information_item_name '=' signal_allowed_expr + { + Set_signal_information *info; + info= &thd->m_parser_state->m_yacc.m_set_signal_info; + int index= (int) $1; + info->clear(); + info->m_item[index]= $3; + } + | signal_information_item_list ',' + signal_condition_information_item_name '=' signal_allowed_expr + { + Set_signal_information *info; + info= &thd->m_parser_state->m_yacc.m_set_signal_info; + int index= (int) $3; + if (info->m_item[index] != NULL) + my_yyabort_error((ER_DUP_SIGNAL_SET, MYF(0), + Diag_condition_item_names[index].str)); + info->m_item[index]= $5; + } + ; + +/* + Only a limited subset of <expr> are allowed in SIGNAL/RESIGNAL. +*/ +signal_allowed_expr: + literal + { $$= $1; } + | variable + { + if ($1->type() == Item::FUNC_ITEM) + { + Item_func *item= (Item_func*) $1; + if (item->functype() == Item_func::SUSERVAR_FUNC) + { + /* + Don't allow the following syntax: + SIGNAL/RESIGNAL ... + SET <signal condition item name> = @foo := expr + */ + thd->parse_error(); + MYSQL_YYABORT; + } + } + $$= $1; + } + | simple_ident + { $$= $1; } + ; + +/* conditions that can be set in signal / resignal */ +signal_condition_information_item_name: + CLASS_ORIGIN_SYM + { $$= DIAG_CLASS_ORIGIN; } + | SUBCLASS_ORIGIN_SYM + { $$= DIAG_SUBCLASS_ORIGIN; } + | CONSTRAINT_CATALOG_SYM + { $$= DIAG_CONSTRAINT_CATALOG; } + | CONSTRAINT_SCHEMA_SYM + { $$= DIAG_CONSTRAINT_SCHEMA; } + | CONSTRAINT_NAME_SYM + { $$= DIAG_CONSTRAINT_NAME; } + | CATALOG_NAME_SYM + { $$= DIAG_CATALOG_NAME; } + | SCHEMA_NAME_SYM + { $$= DIAG_SCHEMA_NAME; } + | TABLE_NAME_SYM + { $$= DIAG_TABLE_NAME; } + | COLUMN_NAME_SYM + { $$= DIAG_COLUMN_NAME; } + | CURSOR_NAME_SYM + { $$= DIAG_CURSOR_NAME; } + | MESSAGE_TEXT_SYM + { $$= DIAG_MESSAGE_TEXT; } + | MYSQL_ERRNO_SYM + { $$= DIAG_MYSQL_ERRNO; } + ; + +resignal_stmt: + RESIGNAL_SYM opt_signal_value opt_set_signal_information + { + if (Lex->add_resignal_statement(thd, $2)) + MYSQL_YYABORT; + } + ; + +get_diagnostics: + GET_SYM which_area DIAGNOSTICS_SYM diagnostics_information + { + Diagnostics_information *info= $4; + + info->set_which_da($2); + + Lex->sql_command= SQLCOM_GET_DIAGNOSTICS; + Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_get_diagnostics(info); + + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +which_area: + /* If <which area> is not specified, then CURRENT is implicit. */ + { $$= Diagnostics_information::CURRENT_AREA; } + | CURRENT_SYM + { $$= Diagnostics_information::CURRENT_AREA; } + ; + +diagnostics_information: + statement_information + { + $$= new (thd->mem_root) Statement_information($1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CONDITION_SYM condition_number condition_information + { + $$= new (thd->mem_root) Condition_information($2, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +statement_information: + statement_information_item + { + $$= new (thd->mem_root) List<Statement_information_item>; + if ($$ == NULL || $$->push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | statement_information ',' statement_information_item + { + if ($1->push_back($3, thd->mem_root)) + MYSQL_YYABORT; + $$= $1; + } + ; + +statement_information_item: + simple_target_specification '=' statement_information_item_name + { + $$= new (thd->mem_root) Statement_information_item($3, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + +simple_target_specification: + ident + { + Lex_input_stream *lip= &thd->m_parser_state->m_lip; + $$= thd->lex->create_item_for_sp_var(&$1, NULL, + lip->get_tok_start(), + lip->get_ptr()); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '@' ident_or_text + { + $$= new (thd->mem_root) Item_func_get_user_var(thd, &$2); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +statement_information_item_name: + NUMBER_SYM + { $$= Statement_information_item::NUMBER; } + | ROW_COUNT_SYM + { $$= Statement_information_item::ROW_COUNT; } + ; + +/* + Only a limited subset of <expr> are allowed in GET DIAGNOSTICS + <condition number>, same subset as for SIGNAL/RESIGNAL. +*/ +condition_number: + signal_allowed_expr + { $$= $1; } + ; + +condition_information: + condition_information_item + { + $$= new (thd->mem_root) List<Condition_information_item>; + if ($$ == NULL || $$->push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | condition_information ',' condition_information_item + { + if ($1->push_back($3, thd->mem_root)) + MYSQL_YYABORT; + $$= $1; + } + ; + +condition_information_item: + simple_target_specification '=' condition_information_item_name + { + $$= new (thd->mem_root) Condition_information_item($3, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + +condition_information_item_name: + CLASS_ORIGIN_SYM + { $$= Condition_information_item::CLASS_ORIGIN; } + | SUBCLASS_ORIGIN_SYM + { $$= Condition_information_item::SUBCLASS_ORIGIN; } + | CONSTRAINT_CATALOG_SYM + { $$= Condition_information_item::CONSTRAINT_CATALOG; } + | CONSTRAINT_SCHEMA_SYM + { $$= Condition_information_item::CONSTRAINT_SCHEMA; } + | CONSTRAINT_NAME_SYM + { $$= Condition_information_item::CONSTRAINT_NAME; } + | CATALOG_NAME_SYM + { $$= Condition_information_item::CATALOG_NAME; } + | SCHEMA_NAME_SYM + { $$= Condition_information_item::SCHEMA_NAME; } + | TABLE_NAME_SYM + { $$= Condition_information_item::TABLE_NAME; } + | COLUMN_NAME_SYM + { $$= Condition_information_item::COLUMN_NAME; } + | CURSOR_NAME_SYM + { $$= Condition_information_item::CURSOR_NAME; } + | MESSAGE_TEXT_SYM + { $$= Condition_information_item::MESSAGE_TEXT; } + | MYSQL_ERRNO_SYM + { $$= Condition_information_item::MYSQL_ERRNO; } + | RETURNED_SQLSTATE_SYM + { $$= Condition_information_item::RETURNED_SQLSTATE; } + ; + +sp_decl_ident: + IDENT_sys + | sp_decl_ident_keyword + { + $$.str= thd->strmake($1.str, $1.length); + if ($$.str == NULL) + MYSQL_YYABORT; + $$.length= $1.length; + } + ; + +sp_decl_ident_keyword: + keyword_directly_assignable + | keyword_sp_not_data_type + ; + + +sp_decl_idents: + sp_decl_ident + { + /* NOTE: field definition is filled in sp_decl section. */ + + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$1, TRUE)) + my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $1.str)); + spc->add_variable(thd, &$1); + $$= 1; + } + | sp_decl_idents ',' ident + { + /* NOTE: field definition is filled in sp_decl section. */ + + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_variable(&$3, TRUE)) + my_yyabort_error((ER_SP_DUP_VAR, MYF(0), $3.str)); + spc->add_variable(thd, &$3); + $$= $1 + 1; + } + ; + +sp_opt_default: + /* Empty */ { $$ = NULL; } + | DEFAULT expr { $$ = $2; } + | SET_VAR expr { $$ = $2; } + ; + +sp_proc_stmt: + sp_labeled_block + | sp_unlabeled_block + | sp_labeled_control + | sp_unlabeled_control + | sp_labelable_stmt + | labels_declaration_oracle sp_labelable_stmt {} + ; + +sp_labelable_stmt: + sp_proc_stmt_statement + | sp_proc_stmt_continue + | sp_proc_stmt_exit + | sp_proc_stmt_leave + | sp_proc_stmt_iterate + | sp_proc_stmt_goto + | sp_proc_stmt_open + | sp_proc_stmt_fetch + | sp_proc_stmt_close + | sp_proc_stmt_return + | sp_proc_stmt_if + | case_stmt_specification + | NULL_SYM { } + ; + +sp_proc_stmt_compound_ok: + sp_proc_stmt_if + | case_stmt_specification + | sp_unlabeled_block + | sp_unlabeled_control + ; + +sp_proc_stmt_if: + IF_SYM + { + if (Lex->maybe_start_compound_statement(thd)) + MYSQL_YYABORT; + Lex->sphead->new_cont_backpatch(NULL); + } + sp_if END IF_SYM + { Lex->sphead->do_cont_backpatch(); } + ; + +sp_statement: + statement + | ident_directly_assignable + { + // Direct procedure call (without the CALL keyword) + if (Lex->call_statement_start(thd, &$1)) + MYSQL_YYABORT; + } + opt_sp_cparam_list + | ident_directly_assignable '.' ident + { + if (Lex->call_statement_start(thd, &$1, &$3)) + MYSQL_YYABORT; + } + opt_sp_cparam_list + ; + +sp_proc_stmt_statement: + { + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + + lex->sphead->reset_lex(thd); + lex->sphead->m_tmp_query= lip->get_tok_start(); + } + sp_statement + { + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + sp_head *sp= lex->sphead; + + sp->m_flags|= sp_get_flags_for_command(lex); + /* "USE db" doesn't work in a procedure */ + if (lex->sql_command == SQLCOM_CHANGE_DB) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "USE")); + /* + Don't add an instruction for SET statements, since all + instructions for them were already added during processing + of "set" rule. + */ + DBUG_ASSERT(lex->sql_command != SQLCOM_SET_OPTION || + lex->var_list.is_empty()); + if (lex->sql_command != SQLCOM_SET_OPTION) + { + sp_instr_stmt *i=new (thd->mem_root) + sp_instr_stmt(sp->instructions(), lex->spcont, lex); + if (i == NULL) + MYSQL_YYABORT; + + /* + Extract the query statement from the tokenizer. The + end is either lex->ptr, if there was no lookahead, + lex->tok_end otherwise. + */ + if (yychar == YYEMPTY) + i->m_query.length= lip->get_ptr() - sp->m_tmp_query; + else + i->m_query.length= lip->get_tok_start() - sp->m_tmp_query;; + if (!(i->m_query.str= strmake_root(thd->mem_root, + sp->m_tmp_query, + i->m_query.length)) || + sp->add_instr(i)) + MYSQL_YYABORT; + } + if (sp->restore_lex(thd)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_return: + RETURN_SYM + { Lex->sphead->reset_lex(thd); } + expr + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + if (sp->m_handler->add_instr_freturn(thd, sp, lex->spcont, + $3, lex) || + sp->restore_lex(thd)) + MYSQL_YYABORT; + } + | RETURN_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + if (sp->m_handler->add_instr_preturn(thd, sp, lex->spcont)) + MYSQL_YYABORT; + } + ; + +reset_lex_expr: + { Lex->sphead->reset_lex(thd); } expr { $$= $2; } + ; + +sp_proc_stmt_exit: + EXIT_SYM + { + if (Lex->sp_exit_statement(thd, NULL)) + MYSQL_YYABORT; + } + | EXIT_SYM label_ident + { + if (Lex->sp_exit_statement(thd, &$2, NULL)) + MYSQL_YYABORT; + } + | EXIT_SYM WHEN_SYM reset_lex_expr + { + if (Lex->sp_exit_statement(thd, $3) || + Lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + | EXIT_SYM label_ident WHEN_SYM reset_lex_expr + { + if (Lex->sp_exit_statement(thd, &$2, $4) || + Lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_continue: + CONTINUE_SYM + { + if (Lex->sp_continue_statement(thd, NULL)) + MYSQL_YYABORT; + } + | CONTINUE_SYM label_ident + { + if (Lex->sp_continue_statement(thd, &$2, NULL)) + MYSQL_YYABORT; + } + | CONTINUE_SYM WHEN_SYM reset_lex_expr + { + if (Lex->sp_continue_statement(thd, $3) || + Lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + | CONTINUE_SYM label_ident WHEN_SYM reset_lex_expr + { + if (Lex->sp_continue_statement(thd, &$2, $4) || + Lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + ; + + +sp_proc_stmt_leave: + LEAVE_SYM label_ident + { + if (Lex->sp_leave_statement(thd, &$2)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_iterate: + ITERATE_SYM label_ident + { + if (Lex->sp_iterate_statement(thd, &$2)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_goto: + GOTO_SYM label_ident + { + if (Lex->sp_goto_statement(thd, &$2)) + MYSQL_YYABORT; + } + ; + + +assignment_source_lex: + { + DBUG_ASSERT(Lex->sphead); + if (!($$= new (thd->mem_root) sp_assignment_lex(thd, thd->lex))) + MYSQL_YYABORT; + } + ; + +assignment_source_expr: + assignment_source_lex + { + DBUG_ASSERT(thd->free_list == NULL); + Lex->sphead->reset_lex(thd, $1); + } + expr + { + DBUG_ASSERT($1 == thd->lex); + $$= $1; + $$->sp_lex_in_use= true; + $$->set_item_and_free_list($3, thd->free_list); + thd->free_list= NULL; + if ($$->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + ; + +for_loop_bound_expr: + assignment_source_lex + { + Lex->sphead->reset_lex(thd, $1); + } + expr + { + DBUG_ASSERT($1 == thd->lex); + $$= $1; + $$->sp_lex_in_use= true; + $$->set_item_and_free_list($3, NULL); + if ($$->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + ; + +cursor_actual_parameters: + assignment_source_expr + { + if (!($$= new (thd->mem_root) List<sp_assignment_lex>)) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | cursor_actual_parameters ',' assignment_source_expr + { + $$= $1; + $$->push_back($3, thd->mem_root); + } + ; + +opt_parenthesized_cursor_actual_parameters: + /* Empty */ { $$= NULL; } + | '(' cursor_actual_parameters ')' { $$= $2; } + ; + +sp_proc_stmt_open: + OPEN_SYM ident opt_parenthesized_cursor_actual_parameters + { + if (Lex->sp_open_cursor(thd, &$2, $3)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_fetch_head: + FETCH_SYM ident INTO + { + if (Lex->sp_add_cfetch(thd, &$2)) + MYSQL_YYABORT; + } + | FETCH_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, &$3)) + MYSQL_YYABORT; + } + | FETCH_SYM NEXT_SYM FROM ident INTO + { + if (Lex->sp_add_cfetch(thd, &$4)) + MYSQL_YYABORT; + } + ; + +sp_proc_stmt_fetch: + sp_proc_stmt_fetch_head sp_fetch_list { } + ; + +sp_proc_stmt_close: + CLOSE_SYM ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint offset; + sp_instr_cclose *i; + + if (! lex->spcont->find_cursor(&$2, &offset, false)) + my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $2.str)); + i= new (thd->mem_root) + sp_instr_cclose(sp->instructions(), lex->spcont, offset); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + ; + +sp_fetch_list: + ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *spc= lex->spcont; + sp_variable *spv; + + if (!spc || !(spv = spc->find_variable(&$1, false))) + my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str)); + + /* An SP local variable */ + sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); + i->add_to_varlist(spv); + } + | sp_fetch_list ',' ident + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *spc= lex->spcont; + sp_variable *spv; + + if (!spc || !(spv = spc->find_variable(&$3, false))) + my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $3.str)); + + /* An SP local variable */ + sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction(); + i->add_to_varlist(spv); + } + ; + +sp_if: + { Lex->sphead->reset_lex(thd); } + expr THEN_SYM + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump_if_not *i= new (thd->mem_root) + sp_instr_jump_if_not(ip, ctx, $2, lex); + if (i == NULL || + sp->push_backpatch(thd, i, ctx->push_label(thd, &empty_clex_str, 0)) || + sp->add_cont_backpatch(i) || + sp->add_instr(i)) + MYSQL_YYABORT; + if (sp->restore_lex(thd)) + MYSQL_YYABORT; + } + sp_proc_stmts1_implicit_block + { + sp_head *sp= Lex->sphead; + sp_pcontext *ctx= Lex->spcont; + uint ip= sp->instructions(); + sp_instr_jump *i= new (thd->mem_root) sp_instr_jump(ip, ctx); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + sp->backpatch(ctx->pop_label()); + sp->push_backpatch(thd, i, ctx->push_label(thd, &empty_clex_str, 0)); + } + sp_elseifs + { + LEX *lex= Lex; + + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; + +sp_elseifs: + /* Empty */ + | ELSIF_SYM sp_if + | ELSE sp_proc_stmts1_implicit_block + ; + +case_stmt_specification: + CASE_SYM + { + if (Lex->maybe_start_compound_statement(thd)) + MYSQL_YYABORT; + + /** + An example of the CASE statement in use is + <pre> + CREATE PROCEDURE proc_19194_simple(i int) + BEGIN + DECLARE str CHAR(10); + + CASE i + WHEN 1 THEN SET str="1"; + WHEN 2 THEN SET str="2"; + WHEN 3 THEN SET str="3"; + ELSE SET str="unknown"; + END CASE; + + SELECT str; + END + </pre> + The actions are used to generate the following code: + <pre> + SHOW PROCEDURE CODE proc_19194_simple; + Pos Instruction + 0 set str@1 NULL + 1 set_case_expr (12) 0 i@0 + 2 jump_if_not 5(12) (case_expr@0 = 1) + 3 set str@1 _latin1'1' + 4 jump 12 + 5 jump_if_not 8(12) (case_expr@0 = 2) + 6 set str@1 _latin1'2' + 7 jump 12 + 8 jump_if_not 11(12) (case_expr@0 = 3) + 9 set str@1 _latin1'3' + 10 jump 12 + 11 set str@1 _latin1'unknown' + 12 stmt 0 "SELECT str" + </pre> + */ + + Lex->sphead->new_cont_backpatch(NULL); + + /* + BACKPATCH: Creating target label for the jump to after END CASE + (instruction 12 in the example) + */ + Lex->spcont->push_label(thd, &empty_clex_str, Lex->sphead->instructions()); + } + case_stmt_body + else_clause_opt + END + CASE_SYM + { + /* + BACKPATCH: Resolving forward jump from + "case_stmt_action_then" to after END CASE + (jump from instruction 4 to 12, 7 to 12 ... in the example) + */ + Lex->sphead->backpatch(Lex->spcont->pop_label()); + + if ($3) + Lex->spcont->pop_case_expr_id(); + + Lex->sphead->do_cont_backpatch(); + } + ; + +case_stmt_body: + { Lex->sphead->reset_lex(thd); /* For expr $2 */ } + expr + { + if (Lex->case_stmt_action_expr($2)) + MYSQL_YYABORT; + + if (Lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + simple_when_clause_list + { $$= 1; } + | searched_when_clause_list + { $$= 0; } + ; + +simple_when_clause_list: + simple_when_clause + | simple_when_clause_list simple_when_clause + ; + +searched_when_clause_list: + searched_when_clause + | searched_when_clause_list searched_when_clause + ; + +simple_when_clause: + WHEN_SYM + { + Lex->sphead->reset_lex(thd); /* For expr $3 */ + } + expr + { + /* Simple case: <caseval> = <whenval> */ + + LEX *lex= Lex; + if (lex->case_stmt_action_when($3, true)) + MYSQL_YYABORT; + /* For expr $3 */ + if (lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + THEN_SYM + sp_proc_stmts1_implicit_block + { + if (Lex->case_stmt_action_then()) + MYSQL_YYABORT; + } + ; + +searched_when_clause: + WHEN_SYM + { + Lex->sphead->reset_lex(thd); /* For expr $3 */ + } + expr + { + LEX *lex= Lex; + if (lex->case_stmt_action_when($3, false)) + MYSQL_YYABORT; + /* For expr $3 */ + if (lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + THEN_SYM + sp_proc_stmts1_implicit_block + { + if (Lex->case_stmt_action_then()) + MYSQL_YYABORT; + } + ; + +else_clause_opt: + /* empty */ + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + uint ip= sp->instructions(); + sp_instr_error *i= new (thd->mem_root) + sp_instr_error(ip, lex->spcont, ER_SP_CASE_NOT_FOUND); + if (i == NULL || + sp->add_instr(i)) + MYSQL_YYABORT; + } + | ELSE sp_proc_stmts1_implicit_block + ; + +sp_opt_label: + /* Empty */ { $$= null_clex_str; } + | label_ident { $$= $1; } + ; + +sp_block_label: + labels_declaration_oracle + { + if (Lex->spcont->block_label_declare(&$1)) + MYSQL_YYABORT; + $$= $1; + } + ; + +sp_labeled_block: + sp_block_label + BEGIN_SYM + { + Lex->sp_block_init(thd, &$1); + if (Lex->sp_block_with_exceptions_finalize_declarations(thd)) + MYSQL_YYABORT; + } + sp_block_statements_and_exceptions + END + sp_opt_label + { + if (Lex->sp_block_finalize(thd, Lex_spblock($4), &$6)) + MYSQL_YYABORT; + } + | sp_block_label + DECLARE_SYM + { + Lex->sp_block_init(thd, &$1); + } + sp_decl_body_list + { + if (Lex->sp_block_with_exceptions_finalize_declarations(thd)) + MYSQL_YYABORT; + } + BEGIN_SYM + sp_block_statements_and_exceptions + END + sp_opt_label + { + $4.hndlrs+= $7.hndlrs; + if (Lex->sp_block_finalize(thd, $4, &$9)) + MYSQL_YYABORT; + } + ; + +opt_not_atomic: + /* Empty */ + | not ATOMIC_SYM /* TODO: BEGIN ATOMIC (not -> opt_not) */ + ; + +sp_unlabeled_block: + BEGIN_SYM opt_not_atomic + { + if (Lex->maybe_start_compound_statement(thd)) + MYSQL_YYABORT; + Lex->sp_block_init(thd); + if (Lex->sp_block_with_exceptions_finalize_declarations(thd)) + MYSQL_YYABORT; + } + sp_block_statements_and_exceptions + END + { + if (Lex->sp_block_finalize(thd, Lex_spblock($4))) + MYSQL_YYABORT; + } + | DECLARE_SYM + { + if (Lex->maybe_start_compound_statement(thd)) + MYSQL_YYABORT; + Lex->sp_block_init(thd); + } + sp_decl_body_list + { + if (Lex->sp_block_with_exceptions_finalize_declarations(thd)) + MYSQL_YYABORT; + } + BEGIN_SYM + sp_block_statements_and_exceptions + END + { + $3.hndlrs+= $6.hndlrs; + if (Lex->sp_block_finalize(thd, $3)) + MYSQL_YYABORT; + } + ; + +sp_instr_addr: + { $$= Lex->sphead->instructions(); } + ; + +sp_body: + { + Lex->sp_block_init(thd); + } + opt_sp_decl_body_list + { + if (Lex->sp_block_with_exceptions_finalize_declarations(thd)) + MYSQL_YYABORT; + } + BEGIN_SYM + sp_block_statements_and_exceptions + { + $2.hndlrs+= $5.hndlrs; + if (Lex->sp_block_finalize(thd, $2)) + MYSQL_YYABORT; + } + END + ; + +sp_block_statements_and_exceptions: + sp_instr_addr + sp_proc_stmts + { + if (Lex->sp_block_with_exceptions_finalize_executable_section(thd, + $1)) + MYSQL_YYABORT; + } + opt_exception_clause + { + if (Lex->sp_block_with_exceptions_finalize_exceptions(thd, $1, $4)) + MYSQL_YYABORT; + $$.init($4); + } + ; + +opt_exception_clause: + /* Empty */ { $$= 0; } + | EXCEPTION_SYM exception_handlers { $$= $2; } + ; + +exception_handlers: + exception_handler { $$= 1; } + | exception_handlers exception_handler { $$= $1 + 1; } + ; + +exception_handler: + WHEN_SYM + { + if (Lex->sp_handler_declaration_init(thd, sp_handler::EXIT)) + MYSQL_YYABORT; + } + sp_hcond_list + THEN_SYM + sp_proc_stmts1_implicit_block + { + if (Lex->sp_handler_declaration_finalize(thd, sp_handler::EXIT)) + MYSQL_YYABORT; + } + ; + +/* This adds one shift/reduce conflict */ +opt_sp_for_loop_direction: + /* Empty */ { $$= 1; } + | REVERSE_SYM { $$= -1; } + ; + +sp_for_loop_index_and_bounds: + ident_directly_assignable sp_for_loop_bounds + { + if (Lex->sp_for_loop_declarations(thd, &$$, &$1, $2)) + MYSQL_YYABORT; + } + ; + +sp_for_loop_bounds: + IN_SYM opt_sp_for_loop_direction for_loop_bound_expr + DOT_DOT_SYM for_loop_bound_expr + { + $$.m_direction= $2; + $$.m_index= $3; + $$.m_upper_bound= $5; + $$.m_implicit_cursor= false; + } + | IN_SYM opt_sp_for_loop_direction for_loop_bound_expr + { + $$.m_direction= $2; + $$.m_index= $3; + $$.m_upper_bound= NULL; + $$.m_implicit_cursor= false; + } + | IN_SYM opt_sp_for_loop_direction '(' sp_cursor_stmt ')' + { + Item *item; + DBUG_ASSERT(Lex->sphead); + LEX_CSTRING name= {STRING_WITH_LEN("[implicit_cursor]") }; + if (Lex->sp_declare_cursor(thd, &name, $4, NULL, true)) + MYSQL_YYABORT; + if (!($$.m_index= new (thd->mem_root) sp_assignment_lex(thd, thd->lex))) + MYSQL_YYABORT; + $$.m_index->sp_lex_in_use= true; + Lex->sphead->reset_lex(thd, $$.m_index); + if (!(item= new (thd->mem_root) Item_field(thd, + Lex->current_context(), + NullS, NullS, &name))) + MYSQL_YYABORT; + $$.m_index->set_item_and_free_list(item, NULL); + if (Lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + $$.m_direction= 1; + $$.m_upper_bound= NULL; + $$.m_implicit_cursor= true; + } + ; + +loop_body: + sp_proc_stmts1 END LOOP_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i= new (thd->mem_root) + sp_instr_jump(ip, lex->spcont, lab->ip); + if (i == NULL || + lex->sphead->add_instr(i)) + MYSQL_YYABORT; + } + ; + +while_body: + expr LOOP_SYM + { + LEX *lex= Lex; + if (lex->sp_while_loop_expression(thd, $1)) + MYSQL_YYABORT; + if (lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + sp_proc_stmts1 END LOOP_SYM + { + if (Lex->sp_while_loop_finalize(thd)) + MYSQL_YYABORT; + } + ; + +repeat_body: + sp_proc_stmts1 UNTIL_SYM + { Lex->sphead->reset_lex(thd); } + expr END REPEAT_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump_if_not *i= new (thd->mem_root) + sp_instr_jump_if_not(ip, lex->spcont, $4, lab->ip, lex); + if (i == NULL || + lex->sphead->add_instr(i)) + MYSQL_YYABORT; + if (lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + /* We can shortcut the cont_backpatch here */ + i->m_cont_dest= ip+1; + } + ; + +pop_sp_loop_label: + sp_opt_label + { + if (Lex->sp_pop_loop_label(thd, &$1)) + MYSQL_YYABORT; + } + ; + +sp_labeled_control: + labels_declaration_oracle LOOP_SYM + { + if (Lex->sp_push_loop_label(thd, &$1)) + MYSQL_YYABORT; + } + loop_body pop_sp_loop_label + { } + | labels_declaration_oracle WHILE_SYM + { + if (Lex->sp_push_loop_label(thd, &$1)) + MYSQL_YYABORT; + Lex->sphead->reset_lex(thd); + } + while_body pop_sp_loop_label + { } + | labels_declaration_oracle FOR_SYM + { + // See "The FOR LOOP statement" comments in sql_lex.cc + Lex->sp_block_init(thd); // The outer DECLARE..BEGIN..END block + } + sp_for_loop_index_and_bounds + { + if (Lex->sp_push_loop_label(thd, &$1)) // The inner WHILE block + MYSQL_YYABORT; + if (Lex->sp_for_loop_condition_test(thd, $4)) + MYSQL_YYABORT; + } + LOOP_SYM + sp_proc_stmts1 + END LOOP_SYM + { + if (Lex->sp_for_loop_finalize(thd, $4)) + MYSQL_YYABORT; + } + pop_sp_loop_label // The inner WHILE block + { + Lex_spblock tmp; + tmp.curs= MY_TEST($4.m_implicit_cursor); + if (Lex->sp_block_finalize(thd, tmp)) // The outer DECLARE..BEGIN..END + MYSQL_YYABORT; + } + | labels_declaration_oracle REPEAT_SYM + { + if (Lex->sp_push_loop_label(thd, &$1)) + MYSQL_YYABORT; + } + repeat_body pop_sp_loop_label + { } + ; + +sp_unlabeled_control: + LOOP_SYM + { + if (Lex->sp_push_loop_empty_label(thd)) + MYSQL_YYABORT; + } + loop_body + { + Lex->sp_pop_loop_empty_label(thd); + } + | WHILE_SYM + { + if (Lex->sp_push_loop_empty_label(thd)) + MYSQL_YYABORT; + Lex->sphead->reset_lex(thd); + } + while_body + { + Lex->sp_pop_loop_empty_label(thd); + } + | FOR_SYM + { + // See "The FOR LOOP statement" comments in sql_lex.cc + if (Lex->maybe_start_compound_statement(thd)) + MYSQL_YYABORT; + Lex->sp_block_init(thd); // The outer DECLARE..BEGIN..END block + } + sp_for_loop_index_and_bounds + { + if (Lex->sp_push_loop_empty_label(thd)) // The inner WHILE block + MYSQL_YYABORT; + if (Lex->sp_for_loop_condition_test(thd, $3)) + MYSQL_YYABORT; + } + LOOP_SYM + sp_proc_stmts1 + END LOOP_SYM + { + Lex_spblock tmp; + tmp.curs= MY_TEST($3.m_implicit_cursor); + if (Lex->sp_for_loop_finalize(thd, $3)) + MYSQL_YYABORT; + Lex->sp_pop_loop_empty_label(thd); // The inner WHILE block + if (Lex->sp_block_finalize(thd, tmp)) // The outer DECLARE..BEGIN..END + MYSQL_YYABORT; + } + | REPEAT_SYM + { + if (Lex->sp_push_loop_empty_label(thd)) + MYSQL_YYABORT; + } + repeat_body + { + Lex->sp_pop_loop_empty_label(thd); + } + ; + +trg_action_time: + BEFORE_SYM + { Lex->trg_chistics.action_time= TRG_ACTION_BEFORE; } + | AFTER_SYM + { Lex->trg_chistics.action_time= TRG_ACTION_AFTER; } + ; + +trg_event: + INSERT + { Lex->trg_chistics.event= TRG_EVENT_INSERT; } + | UPDATE_SYM + { Lex->trg_chistics.event= TRG_EVENT_UPDATE; } + | DELETE_SYM + { Lex->trg_chistics.event= TRG_EVENT_DELETE; } + ; +/* + This part of the parser contains common code for all TABLESPACE + commands. + CREATE TABLESPACE name ... + ALTER TABLESPACE name CHANGE DATAFILE ... + ALTER TABLESPACE name ADD DATAFILE ... + ALTER TABLESPACE name access_mode + CREATE LOGFILE GROUP_SYM name ... + ALTER LOGFILE GROUP_SYM name ADD UNDOFILE .. + ALTER LOGFILE GROUP_SYM name ADD REDOFILE .. + DROP TABLESPACE name + DROP LOGFILE GROUP_SYM name +*/ +change_tablespace_access: + tablespace_name + ts_access_mode + ; + +change_tablespace_info: + tablespace_name + CHANGE ts_datafile + change_ts_option_list + ; + +tablespace_info: + tablespace_name + ADD ts_datafile + opt_logfile_group_name + tablespace_option_list + ; + +opt_logfile_group_name: + /* empty */ {} + | USE_SYM LOGFILE_SYM GROUP_SYM ident + { + LEX *lex= Lex; + lex->alter_tablespace_info->logfile_group_name= $4.str; + } + ; + +alter_tablespace_info: + tablespace_name + ADD ts_datafile + alter_tablespace_option_list + { + Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_ADD_FILE; + } + | tablespace_name + DROP ts_datafile + alter_tablespace_option_list + { + Lex->alter_tablespace_info->ts_alter_tablespace_type= ALTER_TABLESPACE_DROP_FILE; + } + ; + +logfile_group_info: + logfile_group_name + add_log_file + logfile_group_option_list + ; + +alter_logfile_group_info: + logfile_group_name + add_log_file + alter_logfile_group_option_list + ; + +add_log_file: + ADD lg_undofile + | ADD lg_redofile + ; + +change_ts_option_list: + /* empty */ {} + change_ts_options + ; + +change_ts_options: + change_ts_option + | change_ts_options change_ts_option + | change_ts_options ',' change_ts_option + ; + +change_ts_option: + opt_ts_initial_size + | opt_ts_autoextend_size + | opt_ts_max_size + ; + +tablespace_option_list: + tablespace_options + ; + +tablespace_options: + tablespace_option + | tablespace_options tablespace_option + | tablespace_options ',' tablespace_option + ; + +tablespace_option: + opt_ts_initial_size + | opt_ts_autoextend_size + | opt_ts_max_size + | opt_ts_extent_size + | opt_ts_nodegroup + | opt_ts_engine + | ts_wait + | opt_ts_comment + ; + +alter_tablespace_option_list: + alter_tablespace_options + ; + +alter_tablespace_options: + alter_tablespace_option + | alter_tablespace_options alter_tablespace_option + | alter_tablespace_options ',' alter_tablespace_option + ; + +alter_tablespace_option: + opt_ts_initial_size + | opt_ts_autoextend_size + | opt_ts_max_size + | opt_ts_engine + | ts_wait + ; + +logfile_group_option_list: + logfile_group_options + ; + +logfile_group_options: + logfile_group_option + | logfile_group_options logfile_group_option + | logfile_group_options ',' logfile_group_option + ; + +logfile_group_option: + opt_ts_initial_size + | opt_ts_undo_buffer_size + | opt_ts_redo_buffer_size + | opt_ts_nodegroup + | opt_ts_engine + | ts_wait + | opt_ts_comment + ; + +alter_logfile_group_option_list: + alter_logfile_group_options + ; + +alter_logfile_group_options: + alter_logfile_group_option + | alter_logfile_group_options alter_logfile_group_option + | alter_logfile_group_options ',' alter_logfile_group_option + ; + +alter_logfile_group_option: + opt_ts_initial_size + | opt_ts_engine + | ts_wait + ; + + +ts_datafile: + DATAFILE_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->alter_tablespace_info->data_file_name= $2.str; + } + ; + +lg_undofile: + UNDOFILE_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->alter_tablespace_info->undo_file_name= $2.str; + } + ; + +lg_redofile: + REDOFILE_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->alter_tablespace_info->redo_file_name= $2.str; + } + ; + +tablespace_name: + ident + { + LEX *lex= Lex; + lex->alter_tablespace_info= (new (thd->mem_root) + st_alter_tablespace()); + if (lex->alter_tablespace_info == NULL) + MYSQL_YYABORT; + lex->alter_tablespace_info->tablespace_name= $1.str; + lex->sql_command= SQLCOM_ALTER_TABLESPACE; + } + ; + +logfile_group_name: + ident + { + LEX *lex= Lex; + lex->alter_tablespace_info= (new (thd->mem_root) + st_alter_tablespace()); + if (lex->alter_tablespace_info == NULL) + MYSQL_YYABORT; + lex->alter_tablespace_info->logfile_group_name= $1.str; + lex->sql_command= SQLCOM_ALTER_TABLESPACE; + } + ; + +ts_access_mode: + READ_ONLY_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_access_mode= TS_READ_ONLY; + } + | READ_WRITE_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_access_mode= TS_READ_WRITE; + } + | NOT_SYM ACCESSIBLE_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_access_mode= TS_NOT_ACCESSIBLE; + } + ; + +opt_ts_initial_size: + INITIAL_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->initial_size= $3; + } + ; + +opt_ts_autoextend_size: + AUTOEXTEND_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->autoextend_size= $3; + } + ; + +opt_ts_max_size: + MAX_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->max_size= $3; + } + ; + +opt_ts_extent_size: + EXTENT_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->extent_size= $3; + } + ; + +opt_ts_undo_buffer_size: + UNDO_BUFFER_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->undo_buffer_size= $3; + } + ; + +opt_ts_redo_buffer_size: + REDO_BUFFER_SIZE_SYM opt_equal size_number + { + LEX *lex= Lex; + lex->alter_tablespace_info->redo_buffer_size= $3; + } + ; + +opt_ts_nodegroup: + NODEGROUP_SYM opt_equal real_ulong_num + { + LEX *lex= Lex; + if (lex->alter_tablespace_info->nodegroup_id != UNDEF_NODEGROUP) + my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NODEGROUP")); + lex->alter_tablespace_info->nodegroup_id= $3; + } + ; + +opt_ts_comment: + COMMENT_SYM opt_equal TEXT_STRING_sys + { + LEX *lex= Lex; + if (lex->alter_tablespace_info->ts_comment != NULL) + my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"COMMENT")); + lex->alter_tablespace_info->ts_comment= $3.str; + } + ; + +opt_ts_engine: + opt_storage ENGINE_SYM opt_equal storage_engines + { + LEX *lex= Lex; + if (lex->alter_tablespace_info->storage_engine != NULL) + my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE, MYF(0), + "STORAGE ENGINE")); + lex->alter_tablespace_info->storage_engine= $4; + } + ; + +opt_ts_wait: + /* empty */ + | ts_wait + ; + +ts_wait: + WAIT_SYM + { + LEX *lex= Lex; + lex->alter_tablespace_info->wait_until_completed= TRUE; + } + | NO_WAIT_SYM + { + LEX *lex= Lex; + if (!(lex->alter_tablespace_info->wait_until_completed)) + my_yyabort_error((ER_FILEGROUP_OPTION_ONLY_ONCE,MYF(0),"NO_WAIT")); + lex->alter_tablespace_info->wait_until_completed= FALSE; + } + ; + +size_number: + real_ulonglong_num { $$= $1;} + | IDENT_sys + { + ulonglong number; + uint text_shift_number= 0; + longlong prefix_number; + const char *start_ptr= $1.str; + uint str_len= $1.length; + const char *end_ptr= start_ptr + str_len; + int error; + prefix_number= my_strtoll10(start_ptr, (char**) &end_ptr, &error); + if ((start_ptr + str_len - 1) == end_ptr) + { + switch (end_ptr[0]) + { + case 'g': + case 'G': + text_shift_number+=10; + /* fall through */ + case 'm': + case 'M': + text_shift_number+=10; + /* fall through */ + case 'k': + case 'K': + text_shift_number+=10; + break; + default: + my_yyabort_error((ER_WRONG_SIZE_NUMBER, MYF(0))); + } + if (prefix_number >> 31) + my_yyabort_error((ER_SIZE_OVERFLOW_ERROR, MYF(0))); + number= prefix_number << text_shift_number; + } + else + my_yyabort_error((ER_WRONG_SIZE_NUMBER, MYF(0))); + $$= number; + } + ; + +/* + End tablespace part +*/ + +create_body: + '(' create_field_list ')' + { Lex->create_info.option_list= NULL; } + opt_create_table_options opt_create_partitioning opt_create_select {} + | opt_create_table_options opt_create_partitioning opt_create_select {} + /* + the following rule is redundant, but there's a shift/reduce + conflict that prevents the rule above from parsing a syntax like + CREATE TABLE t1 (SELECT 1); + */ + | '(' create_select_query_specification ')' + | '(' create_select_query_specification ')' + { Select->set_braces(1);} union_list {} + | '(' create_select_query_specification ')' + { Select->set_braces(1);} union_order_or_limit {} + | create_like + { + + Lex->create_info.add(DDL_options_st::OPT_LIKE); + TABLE_LIST *src_table= Lex->select_lex.add_table_to_list(thd, + $1, NULL, 0, TL_READ, MDL_SHARED_READ); + if (! src_table) + MYSQL_YYABORT; + /* CREATE TABLE ... LIKE is not allowed for views. */ + src_table->required_type= TABLE_TYPE_NORMAL; + } + ; + +create_like: + LIKE table_ident { $$= $2; } + | '(' LIKE table_ident ')' { $$= $3; } + ; + +opt_create_select: + /* empty */ {} + | opt_duplicate opt_as create_select_query_expression + ; + +create_select_query_expression: + opt_with_clause SELECT_SYM create_select_part2 opt_table_expression + create_select_part4 + { + Select->set_braces(0); + Select->set_with_clause($1); + } + union_clause + | opt_with_clause SELECT_SYM create_select_part2 + create_select_part3_union_not_ready create_select_part4 + { + Select->set_with_clause($1); + } + | '(' create_select_query_specification ')' + | '(' create_select_query_specification ')' + { Select->set_braces(1);} union_list {} + | '(' create_select_query_specification ')' + { Select->set_braces(1);} union_order_or_limit {} + ; + +opt_create_partitioning: + opt_partitioning + { + /* + Remove all tables used in PARTITION clause from the global table + list. Partitioning with subqueries is not allowed anyway. + */ + TABLE_LIST *last_non_sel_table= Lex->create_last_non_select_table; + last_non_sel_table->next_global= 0; + Lex->query_tables_last= &last_non_sel_table->next_global; + } + ; + +/* + This part of the parser is about handling of the partition information. + + It's first version was written by Mikael Ronstrm with lots of answers to + questions provided by Antony Curtis. + + The partition grammar can be called from three places. + 1) CREATE TABLE ... PARTITION .. + 2) ALTER TABLE table_name PARTITION ... + 3) PARTITION ... + + The first place is called when a new table is created from a MySQL client. + The second place is called when a table is altered with the ALTER TABLE + command from a MySQL client. + The third place is called when opening an frm file and finding partition + info in the .frm file. It is necessary to avoid allowing PARTITION to be + an allowed entry point for SQL client queries. This is arranged by setting + some state variables before arriving here. + + To be able to handle errors we will only set error code in this code + and handle the error condition in the function calling the parser. This + is necessary to ensure we can also handle errors when calling the parser + from the openfrm function. +*/ +opt_partitioning: + /* empty */ {} + | partitioning + ; + +partitioning: + PARTITION_SYM have_partitioning + { + LEX *lex= Lex; + lex->part_info= new (thd->mem_root) partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + MYSQL_YYABORT; + } + if (lex->sql_command == SQLCOM_ALTER_TABLE) + { + lex->alter_info.flags|= Alter_info::ALTER_PARTITION; + } + } + partition + ; + +have_partitioning: + /* empty */ + { +#ifdef WITH_PARTITION_STORAGE_ENGINE + LEX_CSTRING partition_name={STRING_WITH_LEN("partition")}; + if (!plugin_is_ready(&partition_name, MYSQL_STORAGE_ENGINE_PLUGIN)) + my_yyabort_error((ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--skip-partition")); +#else + my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), "partitioning", + "--with-plugin-partition")); +#endif + } + ; + +partition_entry: + PARTITION_SYM + { + LEX *lex= Lex; + if (!lex->part_info) + { + thd->parse_error(ER_PARTITION_ENTRY_ERROR); + MYSQL_YYABORT; + } + /* + We enter here when opening the frm file to translate + partition info string into part_info data structure. + */ + } + partition {} + ; + +partition: + BY + { Lex->safe_to_cache_query= 1; } + part_type_def opt_num_parts opt_sub_part part_defs + ; + +part_type_def: + opt_linear KEY_SYM opt_key_algo '(' part_field_list ')' + { + partition_info *part_info= Lex->part_info; + part_info->list_of_part_fields= TRUE; + part_info->column_list= FALSE; + part_info->part_type= HASH_PARTITION; + } + | opt_linear HASH_SYM + { Lex->part_info->part_type= HASH_PARTITION; } + part_func {} + | RANGE_SYM part_func + { Lex->part_info->part_type= RANGE_PARTITION; } + | RANGE_SYM part_column_list + { Lex->part_info->part_type= RANGE_PARTITION; } + | LIST_SYM part_func + { Lex->part_info->part_type= LIST_PARTITION; } + | LIST_SYM part_column_list + { Lex->part_info->part_type= LIST_PARTITION; } + ; + +opt_linear: + /* empty */ {} + | LINEAR_SYM + { Lex->part_info->linear_hash_ind= TRUE;} + ; + +opt_key_algo: + /* empty */ + { Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_NONE;} + | ALGORITHM_SYM '=' real_ulong_num + { + switch ($3) { + case 1: + Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_51; + break; + case 2: + Lex->part_info->key_algorithm= partition_info::KEY_ALGORITHM_55; + break; + default: + thd->parse_error(); + MYSQL_YYABORT; + } + } + ; + +part_field_list: + /* empty */ {} + | part_field_item_list {} + ; + +part_field_item_list: + part_field_item {} + | part_field_item_list ',' part_field_item {} + ; + +part_field_item: + ident + { + partition_info *part_info= Lex->part_info; + part_info->num_columns++; + if (part_info->part_field_list.push_back($1.str, thd->mem_root)) + { + mem_alloc_error(1); + MYSQL_YYABORT; + } + if (part_info->num_columns > MAX_REF_PARTS) + my_yyabort_error((ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), + "list of partition fields")); + } + ; + +part_column_list: + COLUMNS '(' part_field_list ')' + { + partition_info *part_info= Lex->part_info; + part_info->column_list= TRUE; + part_info->list_of_part_fields= TRUE; + } + ; + + +part_func: + '(' remember_name part_func_expr remember_end ')' + { + partition_info *part_info= Lex->part_info; + if (part_info->set_part_expr(thd, $2 + 1, $3, $4, FALSE)) + { MYSQL_YYABORT; } + part_info->num_columns= 1; + part_info->column_list= FALSE; + } + ; + +sub_part_func: + '(' remember_name part_func_expr remember_end ')' + { + if (Lex->part_info->set_part_expr(thd, $2 + 1, $3, $4, TRUE)) + { MYSQL_YYABORT; } + } + ; + + +opt_num_parts: + /* empty */ {} + | PARTITIONS_SYM real_ulong_num + { + uint num_parts= $2; + partition_info *part_info= Lex->part_info; + if (num_parts == 0) + my_yyabort_error((ER_NO_PARTS_ERROR, MYF(0), "partitions")); + + part_info->num_parts= num_parts; + part_info->use_default_num_partitions= FALSE; + } + ; + +opt_sub_part: + /* empty */ {} + | SUBPARTITION_SYM BY opt_linear HASH_SYM sub_part_func + { Lex->part_info->subpart_type= HASH_PARTITION; } + opt_num_subparts {} + | SUBPARTITION_SYM BY opt_linear KEY_SYM opt_key_algo + '(' sub_part_field_list ')' + { + partition_info *part_info= Lex->part_info; + part_info->subpart_type= HASH_PARTITION; + part_info->list_of_subpart_fields= TRUE; + } + opt_num_subparts {} + ; + +sub_part_field_list: + sub_part_field_item {} + | sub_part_field_list ',' sub_part_field_item {} + ; + +sub_part_field_item: + ident + { + partition_info *part_info= Lex->part_info; + if (part_info->subpart_field_list.push_back($1.str, thd->mem_root)) + { + mem_alloc_error(1); + MYSQL_YYABORT; + } + if (part_info->subpart_field_list.elements > MAX_REF_PARTS) + my_yyabort_error((ER_TOO_MANY_PARTITION_FUNC_FIELDS_ERROR, MYF(0), + "list of subpartition fields")); + } + ; + +part_func_expr: + bit_expr + { + if (!Lex->safe_to_cache_query) + { + thd->parse_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR); + MYSQL_YYABORT; + } + $$=$1; + } + ; + +opt_num_subparts: + /* empty */ {} + | SUBPARTITIONS_SYM real_ulong_num + { + uint num_parts= $2; + LEX *lex= Lex; + if (num_parts == 0) + my_yyabort_error((ER_NO_PARTS_ERROR, MYF(0), "subpartitions")); + lex->part_info->num_subparts= num_parts; + lex->part_info->use_default_num_subpartitions= FALSE; + } + ; + +part_defs: + /* empty */ + { + partition_info *part_info= Lex->part_info; + if (part_info->part_type == RANGE_PARTITION) + my_yyabort_error((ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), + "RANGE")); + if (part_info->part_type == LIST_PARTITION) + my_yyabort_error((ER_PARTITIONS_MUST_BE_DEFINED_ERROR, MYF(0), + "LIST")); + } + | '(' part_def_list ')' + { + partition_info *part_info= Lex->part_info; + uint count_curr_parts= part_info->partitions.elements; + if (part_info->num_parts != 0) + { + if (part_info->num_parts != + count_curr_parts) + { + thd->parse_error(ER_PARTITION_WRONG_NO_PART_ERROR); + MYSQL_YYABORT; + } + } + else if (count_curr_parts > 0) + { + part_info->num_parts= count_curr_parts; + } + part_info->count_curr_subparts= 0; + } + ; + +part_def_list: + part_definition {} + | part_def_list ',' part_definition {} + ; + +part_definition: + PARTITION_SYM + { + partition_info *part_info= Lex->part_info; + partition_element *p_elem= new (thd->mem_root) partition_element(); + + if (!p_elem || + part_info->partitions.push_back(p_elem, thd->mem_root)) + { + mem_alloc_error(sizeof(partition_element)); + MYSQL_YYABORT; + } + p_elem->part_state= PART_NORMAL; + part_info->curr_part_elem= p_elem; + part_info->current_partition= p_elem; + part_info->use_default_partitions= FALSE; + part_info->use_default_num_partitions= FALSE; + } + part_name + opt_part_values + opt_part_options + opt_sub_partition + {} + ; + +part_name: + ident + { + partition_info *part_info= Lex->part_info; + partition_element *p_elem= part_info->curr_part_elem; + if (check_ident_length(&$1)) + MYSQL_YYABORT; + p_elem->partition_name= $1.str; + } + ; + +opt_part_values: + /* empty */ + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type == RANGE_PARTITION) + my_yyabort_error((ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN")); + if (part_info->part_type == LIST_PARTITION) + my_yyabort_error((ER_PARTITION_REQUIRES_VALUES_ERROR, MYF(0), + "LIST", "IN")); + } + else + part_info->part_type= HASH_PARTITION; + } + | VALUES LESS_SYM THAN_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != RANGE_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "RANGE", "LESS THAN")); + } + else + part_info->part_type= RANGE_PARTITION; + } + part_func_max {} + | VALUES IN_SYM + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != LIST_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "LIST", "IN")); + } + else + part_info->part_type= LIST_PARTITION; + } + part_values_in {} + | DEFAULT + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + if (! lex->is_partition_management()) + { + if (part_info->part_type != LIST_PARTITION) + my_yyabort_error((ER_PARTITION_WRONG_VALUES_ERROR, MYF(0), + "LIST", "DEFAULT")); + } + else + part_info->part_type= LIST_PARTITION; + if (part_info->init_column_part(thd)) + { + MYSQL_YYABORT; + } + if (part_info->add_max_value(thd)) + { + MYSQL_YYABORT; + } + } + ; + +part_func_max: + MAXVALUE_SYM + { + partition_info *part_info= Lex->part_info; + + if (part_info->num_columns && + part_info->num_columns != 1U) + { + part_info->print_debug("Kilroy II", NULL); + thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR); + MYSQL_YYABORT; + } + else + part_info->num_columns= 1U; + if (part_info->init_column_part(thd)) + { + MYSQL_YYABORT; + } + if (part_info->add_max_value(thd)) + { + MYSQL_YYABORT; + } + } + | part_value_item {} + ; + +part_values_in: + part_value_item + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + part_info->print_debug("part_values_in: part_value_item", NULL); + + if (part_info->num_columns != 1U) + { + if (!lex->is_partition_management() || + part_info->num_columns == 0 || + part_info->num_columns > MAX_REF_PARTS) + { + part_info->print_debug("Kilroy III", NULL); + thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR); + MYSQL_YYABORT; + } + /* + Reorganize the current large array into a list of small + arrays with one entry in each array. This can happen + in the first partition of an ALTER TABLE statement where + we ADD or REORGANIZE partitions. Also can only happen + for LIST partitions. + */ + if (part_info->reorganize_into_single_field_col_val(thd)) + { + MYSQL_YYABORT; + } + } + } + | '(' part_value_list ')' + { + partition_info *part_info= Lex->part_info; + if (part_info->num_columns < 2U) + { + thd->parse_error(ER_ROW_SINGLE_PARTITION_FIELD_ERROR); + MYSQL_YYABORT; + } + } + ; + +part_value_list: + part_value_item {} + | part_value_list ',' part_value_item {} + ; + +part_value_item: + '(' + { + partition_info *part_info= Lex->part_info; + part_info->print_debug("( part_value_item", NULL); + /* Initialisation code needed for each list of value expressions */ + if (!(part_info->part_type == LIST_PARTITION && + part_info->num_columns == 1U) && + part_info->init_column_part(thd)) + { + MYSQL_YYABORT; + } + } + part_value_item_list {} + ')' + { + partition_info *part_info= Lex->part_info; + part_info->print_debug(") part_value_item", NULL); + if (part_info->num_columns == 0) + part_info->num_columns= part_info->curr_list_object; + if (part_info->num_columns != part_info->curr_list_object) + { + /* + All value items lists must be of equal length, in some cases + which is covered by the above if-statement we don't know yet + how many columns is in the partition so the assignment above + ensures that we only report errors when we know we have an + error. + */ + part_info->print_debug("Kilroy I", NULL); + thd->parse_error(ER_PARTITION_COLUMN_LIST_ERROR); + MYSQL_YYABORT; + } + part_info->curr_list_object= 0; + } + ; + +part_value_item_list: + part_value_expr_item {} + | part_value_item_list ',' part_value_expr_item {} + ; + +part_value_expr_item: + MAXVALUE_SYM + { + partition_info *part_info= Lex->part_info; + if (part_info->part_type == LIST_PARTITION) + { + thd->parse_error(ER_MAXVALUE_IN_VALUES_IN); + MYSQL_YYABORT; + } + if (part_info->add_max_value(thd)) + { + MYSQL_YYABORT; + } + } + | bit_expr + { + LEX *lex= Lex; + partition_info *part_info= lex->part_info; + Item *part_expr= $1; + + if (!lex->safe_to_cache_query) + { + thd->parse_error(ER_WRONG_EXPR_IN_PARTITION_FUNC_ERROR); + MYSQL_YYABORT; + } + if (part_info->add_column_list_value(thd, part_expr)) + { + MYSQL_YYABORT; + } + } + ; + + +opt_sub_partition: + /* empty */ + { + partition_info *part_info= Lex->part_info; + if (part_info->num_subparts != 0 && + !part_info->use_default_subpartitions) + { + /* + We come here when we have defined subpartitions on the first + partition but not on all the subsequent partitions. + */ + thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR); + MYSQL_YYABORT; + } + } + | '(' sub_part_list ')' + { + partition_info *part_info= Lex->part_info; + if (part_info->num_subparts != 0) + { + if (part_info->num_subparts != + part_info->count_curr_subparts) + { + thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR); + MYSQL_YYABORT; + } + } + else if (part_info->count_curr_subparts > 0) + { + if (part_info->partitions.elements > 1) + { + thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR); + MYSQL_YYABORT; + } + part_info->num_subparts= part_info->count_curr_subparts; + } + part_info->count_curr_subparts= 0; + } + ; + +sub_part_list: + sub_part_definition {} + | sub_part_list ',' sub_part_definition {} + ; + +sub_part_definition: + SUBPARTITION_SYM + { + partition_info *part_info= Lex->part_info; + partition_element *curr_part= part_info->current_partition; + partition_element *sub_p_elem= new (thd->mem_root) + partition_element(curr_part); + if (part_info->use_default_subpartitions && + part_info->partitions.elements >= 2) + { + /* + create table t1 (a int) + partition by list (a) subpartition by hash (a) + (partition p0 values in (1), + partition p1 values in (2) subpartition sp11); + causes use to arrive since we are on the second + partition, but still use_default_subpartitions + is set. When we come here we're processing at least + the second partition (the current partition processed + have already been put into the partitions list. + */ + thd->parse_error(ER_PARTITION_WRONG_NO_SUBPART_ERROR); + MYSQL_YYABORT; + } + if (!sub_p_elem || + curr_part->subpartitions.push_back(sub_p_elem, thd->mem_root)) + { + mem_alloc_error(sizeof(partition_element)); + MYSQL_YYABORT; + } + part_info->curr_part_elem= sub_p_elem; + part_info->use_default_subpartitions= FALSE; + part_info->use_default_num_subpartitions= FALSE; + part_info->count_curr_subparts++; + } + sub_name opt_part_options {} + ; + +sub_name: + ident_or_text + { + if (check_ident_length(&$1)) + MYSQL_YYABORT; + Lex->part_info->curr_part_elem->partition_name= $1.str; + } + ; + +opt_part_options: + /* empty */ {} + | opt_part_option_list {} + ; + +opt_part_option_list: + opt_part_option_list opt_part_option {} + | opt_part_option {} + ; + +opt_part_option: + TABLESPACE opt_equal ident_or_text + { Lex->part_info->curr_part_elem->tablespace_name= $3.str; } + | opt_storage ENGINE_SYM opt_equal storage_engines + { + partition_info *part_info= Lex->part_info; + part_info->curr_part_elem->engine_type= $4; + part_info->default_engine_type= $4; + } + | CONNECTION_SYM opt_equal TEXT_STRING_sys + { + LEX *lex= Lex; + lex->part_info->curr_part_elem->connect_string.str= $3.str; + lex->part_info->curr_part_elem->connect_string.length= $3.length; + } + | NODEGROUP_SYM opt_equal real_ulong_num + { Lex->part_info->curr_part_elem->nodegroup_id= (uint16) $3; } + | MAX_ROWS opt_equal real_ulonglong_num + { Lex->part_info->curr_part_elem->part_max_rows= (ha_rows) $3; } + | MIN_ROWS opt_equal real_ulonglong_num + { Lex->part_info->curr_part_elem->part_min_rows= (ha_rows) $3; } + | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { Lex->part_info->curr_part_elem->data_file_name= $4.str; } + | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { Lex->part_info->curr_part_elem->index_file_name= $4.str; } + | COMMENT_SYM opt_equal TEXT_STRING_sys + { Lex->part_info->curr_part_elem->part_comment= $3.str; } + ; + +/* + End of partition parser part +*/ + +create_select_query_specification: + opt_with_clause SELECT_SYM create_select_part2 create_select_part3 + create_select_part4 + { + Select->set_with_clause($1); + } + ; + +create_select_part2: + { + LEX *lex=Lex; + if (lex->sql_command == SQLCOM_INSERT) + lex->sql_command= SQLCOM_INSERT_SELECT; + else if (lex->sql_command == SQLCOM_REPLACE) + lex->sql_command= SQLCOM_REPLACE_SELECT; + /* + The following work only with the local list, the global list + is created correctly in this case + */ + lex->current_select->table_list.save_and_clear(&lex->save_list); + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + ; + +create_select_part3: + opt_table_expression + | create_select_part3_union_not_ready + ; + +create_select_part3_union_not_ready: + table_expression order_or_limit + | order_or_limit + ; + +create_select_part4: + opt_select_lock_type + { + /* + The following work only with the local list, the global list + is created correctly in this case + */ + Lex->current_select->table_list.push_front(&Lex->save_list); + } + ; + +opt_as: + /* empty */ {} + | AS {} + ; + +opt_create_database_options: + /* empty */ {} + | create_database_options {} + ; + +create_database_options: + create_database_option {} + | create_database_options create_database_option {} + ; + +create_database_option: + default_collation {} + | default_charset {} + ; + +opt_if_not_exists_table_element: + /* empty */ + { + Lex->check_exists= FALSE; + } + | IF_SYM not EXISTS + { + Lex->check_exists= TRUE; + } + ; + +opt_if_not_exists: + /* empty */ + { + $$.init(); + } + | IF_SYM not EXISTS + { + $$.set(DDL_options_st::OPT_IF_NOT_EXISTS); + } + ; + +create_or_replace: + CREATE /* empty */ + { + $$.init(); + } + | CREATE OR_SYM REPLACE + { + $$.set(DDL_options_st::OPT_OR_REPLACE); + } + ; + +opt_create_table_options: + /* empty */ + | create_table_options + ; + +create_table_options_space_separated: + create_table_option + | create_table_option create_table_options_space_separated + ; + +create_table_options: + create_table_option + | create_table_option create_table_options + | create_table_option ',' create_table_options + ; + +create_table_option: + ENGINE_SYM opt_equal storage_engines + { + Lex->create_info.db_type= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_ENGINE; + } + | MAX_ROWS opt_equal ulonglong_num + { + Lex->create_info.max_rows= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_MAX_ROWS; + } + | MIN_ROWS opt_equal ulonglong_num + { + Lex->create_info.min_rows= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_MIN_ROWS; + } + | AVG_ROW_LENGTH opt_equal ulong_num + { + Lex->create_info.avg_row_length=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_AVG_ROW_LENGTH; + } + | PASSWORD_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.password=$3.str; + Lex->create_info.used_fields|= HA_CREATE_USED_PASSWORD; + } + | COMMENT_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.comment=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_COMMENT; + } + | AUTO_INC opt_equal ulonglong_num + { + Lex->create_info.auto_increment_value=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_AUTO; + } + | PACK_KEYS_SYM opt_equal ulong_num + { + switch($3) { + case 0: + Lex->create_info.table_options|= HA_OPTION_NO_PACK_KEYS; + break; + case 1: + Lex->create_info.table_options|= HA_OPTION_PACK_KEYS; + break; + default: + thd->parse_error(); + MYSQL_YYABORT; + } + Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; + } + | PACK_KEYS_SYM opt_equal DEFAULT + { + Lex->create_info.table_options&= + ~(HA_OPTION_PACK_KEYS | HA_OPTION_NO_PACK_KEYS); + Lex->create_info.used_fields|= HA_CREATE_USED_PACK_KEYS; + } + | STATS_AUTO_RECALC_SYM opt_equal ulong_num + { + switch($3) { + case 0: + Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_OFF; + break; + case 1: + Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_ON; + break; + default: + thd->parse_error(); + MYSQL_YYABORT; + } + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_AUTO_RECALC; + } + | STATS_AUTO_RECALC_SYM opt_equal DEFAULT + { + Lex->create_info.stats_auto_recalc= HA_STATS_AUTO_RECALC_DEFAULT; + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_AUTO_RECALC; + } + | STATS_PERSISTENT_SYM opt_equal ulong_num + { + switch($3) { + case 0: + Lex->create_info.table_options|= HA_OPTION_NO_STATS_PERSISTENT; + break; + case 1: + Lex->create_info.table_options|= HA_OPTION_STATS_PERSISTENT; + break; + default: + thd->parse_error(); + MYSQL_YYABORT; + } + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_PERSISTENT; + } + | STATS_PERSISTENT_SYM opt_equal DEFAULT + { + Lex->create_info.table_options&= + ~(HA_OPTION_STATS_PERSISTENT | HA_OPTION_NO_STATS_PERSISTENT); + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_PERSISTENT; + } + | STATS_SAMPLE_PAGES_SYM opt_equal ulong_num + { + /* From user point of view STATS_SAMPLE_PAGES can be specified as + STATS_SAMPLE_PAGES=N (where 0<N<=65535, it does not make sense to + scan 0 pages) or STATS_SAMPLE_PAGES=default. Internally we record + =default as 0. See create_frm() in sql/table.cc, we use only two + bytes for stats_sample_pages and this is why we do not allow + larger values. 65535 pages, 16kb each means to sample 1GB, which + is impractical. If at some point this needs to be extended, then + we can store the higher bits from stats_sample_pages in .frm too. */ + if ($3 == 0 || $3 > 0xffff) + { + thd->parse_error(); + MYSQL_YYABORT; + } + Lex->create_info.stats_sample_pages=$3; + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_SAMPLE_PAGES; + } + | STATS_SAMPLE_PAGES_SYM opt_equal DEFAULT + { + Lex->create_info.stats_sample_pages=0; + Lex->create_info.used_fields|= HA_CREATE_USED_STATS_SAMPLE_PAGES; + } + | CHECKSUM_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; + Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; + } + | TABLE_CHECKSUM_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_CHECKSUM : HA_OPTION_NO_CHECKSUM; + Lex->create_info.used_fields|= HA_CREATE_USED_CHECKSUM; + } + | PAGE_CHECKSUM_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_PAGE_CHECKSUM; + Lex->create_info.page_checksum= $3; + } + | DELAY_KEY_WRITE_SYM opt_equal ulong_num + { + Lex->create_info.table_options|= $3 ? HA_OPTION_DELAY_KEY_WRITE : HA_OPTION_NO_DELAY_KEY_WRITE; + Lex->create_info.used_fields|= HA_CREATE_USED_DELAY_KEY_WRITE; + } + | ROW_FORMAT_SYM opt_equal row_types + { + Lex->create_info.row_type= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT; + } + | UNION_SYM opt_equal + { + Lex->select_lex.table_list.save_and_clear(&Lex->save_list); + } + '(' opt_table_list ')' + { + /* + Move the union list to the merge_list and exclude its tables + from the global list. + */ + LEX *lex=Lex; + lex->create_info.merge_list= lex->select_lex.table_list; + lex->select_lex.table_list= lex->save_list; + /* + When excluding union list from the global list we assume that + elements of the former immediately follow elements which represent + table being created/altered and parent tables. + */ + TABLE_LIST *last_non_sel_table= lex->create_last_non_select_table; + DBUG_ASSERT(last_non_sel_table->next_global == + lex->create_info.merge_list.first); + last_non_sel_table->next_global= 0; + Lex->query_tables_last= &last_non_sel_table->next_global; + + lex->create_info.used_fields|= HA_CREATE_USED_UNION; + } + | default_charset + | default_collation + | INSERT_METHOD opt_equal merge_insert_types + { + Lex->create_info.merge_insert_method= $3; + Lex->create_info.used_fields|= HA_CREATE_USED_INSERT_METHOD; + } + | DATA_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.data_file_name= $4.str; + Lex->create_info.used_fields|= HA_CREATE_USED_DATADIR; + } + | INDEX_SYM DIRECTORY_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.index_file_name= $4.str; + Lex->create_info.used_fields|= HA_CREATE_USED_INDEXDIR; + } + | TABLESPACE ident + {Lex->create_info.tablespace= $2.str;} + | STORAGE_SYM DISK_SYM + {Lex->create_info.storage_media= HA_SM_DISK;} + | STORAGE_SYM MEMORY_SYM + {Lex->create_info.storage_media= HA_SM_MEMORY;} + | CONNECTION_SYM opt_equal TEXT_STRING_sys + { + Lex->create_info.connect_string.str= $3.str; + Lex->create_info.connect_string.length= $3.length; + Lex->create_info.used_fields|= HA_CREATE_USED_CONNECTION; + } + | KEY_BLOCK_SIZE opt_equal ulong_num + { + Lex->create_info.used_fields|= HA_CREATE_USED_KEY_BLOCK_SIZE; + Lex->create_info.key_block_size= $3; + } + | TRANSACTIONAL_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL; + Lex->create_info.transactional= $3; + } + | IDENT_sys equal TEXT_STRING_sys + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, true, &Lex->create_info.option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ident + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, false, &Lex->create_info.option_list, + &Lex->option_list_last); + } + | IDENT_sys equal real_ulonglong_num + { + new (thd->mem_root) + engine_option_value($1, $3, &Lex->create_info.option_list, + &Lex->option_list_last, thd->mem_root); + } + | IDENT_sys equal DEFAULT + { + new (thd->mem_root) + engine_option_value($1, &Lex->create_info.option_list, + &Lex->option_list_last); + } + | SEQUENCE_SYM opt_equal choice + { + Lex->create_info.used_fields|= HA_CREATE_USED_SEQUENCE; + Lex->create_info.sequence= $3; + } + ; + +default_charset: + opt_default charset opt_equal charset_name_or_default + { + if (Lex->create_info.add_table_option_default_charset($4)) + MYSQL_YYABORT; + } + ; + +default_collation: + opt_default COLLATE_SYM opt_equal collation_name_or_default + { + HA_CREATE_INFO *cinfo= &Lex->create_info; + if ((cinfo->used_fields & HA_CREATE_USED_DEFAULT_CHARSET) && + cinfo->default_table_charset && $4 && + !($4= merge_charset_and_collation(cinfo->default_table_charset, + $4))) + { + MYSQL_YYABORT; + } + + Lex->create_info.default_table_charset= $4; + Lex->create_info.used_fields|= HA_CREATE_USED_DEFAULT_CHARSET; + } + ; + +storage_engines: + ident_or_text + { + plugin_ref plugin= ha_resolve_by_name(thd, &$1, + thd->lex->create_info.tmp_table()); + + if (plugin) + $$= plugin_hton(plugin); + else + { + if (thd->variables.sql_mode & MODE_NO_ENGINE_SUBSTITUTION) + my_yyabort_error((ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str)); + $$= 0; + push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, + ER_UNKNOWN_STORAGE_ENGINE, + ER_THD(thd, ER_UNKNOWN_STORAGE_ENGINE), + $1.str); + } + } + ; + +known_storage_engines: + ident_or_text + { + plugin_ref plugin; + if ((plugin= ha_resolve_by_name(thd, &$1, false))) + $$= plugin_hton(plugin); + else + my_yyabort_error((ER_UNKNOWN_STORAGE_ENGINE, MYF(0), $1.str)); + } + ; + +row_types: + DEFAULT { $$= ROW_TYPE_DEFAULT; } + | FIXED_SYM { $$= ROW_TYPE_FIXED; } + | DYNAMIC_SYM { $$= ROW_TYPE_DYNAMIC; } + | COMPRESSED_SYM { $$= ROW_TYPE_COMPRESSED; } + | REDUNDANT_SYM { $$= ROW_TYPE_REDUNDANT; } + | COMPACT_SYM { $$= ROW_TYPE_COMPACT; } + | PAGE_SYM { $$= ROW_TYPE_PAGE; } + ; + +merge_insert_types: + NO_SYM { $$= MERGE_INSERT_DISABLED; } + | FIRST_SYM { $$= MERGE_INSERT_TO_FIRST; } + | LAST_SYM { $$= MERGE_INSERT_TO_LAST; } + ; + +udf_type: + STRING_SYM {$$ = (int) STRING_RESULT; } + | REAL {$$ = (int) REAL_RESULT; } + | DECIMAL_SYM {$$ = (int) DECIMAL_RESULT; } + | INT_SYM {$$ = (int) INT_RESULT; } + ; + + +create_field_list: + field_list + { + Lex->create_last_non_select_table= Lex->last_table(); + } + ; + +field_list: + field_list_item + | field_list ',' field_list_item + ; + +field_list_item: + column_def { } + | key_def + | constraint_def + ; + +column_def: + field_spec + { $$= $1; } + | field_spec references + { $$= $1; } + ; + +key_def: + key_or_index opt_if_not_exists opt_ident opt_USING_key_algorithm + { + Lex->option_list= NULL; + if (Lex->add_key(Key::MULTIPLE, &$3, $4, $2)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options { } + | key_or_index opt_if_not_exists ident TYPE_SYM btree_or_rtree + { + Lex->option_list= NULL; + if (Lex->add_key(Key::MULTIPLE, &$3, $5, $2)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options { } + | fulltext opt_key_or_index opt_if_not_exists opt_ident + { + Lex->option_list= NULL; + if (Lex->add_key($1, &$4, HA_KEY_ALG_UNDEF, $3)) + MYSQL_YYABORT; + } + '(' key_list ')' fulltext_key_options { } + | spatial opt_key_or_index opt_if_not_exists opt_ident + { + Lex->option_list= NULL; + if (Lex->add_key($1, &$4, HA_KEY_ALG_UNDEF, $3)) + MYSQL_YYABORT; + } + '(' key_list ')' spatial_key_options { } + | opt_constraint constraint_key_type + opt_if_not_exists opt_ident + opt_USING_key_algorithm + { + Lex->option_list= NULL; + if (Lex->add_key($2, $4.str ? &$4 : &$1, $5, $3)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options { } + | opt_constraint constraint_key_type opt_if_not_exists ident + TYPE_SYM btree_or_rtree + { + Lex->option_list= NULL; + if (Lex->add_key($2, $4.str ? &$4 : &$1, $6, $3)) + MYSQL_YYABORT; + } + '(' key_list ')' normal_key_options { } + | opt_constraint FOREIGN KEY_SYM opt_if_not_exists opt_ident + { + if (Lex->check_add_key($4) || + !(Lex->last_key= (new (thd->mem_root) + Key(Key::MULTIPLE, $1.str ? &$1 : &$5, + HA_KEY_ALG_UNDEF, true, $4)))) + MYSQL_YYABORT; + Lex->option_list= NULL; + } + '(' key_list ')' references + { + LEX *lex=Lex; + Key *key= (new (thd->mem_root) + Foreign_key($5.str ? &$5 : &$1, + &lex->last_key->columns, + &$10->db, + &$10->table, + &lex->ref_list, + lex->fk_delete_opt, + lex->fk_update_opt, + lex->fk_match_option, + $4)); + if (key == NULL) + MYSQL_YYABORT; + /* + handle_if_exists_options() expectes the two keys in this order: + the Foreign_key, followed by its auto-generated Key. + */ + lex->alter_info.key_list.push_back(key, thd->mem_root); + lex->alter_info.key_list.push_back(Lex->last_key, thd->mem_root); + lex->option_list= NULL; + + /* Only used for ALTER TABLE. Ignored otherwise. */ + lex->alter_info.flags|= Alter_info::ADD_FOREIGN_KEY; + } + ; + +constraint_def: + opt_constraint check_constraint + { + Lex->add_constraint(&$1, $2, FALSE); + } + ; + +opt_check_constraint: + /* empty */ { $$= (Virtual_column_info*) 0; } + | check_constraint { $$= $1;} + ; + +check_constraint: + CHECK_SYM '(' expr ')' + { + Virtual_column_info *v= + add_virtual_expression(thd, $3); + if (!v) + { + MYSQL_YYABORT; + } + $$= v; + } + ; + +opt_constraint: + /* empty */ { $$= null_clex_str; } + | constraint { $$= $1; } + ; + +constraint: + CONSTRAINT opt_ident { $$=$2; } + ; + +field_spec: + field_ident + { + LEX *lex=Lex; + Create_field *f= new (thd->mem_root) Create_field(); + + if (check_string_char_length(&$1, 0, NAME_CHAR_LEN, + system_charset_info, 1)) + my_yyabort_error((ER_TOO_LONG_IDENT, MYF(0), $1.str)); + + if (!f) + MYSQL_YYABORT; + + lex->init_last_field(f, &$1, NULL); + $<create_field>$= f; + } + field_type_or_serial opt_check_constraint + { + LEX *lex=Lex; + $$= $<create_field>2; + + $$->check_constraint= $4; + + if ($$->check(thd)) + MYSQL_YYABORT; + + lex->alter_info.create_list.push_back($$, thd->mem_root); + + $$->create_if_not_exists= Lex->check_exists; + if ($$->flags & PRI_KEY_FLAG) + lex->add_key_to_list(&$1, Key::PRIMARY, lex->check_exists); + else if ($$->flags & UNIQUE_KEY_FLAG) + lex->add_key_to_list(&$1, Key::UNIQUE, lex->check_exists); + } + ; + +field_type_or_serial: + field_type { Lex->last_field->set_attributes($1, Lex->charset); } + field_def + | SERIAL_SYM + { + Lex->last_field->set_handler(&type_handler_longlong); + Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG + | UNSIGNED_FLAG | UNIQUE_KEY_FLAG; + } + opt_serial_attribute + ; + +opt_serial_attribute: + /* empty */ {} + | opt_serial_attribute_list {} + ; + +opt_serial_attribute_list: + opt_serial_attribute_list serial_attribute {} + | serial_attribute + ; + + +field_def: + opt_attribute + | opt_generated_always AS virtual_column_func + { + Lex->last_field->vcol_info= $3; + Lex->last_field->flags&= ~NOT_NULL_FLAG; // undo automatic NOT NULL for timestamps + } + vcol_opt_specifier vcol_opt_attribute + ; + +opt_generated_always: + /* empty */ {} + | GENERATED_SYM ALWAYS_SYM {} + ; + +vcol_opt_specifier: + /* empty */ + { + Lex->last_field->vcol_info->set_stored_in_db_flag(FALSE); + } + | VIRTUAL_SYM + { + Lex->last_field->vcol_info->set_stored_in_db_flag(FALSE); + } + | PERSISTENT_SYM + { + Lex->last_field->vcol_info->set_stored_in_db_flag(TRUE); + } + | STORED_SYM + { + Lex->last_field->vcol_info->set_stored_in_db_flag(TRUE); + } + ; + +vcol_opt_attribute: + /* empty */ {} + | vcol_opt_attribute_list {} + ; + +vcol_opt_attribute_list: + vcol_opt_attribute_list vcol_attribute {} + | vcol_attribute + ; + +vcol_attribute: + UNIQUE_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | UNIQUE_SYM KEY_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | COMMENT_SYM TEXT_STRING_sys { Lex->last_field->comment= $2; } + ; + +parse_vcol_expr: + PARSE_VCOL_EXPR_SYM + { + /* + "PARSE_VCOL_EXPR" can only be used by the SQL server + when reading a '*.frm' file. + Prevent the end user from invoking this command. + */ + MYSQL_YYABORT_UNLESS(Lex->parse_vcol_expr); + } + expr + { + Virtual_column_info *v= add_virtual_expression(thd, $3); + if (!v) + MYSQL_YYABORT; + Lex->last_field->vcol_info= v; + } + ; + +parenthesized_expr: + subselect + { + $$= new (thd->mem_root) Item_singlerow_subselect(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | expr + | expr ',' expr_list + { + $3->push_front($1, thd->mem_root); + $$= new (thd->mem_root) Item_row(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +virtual_column_func: + '(' parenthesized_expr ')' + { + Virtual_column_info *v= + add_virtual_expression(thd, $2); + if (!v) + { + MYSQL_YYABORT; + } + $$= v; + } + ; + +expr_or_literal: column_default_non_parenthesized_expr | signed_literal ; + +column_default_expr: + virtual_column_func + | expr_or_literal + { + if (!($$= add_virtual_expression(thd, $1))) + MYSQL_YYABORT; + } + ; + +field_type: + field_type_numeric + | field_type_temporal + | field_type_string + | field_type_lob + | field_type_misc + ; + + +sp_param_field_type: + field_type_numeric + | field_type_temporal + | sp_param_field_type_string + | field_type_lob + | field_type_misc + ; + + +field_type_numeric: + int_type opt_field_length field_options { $$.set($1, $2); } + | real_type opt_precision field_options { $$.set($1, $2); } + | FLOAT_SYM float_options field_options + { + $$.set(&type_handler_float, $2); + if ($2.length() && !$2.dec()) + { + int err; + ulonglong tmp_length= my_strtoll10($2.length(), NULL, &err); + if (err || tmp_length > PRECISION_FOR_DOUBLE) + my_yyabort_error((ER_WRONG_FIELD_SPEC, MYF(0), + Lex->last_field->field_name.str)); + if (tmp_length > PRECISION_FOR_FLOAT) + $$.set(&type_handler_double); + else + $$.set(&type_handler_float); + } + } + | BIT_SYM opt_field_length_default_1 + { + $$.set(&type_handler_bit, $2); + } + | BOOL_SYM + { + $$.set(&type_handler_tiny, "1"); + } + | BOOLEAN_SYM + { + $$.set(&type_handler_tiny, "1"); + } + | DECIMAL_SYM float_options field_options + { $$.set(&type_handler_newdecimal, $2);} + | NUMBER_SYM float_options field_options + { + if ($2.length() != 0) + $$.set(&type_handler_newdecimal, $2); + else + $$.set(&type_handler_double); + } + | NUMERIC_SYM float_options field_options + { $$.set(&type_handler_newdecimal, $2);} + | FIXED_SYM float_options field_options + { $$.set(&type_handler_newdecimal, $2);} + ; + + +field_type_string: + char opt_field_length_default_1 opt_binary + { + $$.set(&type_handler_string, $2); + } + | nchar opt_field_length_default_1 opt_bin_mod + { + $$.set(&type_handler_string, $2); + bincmp_collation(national_charset_info, $3); + } + | BINARY opt_field_length_default_1 + { + Lex->charset=&my_charset_bin; + $$.set(&type_handler_string, $2); + } + | varchar field_length opt_binary + { + $$.set(&type_handler_varchar, $2); + } + | VARCHAR2 field_length opt_binary + { + $$.set(&type_handler_varchar, $2); + } + | nvarchar field_length opt_bin_mod + { + $$.set(&type_handler_varchar, $2); + bincmp_collation(national_charset_info, $3); + } + | VARBINARY field_length + { + Lex->charset=&my_charset_bin; + $$.set(&type_handler_varchar, $2); + } + | RAW field_length + { + Lex->charset= &my_charset_bin; + $$.set(&type_handler_varchar, $2); + } + ; + + +sp_param_field_type_string: + char opt_field_length_default_sp_param_char opt_binary + { + $$.set(&type_handler_varchar, $2); + } + | nchar opt_field_length_default_sp_param_char opt_bin_mod + { + $$.set(&type_handler_varchar, $2); + bincmp_collation(national_charset_info, $3); + } + | BINARY opt_field_length_default_sp_param_char + { + Lex->charset=&my_charset_bin; + $$.set(&type_handler_varchar, $2); + } + | varchar opt_field_length_default_sp_param_varchar opt_binary + { + $$.set(&type_handler_varchar, $2); + } + | VARCHAR2 opt_field_length_default_sp_param_varchar opt_binary + { + $$.set(&type_handler_varchar, $2); + } + | nvarchar opt_field_length_default_sp_param_varchar opt_bin_mod + { + $$.set(&type_handler_varchar, $2); + bincmp_collation(national_charset_info, $3); + } + | VARBINARY opt_field_length_default_sp_param_varchar + { + Lex->charset= &my_charset_bin; + $$.set(&type_handler_varchar, $2); + } + | RAW opt_field_length_default_sp_param_varchar + { + Lex->charset= &my_charset_bin; + $$.set(&type_handler_varchar, $2); + } + ; + + +field_type_temporal: + YEAR_SYM opt_field_length field_options + { + if ($2) + { + errno= 0; + ulong length= strtoul($2, NULL, 10); + if (errno == 0 && length <= MAX_FIELD_BLOBLENGTH && length != 4) + { + char buff[sizeof("YEAR()") + MY_INT64_NUM_DECIMAL_DIGITS + 1]; + my_snprintf(buff, sizeof(buff), "YEAR(%lu)", length); + push_warning_printf(thd, Sql_condition::WARN_LEVEL_NOTE, + ER_WARN_DEPRECATED_SYNTAX, + ER_THD(thd, ER_WARN_DEPRECATED_SYNTAX), + buff, "YEAR(4)"); + } + } + $$.set(&type_handler_year, $2); + } + | DATE_SYM { $$.set(thd->type_handler_for_date()); } + | TIME_SYM opt_field_length + { + $$.set(opt_mysql56_temporal_format ? + static_cast<const Type_handler*>(&type_handler_time2) : + static_cast<const Type_handler*>(&type_handler_time), + $2); + } + | TIMESTAMP opt_field_length + { + if (thd->variables.sql_mode & MODE_MAXDB) + $$.set(opt_mysql56_temporal_format ? + static_cast<const Type_handler*>(&type_handler_datetime2) : + static_cast<const Type_handler*>(&type_handler_datetime), + $2); + else + { + /* + Unlike other types TIMESTAMP fields are NOT NULL by default. + Unless --explicit-defaults-for-timestamp is given. + */ + if (!opt_explicit_defaults_for_timestamp) + Lex->last_field->flags|= NOT_NULL_FLAG; + $$.set(opt_mysql56_temporal_format ? + static_cast<const Type_handler*>(&type_handler_timestamp2): + static_cast<const Type_handler*>(&type_handler_timestamp), + $2); + } + } + | DATETIME opt_field_length + { + $$.set(opt_mysql56_temporal_format ? + static_cast<const Type_handler*>(&type_handler_datetime2) : + static_cast<const Type_handler*>(&type_handler_datetime), + $2); + } + ; + + +field_type_lob: + TINYBLOB + { + Lex->charset=&my_charset_bin; + $$.set(&type_handler_tiny_blob); + } + | BLOB_SYM opt_field_length + { + Lex->charset=&my_charset_bin; + $$.set(&type_handler_long_blob); + } + | spatial_type float_options srid_option + { +#ifdef HAVE_SPATIAL + Lex->charset=&my_charset_bin; + Lex->last_field->geom_type= $1; + $$.set(&type_handler_geometry, $2); +#else + my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, + sym_group_geom.needed_define)); +#endif + } + | MEDIUMBLOB + { + Lex->charset=&my_charset_bin; + $$.set(&type_handler_medium_blob); + } + | LONGBLOB + { + Lex->charset=&my_charset_bin; + $$.set(&type_handler_long_blob); + } + | LONG_SYM VARBINARY + { + Lex->charset=&my_charset_bin; + $$.set(&type_handler_medium_blob); + } + | LONG_SYM varchar opt_binary + { $$.set(&type_handler_medium_blob); } + | TINYTEXT opt_binary + { $$.set(&type_handler_tiny_blob); } + | TEXT_SYM opt_field_length opt_binary + { $$.set(&type_handler_blob, $2); } + | MEDIUMTEXT opt_binary + { $$.set(&type_handler_medium_blob); } + | LONGTEXT opt_binary + { $$.set(&type_handler_long_blob); } + | CLOB opt_binary + { $$.set(&type_handler_long_blob); } + | LONG_SYM opt_binary + { $$.set(&type_handler_medium_blob); } + | JSON_SYM + { + Lex->charset= &my_charset_utf8mb4_bin; + $$.set(&type_handler_long_blob); + } + ; + +field_type_misc: + ENUM '(' string_list ')' opt_binary + { $$.set(&type_handler_enum); } + | SET '(' string_list ')' opt_binary + { $$.set(&type_handler_set); } + ; + +spatial_type: + GEOMETRY_SYM { $$= Field::GEOM_GEOMETRY; } + | GEOMETRYCOLLECTION { $$= Field::GEOM_GEOMETRYCOLLECTION; } + | POINT_SYM { $$= Field::GEOM_POINT; } + | MULTIPOINT { $$= Field::GEOM_MULTIPOINT; } + | LINESTRING { $$= Field::GEOM_LINESTRING; } + | MULTILINESTRING { $$= Field::GEOM_MULTILINESTRING; } + | POLYGON { $$= Field::GEOM_POLYGON; } + | MULTIPOLYGON { $$= Field::GEOM_MULTIPOLYGON; } + ; + +char: + CHAR_SYM {} + ; + +nchar: + NCHAR_SYM {} + | NATIONAL_SYM CHAR_SYM {} + ; + +varchar: + char VARYING {} + | VARCHAR {} + ; + +nvarchar: + NATIONAL_SYM VARCHAR {} + | NVARCHAR_SYM {} + | NCHAR_SYM VARCHAR {} + | NATIONAL_SYM CHAR_SYM VARYING {} + | NCHAR_SYM VARYING {} + ; + +int_type: + INT_SYM { $$= &type_handler_long; } + | TINYINT { $$= &type_handler_tiny; } + | SMALLINT { $$= &type_handler_short; } + | MEDIUMINT { $$= &type_handler_int24; } + | BIGINT { $$= &type_handler_longlong; } + ; + +real_type: + REAL + { + $$= thd->variables.sql_mode & MODE_REAL_AS_FLOAT ? + static_cast<const Type_handler *>(&type_handler_float) : + static_cast<const Type_handler *>(&type_handler_double); + } + | DOUBLE_SYM { $$= &type_handler_double; } + | DOUBLE_SYM PRECISION { $$= &type_handler_double; } + ; + +srid_option: + /* empty */ + { Lex->last_field->srid= 0; } + | + REF_SYSTEM_ID_SYM '=' NUM + { + Lex->last_field->srid=atoi($3.str); + } + ; + +float_options: + /* empty */ { $$.set(0, 0); } + | field_length { $$.set($1, 0); } + | precision { $$= $1; } + ; + +precision: + '(' NUM ',' NUM ')' { $$.set($2.str, $4.str); } + ; + +field_options: + /* empty */ {} + | SIGNED_SYM {} + | UNSIGNED { Lex->last_field->flags|= UNSIGNED_FLAG;} + | ZEROFILL { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; } + | UNSIGNED ZEROFILL { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; } + | ZEROFILL UNSIGNED { Lex->last_field->flags|= UNSIGNED_FLAG | ZEROFILL_FLAG; } + ; + +field_length: + '(' LONG_NUM ')' { $$= $2.str; } + | '(' ULONGLONG_NUM ')' { $$= $2.str; } + | '(' DECIMAL_NUM ')' { $$= $2.str; } + | '(' NUM ')' { $$= $2.str; }; + +opt_field_length: + /* empty */ { $$= (char*) 0; /* use default length */ } + | field_length { $$= $1; } + +opt_field_length_default_1: + /* empty */ { $$= (char*) "1"; } + | field_length { $$= $1; } + + +/* + In sql_mode=ORACLE, real size of VARCHAR and CHAR with no length + in SP parameters is fixed at runtime with the length of real args. + Let's translate VARCHAR to VARCHAR(4000) for return value. + + Since Oracle 9, maximum size for VARCHAR in PL/SQL is 32767. + + In MariaDB the limit for VARCHAR is 65535 bytes. + We could translate VARCHAR with no length to VARCHAR(65535), but + it would mean that for multi-byte character sets we'd have to translate + VARCHAR to MEDIUMTEXT, to guarantee 65535 characters. + + Also we could translate VARCHAR to VARCHAR(16383), where 16383 is + the maximum possible length in characters in case of mbmaxlen=4 + (e.g. utf32, utf16, utf8mb4). However, we'll have character sets with + mbmaxlen=5 soon (e.g. gb18030). +*/ +opt_field_length_default_sp_param_varchar: + /* empty */ { $$.set("4000", "4000"); } + | field_length { $$.set($1, NULL); } + +opt_field_length_default_sp_param_char: + /* empty */ { $$.set("2000", "2000"); } + | field_length { $$.set($1, NULL); } + +opt_precision: + /* empty */ { $$.set(0, 0); } + | precision { $$= $1; } + ; + +opt_attribute: + /* empty */ {} + | opt_attribute_list {} + ; + +opt_attribute_list: + opt_attribute_list attribute {} + | attribute + ; + +attribute: + NULL_SYM { Lex->last_field->flags&= ~ NOT_NULL_FLAG; } + | DEFAULT column_default_expr { Lex->last_field->default_value= $2; } + | ON UPDATE_SYM NOW_SYM opt_default_time_precision + { + Item *item= new (thd->mem_root) Item_func_now_local(thd, $4); + if (item == NULL) + MYSQL_YYABORT; + Lex->last_field->on_update= item; + } + | AUTO_INC { Lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG; } + | SERIAL_SYM DEFAULT VALUE_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= AUTO_INCREMENT_FLAG | NOT_NULL_FLAG | UNIQUE_KEY_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | COLLATE_SYM collation_name + { + if (Lex->charset && !my_charset_same(Lex->charset,$2)) + my_yyabort_error((ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $2->name,Lex->charset->csname)); + Lex->last_field->charset= $2; + } + | serial_attribute + ; + +serial_attribute: + not NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; } + | opt_primary KEY_SYM + { + LEX *lex=Lex; + lex->last_field->flags|= PRI_KEY_FLAG | NOT_NULL_FLAG; + lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | vcol_attribute + | IDENT_sys equal TEXT_STRING_sys + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, true, &Lex->last_field->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ident + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, false, &Lex->last_field->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal real_ulonglong_num + { + new (thd->mem_root) + engine_option_value($1, $3, &Lex->last_field->option_list, + &Lex->option_list_last, thd->mem_root); + } + | IDENT_sys equal DEFAULT + { + new (thd->mem_root) + engine_option_value($1, &Lex->last_field->option_list, &Lex->option_list_last); + } + ; + + +type_with_opt_collate: + field_type opt_collate + { + $$= $1; + + if ($2) + { + if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))) + MYSQL_YYABORT; + } + Lex->last_field->set_attributes($1, Lex->charset); + } + ; + +sp_param_type_with_opt_collate: + sp_param_field_type opt_collate + { + $$= $1; + if ($2) + { + if (!(Lex->charset= merge_charset_and_collation(Lex->charset, $2))) + MYSQL_YYABORT; + } + Lex->last_field->set_attributes($1, Lex->charset); + } + ; + +charset: + CHAR_SYM SET {} + | CHARSET {} + ; + +charset_name: + ident_or_text + { + if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0)))) + my_yyabort_error((ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str)); + } + | BINARY { $$= &my_charset_bin; } + ; + +charset_name_or_default: + charset_name { $$=$1; } + | DEFAULT { $$=NULL; } + ; + +opt_load_data_charset: + /* Empty */ { $$= NULL; } + | charset charset_name_or_default { $$= $2; } + ; + +old_or_new_charset_name: + ident_or_text + { + if (!($$=get_charset_by_csname($1.str,MY_CS_PRIMARY,MYF(0))) && + !($$=get_old_charset_by_name($1.str))) + my_yyabort_error((ER_UNKNOWN_CHARACTER_SET, MYF(0), $1.str)); + } + | BINARY { $$= &my_charset_bin; } + ; + +old_or_new_charset_name_or_default: + old_or_new_charset_name { $$=$1; } + | DEFAULT { $$=NULL; } + ; + +collation_name: + ident_or_text + { + if (!($$= mysqld_collation_get_by_name($1.str))) + MYSQL_YYABORT; + } + ; + +opt_collate: + /* empty */ { $$=NULL; } + | COLLATE_SYM collation_name_or_default { $$=$2; } + ; + +collation_name_or_default: + collation_name { $$=$1; } + | DEFAULT { $$=NULL; } + ; + +opt_default: + /* empty */ {} + | DEFAULT {} + ; + +charset_or_alias: + charset charset_name { $$= $2; } + | ASCII_SYM { $$= &my_charset_latin1; } + | UNICODE_SYM + { + if (!($$= get_charset_by_csname("ucs2", MY_CS_PRIMARY,MYF(0)))) + my_yyabort_error((ER_UNKNOWN_CHARACTER_SET, MYF(0), "ucs2")); + } + ; + +opt_binary: + /* empty */ { bincmp_collation(NULL, false); } + | BYTE_SYM { bincmp_collation(&my_charset_bin, false); } + | charset_or_alias opt_bin_mod { bincmp_collation($1, $2); } + | BINARY { bincmp_collation(NULL, true); } + | BINARY charset_or_alias { bincmp_collation($2, true); } + ; + +opt_bin_mod: + /* empty */ { $$= false; } + | BINARY { $$= true; } + ; + +ws_nweights: + '(' real_ulong_num + { + if ($2 == 0) + { + thd->parse_error(); + MYSQL_YYABORT; + } + } + ')' + { $$= $2; } + ; + +ws_level_flag_desc: + ASC { $$= 0; } + | DESC { $$= 1 << MY_STRXFRM_DESC_SHIFT; } + ; + +ws_level_flag_reverse: + REVERSE_SYM { $$= 1 << MY_STRXFRM_REVERSE_SHIFT; } ; + +ws_level_flags: + /* empty */ { $$= 0; } + | ws_level_flag_desc { $$= $1; } + | ws_level_flag_desc ws_level_flag_reverse { $$= $1 | $2; } + | ws_level_flag_reverse { $$= $1 ; } + ; + +ws_level_number: + real_ulong_num + { + $$= $1 < 1 ? 1 : ($1 > MY_STRXFRM_NLEVELS ? MY_STRXFRM_NLEVELS : $1); + $$--; + } + ; + +ws_level_list_item: + ws_level_number ws_level_flags + { + $$= (1 | $2) << $1; + } + ; + +ws_level_list: + ws_level_list_item { $$= $1; } + | ws_level_list ',' ws_level_list_item { $$|= $3; } + ; + +ws_level_range: + ws_level_number '-' ws_level_number + { + uint start= $1; + uint end= $3; + for ($$= 0; start <= end; start++) + $$|= (1 << start); + } + ; + +ws_level_list_or_range: + ws_level_list { $$= $1; } + | ws_level_range { $$= $1; } + ; + +opt_ws_levels: + /* empty*/ { $$= 0; } + | LEVEL_SYM ws_level_list_or_range { $$= $2; } + ; + +opt_primary: + /* empty */ + | PRIMARY_SYM + ; + +references: + REFERENCES + table_ident + opt_ref_list + opt_match_clause + opt_on_update_delete + { + $$=$2; + } + ; + +opt_ref_list: + /* empty */ + { Lex->ref_list.empty(); } + | '(' ref_list ')' + ; + +ref_list: + ref_list ',' ident + { + Key_part_spec *key= new (thd->mem_root) Key_part_spec(&$3, 0); + if (key == NULL) + MYSQL_YYABORT; + Lex->ref_list.push_back(key, thd->mem_root); + } + | ident + { + Key_part_spec *key= new (thd->mem_root) Key_part_spec(&$1, 0); + if (key == NULL) + MYSQL_YYABORT; + LEX *lex= Lex; + lex->ref_list.empty(); + lex->ref_list.push_back(key, thd->mem_root); + } + ; + +opt_match_clause: + /* empty */ + { Lex->fk_match_option= Foreign_key::FK_MATCH_UNDEF; } + | MATCH FULL + { Lex->fk_match_option= Foreign_key::FK_MATCH_FULL; } + | MATCH PARTIAL + { Lex->fk_match_option= Foreign_key::FK_MATCH_PARTIAL; } + | MATCH SIMPLE_SYM + { Lex->fk_match_option= Foreign_key::FK_MATCH_SIMPLE; } + ; + +opt_on_update_delete: + /* empty */ + { + LEX *lex= Lex; + lex->fk_update_opt= FK_OPTION_UNDEF; + lex->fk_delete_opt= FK_OPTION_UNDEF; + } + | ON UPDATE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= $3; + lex->fk_delete_opt= FK_OPTION_UNDEF; + } + | ON DELETE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= FK_OPTION_UNDEF; + lex->fk_delete_opt= $3; + } + | ON UPDATE_SYM delete_option + ON DELETE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= $3; + lex->fk_delete_opt= $6; + } + | ON DELETE_SYM delete_option + ON UPDATE_SYM delete_option + { + LEX *lex= Lex; + lex->fk_update_opt= $6; + lex->fk_delete_opt= $3; + } + ; + +delete_option: + RESTRICT { $$= FK_OPTION_RESTRICT; } + | CASCADE { $$= FK_OPTION_CASCADE; } + | SET NULL_SYM { $$= FK_OPTION_SET_NULL; } + | NO_SYM ACTION { $$= FK_OPTION_NO_ACTION; } + | SET DEFAULT { $$= FK_OPTION_SET_DEFAULT; } + ; + +constraint_key_type: + PRIMARY_SYM KEY_SYM { $$= Key::PRIMARY; } + | UNIQUE_SYM opt_key_or_index { $$= Key::UNIQUE; } + ; + +key_or_index: + KEY_SYM {} + | INDEX_SYM {} + ; + +opt_key_or_index: + /* empty */ {} + | key_or_index + ; + +keys_or_index: + KEYS {} + | INDEX_SYM {} + | INDEXES {} + ; + +opt_unique: + /* empty */ { $$= Key::MULTIPLE; } + | UNIQUE_SYM { $$= Key::UNIQUE; } + ; + +fulltext: + FULLTEXT_SYM { $$= Key::FULLTEXT;} + ; + +spatial: + SPATIAL_SYM + { +#ifdef HAVE_SPATIAL + $$= Key::SPATIAL; +#else + my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, + sym_group_geom.needed_define)); +#endif + } + ; + +normal_key_options: + /* empty */ {} + | normal_key_opts { Lex->last_key->option_list= Lex->option_list; } + ; + +fulltext_key_options: + /* empty */ {} + | fulltext_key_opts { Lex->last_key->option_list= Lex->option_list; } + ; + +spatial_key_options: + /* empty */ {} + | spatial_key_opts { Lex->last_key->option_list= Lex->option_list; } + ; + +normal_key_opts: + normal_key_opt + | normal_key_opts normal_key_opt + ; + +spatial_key_opts: + spatial_key_opt + | spatial_key_opts spatial_key_opt + ; + +fulltext_key_opts: + fulltext_key_opt + | fulltext_key_opts fulltext_key_opt + ; + +opt_USING_key_algorithm: + /* Empty*/ { $$= HA_KEY_ALG_UNDEF; } + | USING btree_or_rtree { $$= $2; } + +/* TYPE is a valid identifier, so it's handled differently than USING */ +opt_key_algorithm_clause: + /* Empty*/ { $$= HA_KEY_ALG_UNDEF; } + | USING btree_or_rtree { $$= $2; } + | TYPE_SYM btree_or_rtree { $$= $2; } + +key_using_alg: + USING btree_or_rtree + { Lex->last_key->key_create_info.algorithm= $2; } + | TYPE_SYM btree_or_rtree + { Lex->last_key->key_create_info.algorithm= $2; } + ; + +all_key_opt: + KEY_BLOCK_SIZE opt_equal ulong_num + { Lex->last_key->key_create_info.block_size= $3; } + | COMMENT_SYM TEXT_STRING_sys + { Lex->last_key->key_create_info.comment= $2; } + | IDENT_sys equal TEXT_STRING_sys + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, true, &Lex->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ident + { + if ($3.length > ENGINE_OPTION_MAX_LENGTH) + my_yyabort_error((ER_VALUE_TOO_LONG, MYF(0), $1.str)); + new (thd->mem_root) + engine_option_value($1, $3, false, &Lex->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal real_ulonglong_num + { + new (thd->mem_root) + engine_option_value($1, $3, &Lex->option_list, + &Lex->option_list_last, thd->mem_root); + } + | IDENT_sys equal DEFAULT + { + new (thd->mem_root) + engine_option_value($1, &Lex->option_list, &Lex->option_list_last); + } + ; + +normal_key_opt: + all_key_opt + | key_using_alg + ; + +spatial_key_opt: + all_key_opt + ; + +fulltext_key_opt: + all_key_opt + | WITH PARSER_SYM IDENT_sys + { + if (plugin_is_ready(&$3, MYSQL_FTPARSER_PLUGIN)) + Lex->last_key->key_create_info.parser_name= $3; + else + my_yyabort_error((ER_FUNCTION_NOT_DEFINED, MYF(0), $3.str)); + } + ; + +btree_or_rtree: + BTREE_SYM { $$= HA_KEY_ALG_BTREE; } + | RTREE_SYM { $$= HA_KEY_ALG_RTREE; } + | HASH_SYM { $$= HA_KEY_ALG_HASH; } + ; + +key_list: + key_list ',' key_part order_dir + { + Lex->last_key->columns.push_back($3, thd->mem_root); + } + | key_part order_dir + { + Lex->last_key->columns.push_back($1, thd->mem_root); + } + ; + +key_part: + ident + { + $$= new (thd->mem_root) Key_part_spec(&$1, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ident '(' NUM ')' + { + int key_part_len= atoi($3.str); + if (!key_part_len) + my_yyabort_error((ER_KEY_PART_0, MYF(0), $1.str)); + $$= new (thd->mem_root) Key_part_spec(&$1, (uint) key_part_len); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_ident: + /* empty */ { $$= null_clex_str; } + | field_ident { $$= $1; } + ; + +opt_component: + /* empty */ { $$= null_clex_str; } + | '.' ident { $$= $2; } + ; + +string_list: + text_string + { Lex->last_field->interval_list.push_back($1, thd->mem_root); } + | string_list ',' text_string + { Lex->last_field->interval_list.push_back($3, thd->mem_root); }; + +/* +** Alter table +*/ + +alter: + ALTER + { + Lex->name= null_clex_str; + Lex->table_type= TABLE_TYPE_UNKNOWN; + Lex->sql_command= SQLCOM_ALTER_TABLE; + Lex->duplicates= DUP_ERROR; + Lex->select_lex.init_order(); + Lex->create_info.init(); + Lex->create_info.row_type= ROW_TYPE_NOT_USED; + Lex->alter_info.reset(); + Lex->no_write_to_binlog= 0; + Lex->create_info.storage_media= HA_SM_DEFAULT; + DBUG_ASSERT(!Lex->m_sql_cmd); + } + alter_options TABLE_SYM table_ident + { + if (!Lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_UPGRADABLE)) + MYSQL_YYABORT; + Lex->select_lex.db= (Lex->select_lex.table_list.first)->db; + Lex->create_last_non_select_table= Lex->last_table(); + } + alter_commands + { + if (!Lex->m_sql_cmd) + { + /* Create a generic ALTER TABLE statment. */ + Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_table(); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + } + | ALTER DATABASE ident_or_empty + { + Lex->create_info.default_table_charset= NULL; + Lex->create_info.used_fields= 0; + } + create_database_options + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_ALTER_DB; + lex->name= $3; + if (lex->name.str == NULL && + lex->copy_db_to(&lex->name.str, &lex->name.length)) + MYSQL_YYABORT; + } + | ALTER DATABASE ident UPGRADE_SYM DATA_SYM DIRECTORY_SYM NAME_SYM + { + LEX *lex= Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "DATABASE")); + lex->sql_command= SQLCOM_ALTER_DB_UPGRADE; + lex->name= $3; + } + | ALTER PROCEDURE_SYM sp_name + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE")); + lex->sp_chistics.init(); + } + sp_a_chistics + { + LEX *lex=Lex; + + lex->sql_command= SQLCOM_ALTER_PROCEDURE; + lex->spname= $3; + } + | ALTER FUNCTION_SYM sp_name + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); + lex->sp_chistics.init(); + } + sp_a_chistics + { + LEX *lex=Lex; + + lex->sql_command= SQLCOM_ALTER_FUNCTION; + lex->spname= $3; + } + | ALTER view_algorithm definer_opt opt_view_suid VIEW_SYM table_ident + { + if (Lex->add_alter_view(thd, $2, $4, $6)) + MYSQL_YYABORT; + } + view_list_opt AS view_select + {} + | ALTER definer_opt opt_view_suid VIEW_SYM table_ident + /* + We have two separate rules for ALTER VIEW rather that + optional view_algorithm above, to resolve the ambiguity + with the ALTER EVENT below. + */ + { + if (Lex->add_alter_view(thd, VIEW_ALGORITHM_INHERIT, $3, $5)) + MYSQL_YYABORT; + } + view_list_opt AS view_select + {} + | ALTER definer_opt remember_name EVENT_SYM sp_name + { + /* + It is safe to use Lex->spname because + ALTER EVENT xxx RENATE TO yyy DO ALTER EVENT RENAME TO + is not allowed. Lex->spname is used in the case of RENAME TO + If it had to be supported spname had to be added to + Event_parse_data. + */ + + if (!(Lex->event_parse_data= Event_parse_data::new_instance(thd))) + MYSQL_YYABORT; + Lex->event_parse_data->identifier= $5; + + Lex->sql_command= SQLCOM_ALTER_EVENT; + Lex->stmt_definition_begin= $3; + } + ev_alter_on_schedule_completion + opt_ev_rename_to + opt_ev_status + opt_ev_comment + opt_ev_sql_stmt + { + if (!($7 || $8 || $9 || $10 || $11)) + { + thd->parse_error(); + MYSQL_YYABORT; + } + /* + sql_command is set here because some rules in ev_sql_stmt + can overwrite it + */ + Lex->sql_command= SQLCOM_ALTER_EVENT; + Lex->stmt_definition_end= (char*)YYLIP->get_cpp_ptr(); + } + | ALTER TABLESPACE alter_tablespace_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= ALTER_TABLESPACE; + } + | ALTER LOGFILE_SYM GROUP_SYM alter_logfile_group_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= ALTER_LOGFILE_GROUP; + } + | ALTER TABLESPACE change_tablespace_info + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= CHANGE_FILE_TABLESPACE; + } + | ALTER TABLESPACE change_tablespace_access + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= ALTER_ACCESS_MODE_TABLESPACE; + } + | ALTER SERVER_SYM ident_or_text + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_ALTER_SERVER; + lex->server_options.reset($3); + } OPTIONS_SYM '(' server_options_list ')' { } + /* ALTER USER foo is allowed for MySQL compatibility. */ + | ALTER opt_if_exists USER_SYM clear_privileges grant_list + opt_require_clause opt_resource_options + { + Lex->create_info.set($2); + Lex->sql_command= SQLCOM_ALTER_USER; + } + | ALTER SEQUENCE_SYM opt_if_exists_table_element + { + LEX *lex= Lex; + lex->name= null_clex_str; + lex->table_type= TABLE_TYPE_UNKNOWN; + lex->sql_command= SQLCOM_ALTER_SEQUENCE; + lex->create_info.init(); + lex->no_write_to_binlog= 0; + DBUG_ASSERT(!lex->m_sql_cmd); + } + table_ident + { + LEX *lex= Lex; + if (!(lex->create_info.seq_create_info= new (thd->mem_root) + sequence_definition()) || + !lex->select_lex.add_table_to_list(thd, $5, NULL, + TL_OPTION_SEQUENCE, + TL_WRITE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + } + sequence_defs + { + /* Create a generic ALTER SEQUENCE statment. */ + Lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_alter_sequence(); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +ev_alter_on_schedule_completion: + /* empty */ { $$= 0;} + | ON SCHEDULE_SYM ev_schedule_time { $$= 1; } + | ev_on_completion { $$= 1; } + | ON SCHEDULE_SYM ev_schedule_time ev_on_completion { $$= 1; } + ; + +opt_ev_rename_to: + /* empty */ { $$= 0;} + | RENAME TO_SYM sp_name + { + /* + Use lex's spname to hold the new name. + The original name is in the Event_parse_data object + */ + Lex->spname= $3; + $$= 1; + } + ; + +opt_ev_sql_stmt: + /* empty*/ { $$= 0;} + | DO_SYM ev_sql_stmt { $$= 1; } + ; + +ident_or_empty: + /* empty */ { $$= null_clex_str; } + | ident { $$= $1; } + ; + +alter_commands: + /* empty */ + | DISCARD TABLESPACE + { + Lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_discard_import_tablespace( + Sql_cmd_discard_import_tablespace::DISCARD_TABLESPACE); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + | IMPORT TABLESPACE + { + Lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_discard_import_tablespace( + Sql_cmd_discard_import_tablespace::IMPORT_TABLESPACE); + if (Lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + | alter_list + opt_partitioning + | alter_list + remove_partitioning + | remove_partitioning + | partitioning +/* + This part was added for release 5.1 by Mikael Ronstrm. + From here we insert a number of commands to manage the partitions of a + partitioned table such as adding partitions, dropping partitions, + reorganising partitions in various manners. In future releases the list + will be longer. +*/ + | add_partition_rule + | DROP PARTITION_SYM opt_if_exists alt_part_name_list + { + Lex->alter_info.flags|= Alter_info::ALTER_DROP_PARTITION; + DBUG_ASSERT(!Lex->if_exists()); + Lex->create_info.add($3); + } + | REBUILD_SYM PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= Lex; + lex->alter_info.flags|= Alter_info::ALTER_REBUILD_PARTITION; + lex->no_write_to_binlog= $3; + } + | OPTIMIZE PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->no_write_to_binlog= $3; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_optimize_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + opt_no_write_to_binlog + | ANALYZE_SYM PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->no_write_to_binlog= $3; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_analyze_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + | CHECK_SYM PARTITION_SYM all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_check_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + opt_mi_check_type + | REPAIR PARTITION_SYM opt_no_write_to_binlog + all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->no_write_to_binlog= $3; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_repair_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + opt_mi_repair_type + | COALESCE PARTITION_SYM opt_no_write_to_binlog real_ulong_num + { + LEX *lex= Lex; + lex->alter_info.flags|= Alter_info::ALTER_COALESCE_PARTITION; + lex->no_write_to_binlog= $3; + lex->alter_info.num_parts= $4; + } + | TRUNCATE_SYM PARTITION_SYM all_or_alt_part_name_list + { + LEX *lex= thd->lex; + lex->check_opt.init(); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_truncate_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + | reorg_partition_rule + | EXCHANGE_SYM PARTITION_SYM alt_part_name_item + WITH TABLE_SYM table_ident have_partitioning + { + LEX *lex= thd->lex; + size_t dummy; + lex->select_lex.db=$6->db.str; + if (lex->select_lex.db == NULL && + lex->copy_db_to(&lex->select_lex.db, &dummy)) + { + MYSQL_YYABORT; + } + lex->name= $6->table; + lex->alter_info.flags|= Alter_info::ALTER_EXCHANGE_PARTITION; + if (!lex->select_lex.add_table_to_list(thd, $6, NULL, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_NO_WRITE)) + MYSQL_YYABORT; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) + Sql_cmd_alter_table_exchange_partition(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +remove_partitioning: + REMOVE_SYM PARTITIONING_SYM + { + Lex->alter_info.flags|= Alter_info::ALTER_REMOVE_PARTITIONING; + } + ; + +all_or_alt_part_name_list: + ALL + { + Lex->alter_info.flags|= Alter_info::ALTER_ALL_PARTITION; + } + | alt_part_name_list + ; + +add_partition_rule: + ADD PARTITION_SYM opt_if_not_exists + opt_no_write_to_binlog + { + LEX *lex= Lex; + lex->part_info= new (thd->mem_root) partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + MYSQL_YYABORT; + } + lex->alter_info.flags|= Alter_info::ALTER_ADD_PARTITION; + DBUG_ASSERT(!Lex->create_info.if_not_exists()); + lex->create_info.set($3); + lex->no_write_to_binlog= $4; + } + add_part_extra + {} + ; + +add_part_extra: + /* empty */ + | '(' part_def_list ')' + { + LEX *lex= Lex; + lex->part_info->num_parts= lex->part_info->partitions.elements; + } + | PARTITIONS_SYM real_ulong_num + { + Lex->part_info->num_parts= $2; + } + ; + +reorg_partition_rule: + REORGANIZE_SYM PARTITION_SYM opt_no_write_to_binlog + { + LEX *lex= Lex; + lex->part_info= new (thd->mem_root) partition_info(); + if (!lex->part_info) + { + mem_alloc_error(sizeof(partition_info)); + MYSQL_YYABORT; + } + lex->no_write_to_binlog= $3; + } + reorg_parts_rule + ; + +reorg_parts_rule: + /* empty */ + { + Lex->alter_info.flags|= Alter_info::ALTER_TABLE_REORG; + } + | alt_part_name_list + { + Lex->alter_info.flags|= Alter_info::ALTER_REORGANIZE_PARTITION; + } + INTO '(' part_def_list ')' + { + partition_info *part_info= Lex->part_info; + part_info->num_parts= part_info->partitions.elements; + } + ; + +alt_part_name_list: + alt_part_name_item {} + | alt_part_name_list ',' alt_part_name_item {} + ; + +alt_part_name_item: + ident + { + if (Lex->alter_info.partition_names.push_back($1.str, + thd->mem_root)) + { + mem_alloc_error(1); + MYSQL_YYABORT; + } + } + ; + +/* + End of management of partition commands +*/ + +alter_list: + alter_list_item + | alter_list ',' alter_list_item + ; + +add_column: + ADD opt_column opt_if_not_exists_table_element + { + LEX *lex=Lex; + lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN; + } + ; + +alter_list_item: + add_column column_def opt_place + { + Lex->create_last_non_select_table= Lex->last_table(); + $2->after= $3; + } + | ADD key_def + { + Lex->create_last_non_select_table= Lex->last_table(); + Lex->alter_info.flags|= Alter_info::ALTER_ADD_INDEX; + } + | add_column '(' create_field_list ')' + { + Lex->alter_info.flags|= Alter_info::ALTER_ADD_COLUMN | + Alter_info::ALTER_ADD_INDEX; + } + | ADD constraint_def + { + Lex->alter_info.flags|= Alter_info::ALTER_ADD_CHECK_CONSTRAINT; + } + | ADD CONSTRAINT IF_SYM not EXISTS field_ident check_constraint + { + Lex->alter_info.flags|= Alter_info::ALTER_ADD_CHECK_CONSTRAINT; + Lex->add_constraint(&$6, $7, TRUE); + } + | CHANGE opt_column opt_if_exists_table_element field_ident + field_spec opt_place + { + Lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN; + Lex->create_last_non_select_table= Lex->last_table(); + $5->change= $4; + $5->after= $6; + } + | MODIFY_SYM opt_column opt_if_exists_table_element + field_spec opt_place + { + Lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN; + Lex->create_last_non_select_table= Lex->last_table(); + $4->change= $4->field_name; + $4->after= $5; + } + | DROP opt_column opt_if_exists_table_element field_ident opt_restrict + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::COLUMN, $4.str, $3)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_DROP_COLUMN; + } + | DROP CONSTRAINT opt_if_exists_table_element field_ident + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::CHECK_CONSTRAINT, + $4.str, $3)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_DROP_CHECK_CONSTRAINT; + } + | DROP FOREIGN KEY_SYM opt_if_exists_table_element field_ident + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::FOREIGN_KEY, $5.str, $4)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::DROP_FOREIGN_KEY; + } + | DROP PRIMARY_SYM KEY_SYM + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::KEY, primary_key_name, + FALSE)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX; + } + | DROP key_or_index opt_if_exists_table_element field_ident + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::KEY, $4.str, $3)); + if (ad == NULL) + MYSQL_YYABORT; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_DROP_INDEX; + } + | DISABLE_SYM KEYS + { + LEX *lex=Lex; + lex->alter_info.keys_onoff= Alter_info::DISABLE; + lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF; + } + | ENABLE_SYM KEYS + { + LEX *lex=Lex; + lex->alter_info.keys_onoff= Alter_info::ENABLE; + lex->alter_info.flags|= Alter_info::ALTER_KEYS_ONOFF; + } + | ALTER opt_column field_ident SET DEFAULT column_default_expr + { + LEX *lex=Lex; + Alter_column *ac= new (thd->mem_root) Alter_column($3.str,$6); + if (ac == NULL) + MYSQL_YYABORT; + lex->alter_info.alter_list.push_back(ac, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; + } + | ALTER opt_column field_ident DROP DEFAULT + { + LEX *lex=Lex; + Alter_column *ac= (new (thd->mem_root) + Alter_column($3.str, (Virtual_column_info*) 0)); + if (ac == NULL) + MYSQL_YYABORT; + lex->alter_info.alter_list.push_back(ac, thd->mem_root); + lex->alter_info.flags|= Alter_info::ALTER_CHANGE_COLUMN_DEFAULT; + } + | RENAME opt_to table_ident + { + LEX *lex=Lex; + size_t dummy; + lex->select_lex.db=$3->db.str; + if (lex->select_lex.db == NULL && + lex->copy_db_to(&lex->select_lex.db, &dummy)) + { + MYSQL_YYABORT; + } + if (check_table_name($3->table.str,$3->table.length, FALSE) || + ($3->db.str && check_db_name((LEX_STRING*) &$3->db))) + my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3->table.str)); + lex->name= $3->table; + lex->alter_info.flags|= Alter_info::ALTER_RENAME; + } + | CONVERT_SYM TO_SYM charset charset_name_or_default opt_collate + { + if (!$4) + { + $4= thd->variables.collation_database; + } + $5= $5 ? $5 : $4; + if (!my_charset_same($4,$5)) + my_yyabort_error((ER_COLLATION_CHARSET_MISMATCH, MYF(0), + $5->name, $4->csname)); + if (Lex->create_info.add_alter_list_item_convert_to_charset($5)) + MYSQL_YYABORT; + Lex->alter_info.flags|= Alter_info::ALTER_OPTIONS; + } + | create_table_options_space_separated + { + LEX *lex=Lex; + lex->alter_info.flags|= Alter_info::ALTER_OPTIONS; + if ((lex->create_info.used_fields & HA_CREATE_USED_ENGINE) && + !lex->create_info.db_type) + { + lex->create_info.used_fields&= ~HA_CREATE_USED_ENGINE; + } + } + | FORCE_SYM + { + Lex->alter_info.flags|= Alter_info::ALTER_RECREATE; + } + | alter_order_clause + { + LEX *lex=Lex; + lex->alter_info.flags|= Alter_info::ALTER_ORDER; + } + | alter_algorithm_option + | alter_lock_option + ; + +opt_index_lock_algorithm: + /* empty */ + | alter_lock_option + | alter_algorithm_option + | alter_lock_option alter_algorithm_option + | alter_algorithm_option alter_lock_option + +alter_algorithm_option: + ALGORITHM_SYM opt_equal DEFAULT + { + Lex->alter_info.requested_algorithm= + Alter_info::ALTER_TABLE_ALGORITHM_DEFAULT; + } + | ALGORITHM_SYM opt_equal ident + { + if (Lex->alter_info.set_requested_algorithm(&$3)) + my_yyabort_error((ER_UNKNOWN_ALTER_ALGORITHM, MYF(0), $3.str)); + } + ; + +alter_lock_option: + LOCK_SYM opt_equal DEFAULT + { + Lex->alter_info.requested_lock= + Alter_info::ALTER_TABLE_LOCK_DEFAULT; + } + | LOCK_SYM opt_equal ident + { + if (Lex->alter_info.set_requested_lock(&$3)) + my_yyabort_error((ER_UNKNOWN_ALTER_LOCK, MYF(0), $3.str)); + } + ; + +opt_column: + /* empty */ {} + | COLUMN_SYM {} + ; + +opt_ignore: + /* empty */ { Lex->ignore= 0;} + | IGNORE_SYM { Lex->ignore= 1;} + ; + +alter_options: + { Lex->ignore= 0;} alter_options_part2 + ; + +alter_options_part2: + /* empty */ + | alter_option_list + ; + +alter_option_list: + alter_option_list alter_option + | alter_option + ; + +alter_option: + IGNORE_SYM { Lex->ignore= 1;} + | ONLINE_SYM + { + Lex->alter_info.requested_lock= + Alter_info::ALTER_TABLE_LOCK_NONE; + } + + +opt_restrict: + /* empty */ { Lex->drop_mode= DROP_DEFAULT; } + | RESTRICT { Lex->drop_mode= DROP_RESTRICT; } + | CASCADE { Lex->drop_mode= DROP_CASCADE; } + ; + +opt_place: + /* empty */ { $$= null_clex_str; } + | AFTER_SYM ident + { + $$= $2; + Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER; + } + | FIRST_SYM + { + $$.str= first_keyword; + $$.length= 5; /* Length of "first" */ + Lex->alter_info.flags |= Alter_info::ALTER_COLUMN_ORDER; + } + ; + +opt_to: + /* empty */ {} + | TO_SYM {} + | '=' {} + | AS {} + ; + +slave: + START_SYM SLAVE optional_connection_name slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_START; + lex->type = 0; + /* If you change this code don't forget to update SLAVE START too */ + } + slave_until + {} + | START_SYM ALL SLAVES slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_ALL_START; + lex->type = 0; + /* If you change this code don't forget to update STOP SLAVE too */ + } + {} + | STOP_SYM SLAVE optional_connection_name slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_STOP; + lex->type = 0; + /* If you change this code don't forget to update SLAVE STOP too */ + } + | STOP_SYM ALL SLAVES slave_thread_opts + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_SLAVE_ALL_STOP; + lex->type = 0; + /* If you change this code don't forget to update SLAVE STOP too */ + } + ; + +start: + START_SYM TRANSACTION_SYM opt_start_transaction_option_list + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_BEGIN; + /* READ ONLY and READ WRITE are mutually exclusive. */ + if (($3 & MYSQL_START_TRANS_OPT_READ_WRITE) && + ($3 & MYSQL_START_TRANS_OPT_READ_ONLY)) + { + thd->parse_error(); + MYSQL_YYABORT; + } + lex->start_transaction_opt= $3; + } + ; + +opt_start_transaction_option_list: + /* empty */ + { + $$= 0; + } + | start_transaction_option_list + { + $$= $1; + } + ; + +start_transaction_option_list: + start_transaction_option + { + $$= $1; + } + | start_transaction_option_list ',' start_transaction_option + { + $$= $1 | $3; + } + ; + +start_transaction_option: + WITH CONSISTENT_SYM SNAPSHOT_SYM + { + $$= MYSQL_START_TRANS_OPT_WITH_CONS_SNAPSHOT; + } + | READ_SYM ONLY_SYM + { + $$= MYSQL_START_TRANS_OPT_READ_ONLY; + } + | READ_SYM WRITE_SYM + { + $$= MYSQL_START_TRANS_OPT_READ_WRITE; + } + ; + +slave_thread_opts: + { Lex->slave_thd_opt= 0; } + slave_thread_opt_list + {} + ; + +slave_thread_opt_list: + slave_thread_opt + | slave_thread_opt_list ',' slave_thread_opt + ; + +slave_thread_opt: + /*empty*/ {} + | SQL_THREAD { Lex->slave_thd_opt|=SLAVE_SQL; } + | RELAY_THREAD { Lex->slave_thd_opt|=SLAVE_IO; } + ; + +slave_until: + /*empty*/ {} + | UNTIL_SYM slave_until_opts + { + LEX *lex=Lex; + if (((lex->mi.log_file_name || lex->mi.pos) && + (lex->mi.relay_log_name || lex->mi.relay_log_pos)) || + !((lex->mi.log_file_name && lex->mi.pos) || + (lex->mi.relay_log_name && lex->mi.relay_log_pos))) + my_yyabort_error((ER_BAD_SLAVE_UNTIL_COND, MYF(0))); + } + | UNTIL_SYM MASTER_GTID_POS_SYM '=' TEXT_STRING_sys + { + Lex->mi.gtid_pos_str = $4; + } + ; + +slave_until_opts: + master_file_def + | slave_until_opts ',' master_file_def + ; + +checksum: + CHECKSUM_SYM table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_CHECKSUM; + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + table_list opt_checksum_type + {} + ; + +opt_checksum_type: + /* nothing */ { Lex->check_opt.flags= 0; } + | QUICK { Lex->check_opt.flags= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags= T_EXTEND; } + ; + +repair_table_or_view: + table_or_tables table_list opt_mi_repair_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_repair_type + ; + +repair: + REPAIR opt_no_write_to_binlog + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPAIR; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + lex->alter_info.reset(); + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + repair_table_or_view + { + LEX* lex= thd->lex; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_repair_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +opt_mi_repair_type: + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_repair_types {} + ; + +mi_repair_types: + mi_repair_type {} + | mi_repair_type mi_repair_types {} + ; + +mi_repair_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | USE_FRM { Lex->check_opt.sql_flags|= TT_USEFRM; } + ; + +opt_view_repair_type: + /* empty */ { } + | FROM MYSQL_SYM { Lex->check_opt.sql_flags|= TT_FROM_MYSQL; } + ; + +analyze: + ANALYZE_SYM opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_ANALYZE; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + lex->alter_info.reset(); + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + analyze_table_list + { + LEX* lex= thd->lex; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_analyze_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +analyze_table_list: + analyze_table_elem_spec + | analyze_table_list ',' analyze_table_elem_spec + ; + +analyze_table_elem_spec: + table_name opt_persistent_stat_clause + ; + +opt_persistent_stat_clause: + /* empty */ + {} + | PERSISTENT_SYM FOR_SYM persistent_stat_spec + { + thd->lex->with_persistent_for_clause= TRUE; + } + ; + +persistent_stat_spec: + ALL + {} + | COLUMNS persistent_column_stat_spec INDEXES persistent_index_stat_spec + {} + +persistent_column_stat_spec: + ALL {} + | '(' + { + LEX* lex= thd->lex; + lex->column_list= new (thd->mem_root) List<LEX_STRING>; + if (lex->column_list == NULL) + MYSQL_YYABORT; + } + table_column_list + ')' + ; + +persistent_index_stat_spec: + ALL {} + | '(' + { + LEX* lex= thd->lex; + lex->index_list= new (thd->mem_root) List<LEX_STRING>; + if (lex->index_list == NULL) + MYSQL_YYABORT; + } + table_index_list + ')' + ; + +table_column_list: + /* empty */ + {} + | ident + { + Lex->column_list->push_back((LEX_STRING*) + thd->memdup(&$1, sizeof(LEX_STRING)), thd->mem_root); + } + | table_column_list ',' ident + { + Lex->column_list->push_back((LEX_STRING*) + thd->memdup(&$3, sizeof(LEX_STRING)), thd->mem_root); + } + ; + +table_index_list: + /* empty */ + {} + | table_index_name + | table_index_list ',' table_index_name + ; + +table_index_name: + ident + { + Lex->index_list->push_back((LEX_STRING*) + thd->memdup(&$1, sizeof(LEX_STRING)), + thd->mem_root); + } + | + PRIMARY_SYM + { + LEX_STRING str= {(char*) "PRIMARY", 7}; + Lex->index_list->push_back((LEX_STRING*) + thd->memdup(&str, sizeof(LEX_STRING)), + thd->mem_root); + } + ; + +binlog_base64_event: + BINLOG_SYM TEXT_STRING_sys + { + Lex->sql_command = SQLCOM_BINLOG_BASE64_EVENT; + Lex->comment= $2; + } + ; + +check_view_or_table: + table_or_tables table_list opt_mi_check_type + | VIEW_SYM + { Lex->table_type= TABLE_TYPE_VIEW; } + table_list opt_view_check_type + ; + +check: CHECK_SYM + { + LEX *lex=Lex; + + lex->sql_command = SQLCOM_CHECK; + lex->check_opt.init(); + lex->alter_info.reset(); + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + check_view_or_table + { + LEX* lex= thd->lex; + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "CHECK")); + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_check_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +opt_mi_check_type: + /* empty */ { Lex->check_opt.flags = T_MEDIUM; } + | mi_check_types {} + ; + +mi_check_types: + mi_check_type {} + | mi_check_type mi_check_types {} + ; + +mi_check_type: + QUICK { Lex->check_opt.flags|= T_QUICK; } + | FAST_SYM { Lex->check_opt.flags|= T_FAST; } + | MEDIUM_SYM { Lex->check_opt.flags|= T_MEDIUM; } + | EXTENDED_SYM { Lex->check_opt.flags|= T_EXTEND; } + | CHANGED { Lex->check_opt.flags|= T_CHECK_ONLY_CHANGED; } + | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; } + ; + +opt_view_check_type: + /* empty */ { } + | FOR_SYM UPGRADE_SYM { Lex->check_opt.sql_flags|= TT_FOR_UPGRADE; } + ; + +optimize: + OPTIMIZE opt_no_write_to_binlog table_or_tables + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_OPTIMIZE; + lex->no_write_to_binlog= $2; + lex->check_opt.init(); + lex->alter_info.reset(); + /* Will be overridden during execution. */ + YYPS->m_lock_type= TL_UNLOCK; + } + table_list + { + LEX* lex= thd->lex; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_optimize_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + ; + +opt_no_write_to_binlog: + /* empty */ { $$= 0; } + | NO_WRITE_TO_BINLOG { $$= 1; } + | LOCAL_SYM { $$= 1; } + ; + +rename: + RENAME table_or_tables + { + Lex->sql_command= SQLCOM_RENAME_TABLE; + } + table_to_table_list + {} + | RENAME USER_SYM clear_privileges rename_list + { + Lex->sql_command = SQLCOM_RENAME_USER; + } + ; + +rename_list: + user TO_SYM user + { + if (Lex->users_list.push_back($1, thd->mem_root) || + Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + | rename_list ',' user TO_SYM user + { + if (Lex->users_list.push_back($3, thd->mem_root) || + Lex->users_list.push_back($5, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +table_to_table_list: + table_to_table + | table_to_table_list ',' table_to_table + ; + +table_to_table: + table_ident TO_SYM table_ident + { + LEX *lex=Lex; + SELECT_LEX *sl= lex->current_select; + if (!sl->add_table_to_list(thd, $1,NULL,TL_OPTION_UPDATING, + TL_IGNORE, MDL_EXCLUSIVE) || + !sl->add_table_to_list(thd, $3,NULL,TL_OPTION_UPDATING, + TL_IGNORE, MDL_EXCLUSIVE)) + MYSQL_YYABORT; + } + ; + +keycache: + CACHE_SYM INDEX_SYM + { + Lex->alter_info.reset(); + } + keycache_list_or_parts IN_SYM key_cache_name + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ASSIGN_TO_KEYCACHE; + lex->ident= $6; + } + ; + +keycache_list_or_parts: + keycache_list + | assign_to_keycache_parts + ; + +keycache_list: + assign_to_keycache + | keycache_list ',' assign_to_keycache + ; + +assign_to_keycache: + table_ident cache_keys_spec + { + if (!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ, + MDL_SHARED_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + +assign_to_keycache_parts: + table_ident adm_partition cache_keys_spec + { + if (!Select->add_table_to_list(thd, $1, NULL, 0, TL_READ, + MDL_SHARED_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + +key_cache_name: + ident { $$= $1; } + | DEFAULT { $$ = default_key_cache_base; } + ; + +preload: + LOAD INDEX_SYM INTO CACHE_SYM + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_PRELOAD_KEYS; + lex->alter_info.reset(); + } + preload_list_or_parts + {} + ; + +preload_list_or_parts: + preload_keys_parts + | preload_list + ; + +preload_list: + preload_keys + | preload_list ',' preload_keys + ; + +preload_keys: + table_ident cache_keys_spec opt_ignore_leaves + { + if (!Select->add_table_to_list(thd, $1, NULL, $3, TL_READ, + MDL_SHARED_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + +preload_keys_parts: + table_ident adm_partition cache_keys_spec opt_ignore_leaves + { + if (!Select->add_table_to_list(thd, $1, NULL, $4, TL_READ, + MDL_SHARED_READ, + Select->pop_index_hints())) + MYSQL_YYABORT; + } + ; + +adm_partition: + PARTITION_SYM have_partitioning + { + Lex->alter_info.flags|= Alter_info::ALTER_ADMIN_PARTITION; + } + '(' all_or_alt_part_name_list ')' + ; + +cache_keys_spec: + { + Lex->select_lex.alloc_index_hints(thd); + Select->set_index_hint_type(INDEX_HINT_USE, + INDEX_HINT_MASK_ALL); + } + cache_key_list_or_empty + ; + +cache_key_list_or_empty: + /* empty */ { } + | key_or_index '(' opt_key_usage_list ')' + ; + +opt_ignore_leaves: + /* empty */ + { $$= 0; } + | IGNORE_SYM LEAVES { $$= TL_OPTION_IGNORE_LEAVES; } + ; + +/* + Select : retrieve data from table +*/ + + +select: + opt_with_clause select_init + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SELECT; + lex->current_select->set_with_clause($1); + } + ; + +select_init: + SELECT_SYM select_options_and_item_list select_init3 + | '(' select_paren ')' + | '(' select_paren ')' union_list + | '(' select_paren ')' union_order_or_limit + ; + +union_list_part2: + SELECT_SYM select_options_and_item_list select_init3_union_query_term + | '(' select_paren_union_query_term ')' + | '(' select_paren_union_query_term ')' union_list + | '(' select_paren_union_query_term ')' union_order_or_limit + ; + +select_paren: + { + /* + In order to correctly parse UNION's global ORDER BY we need to + set braces before parsing the clause. + */ + Lex->current_select->set_braces(true); + } + SELECT_SYM select_options_and_item_list select_part3 + opt_select_lock_type + { + DBUG_ASSERT(Lex->current_select->braces); + } + | '(' select_paren ')' + ; + +select_paren_union_query_term: + { + /* + In order to correctly parse UNION's global ORDER BY we need to + set braces before parsing the clause. + */ + Lex->current_select->set_braces(true); + } + SELECT_SYM select_options_and_item_list select_part3_union_query_term + opt_select_lock_type + { + DBUG_ASSERT(Lex->current_select->braces); + } + | '(' select_paren_union_query_term ')' + ; + +select_paren_view: + { + /* + In order to correctly parse UNION's global ORDER BY we need to + set braces before parsing the clause. + */ + Lex->current_select->set_braces(true); + } + SELECT_SYM select_options_and_item_list select_part3_view + opt_select_lock_type + { + DBUG_ASSERT(Lex->current_select->braces); + } + | '(' select_paren_view ')' + ; + +/* The equivalent of select_paren for nested queries. */ +select_paren_derived: + { + Lex->current_select->set_braces(true); + } + SELECT_SYM select_part2_derived + opt_table_expression + opt_order_clause + opt_limit_clause + opt_select_lock_type + { + DBUG_ASSERT(Lex->current_select->braces); + $$= Lex->current_select->master_unit()->first_select(); + } + | '(' select_paren_derived ')' { $$= $2; } + ; + +select_init3: + opt_table_expression + opt_select_lock_type + { + /* Parentheses carry no meaning here */ + Lex->current_select->set_braces(false); + } + union_clause + | select_part3_union_not_ready + opt_select_lock_type + { + /* Parentheses carry no meaning here */ + Lex->current_select->set_braces(false); + } + ; + + +select_init3_union_query_term: + opt_table_expression + opt_select_lock_type + { + /* Parentheses carry no meaning here */ + Lex->current_select->set_braces(false); + } + union_clause + | select_part3_union_not_ready_noproc + opt_select_lock_type + { + /* Parentheses carry no meaning here */ + Lex->current_select->set_braces(false); + } + ; + + +select_init3_view: + opt_table_expression opt_select_lock_type + { + Lex->current_select->set_braces(false); + } + | opt_table_expression opt_select_lock_type + { + Lex->current_select->set_braces(false); + } + union_list_view + | order_or_limit opt_select_lock_type + { + Lex->current_select->set_braces(false); + } + | table_expression order_or_limit opt_select_lock_type + { + Lex->current_select->set_braces(false); + } + ; + +/* + The SELECT parts after select_item_list that cannot be followed by UNION. +*/ + +select_part3: + opt_table_expression + | select_part3_union_not_ready + ; + +select_part3_union_query_term: + opt_table_expression + | select_part3_union_not_ready_noproc + ; + +select_part3_view: + opt_table_expression + | order_or_limit + | table_expression order_or_limit + ; + +select_part3_union_not_ready: + select_part3_union_not_ready_noproc + | table_expression procedure_clause + | table_expression order_or_limit procedure_clause + ; + +select_part3_union_not_ready_noproc: + order_or_limit + | into opt_table_expression opt_order_clause opt_limit_clause + | table_expression into + | table_expression order_or_limit + | table_expression order_or_limit into + ; + +select_options_and_item_list: + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + if (sel->linkage != UNION_TYPE) + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + ; + + +/** + <table expression>, as in the SQL standard. +*/ +table_expression: + from_clause + opt_where_clause + opt_group_clause + opt_having_clause + opt_window_clause + ; + +opt_table_expression: + /* Empty */ + | table_expression + ; + +from_clause: + FROM table_reference_list + ; + +table_reference_list: + join_table_list + { + Select->context.table_list= + Select->context.first_name_resolution_table= + Select->table_list.first; + } + | DUAL_SYM + /* oracle compatibility: oracle always requires FROM clause, + and DUAL is system table without fields. + Is "SELECT 1 FROM DUAL" any better than "SELECT 1" ? + Hmmm :) */ + ; + +select_options: + /* empty*/ + | select_option_list + { + if (Select->options & SELECT_DISTINCT && Select->options & SELECT_ALL) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "ALL", "DISTINCT")); + } + ; + +select_option_list: + select_option_list select_option + | select_option + ; + +select_option: + query_expression_option + | SQL_NO_CACHE_SYM + { + /* + Allow this flag only on the first top-level SELECT statement, if + SQL_CACHE wasn't specified, and only once per query. + */ + if (Lex->current_select != &Lex->select_lex) + my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_NO_CACHE")); + if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_CACHE", "SQL_NO_CACHE")); + if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_NO_CACHE")); + + Lex->safe_to_cache_query=0; + Lex->select_lex.options&= ~OPTION_TO_QUERY_CACHE; + Lex->select_lex.sql_cache= SELECT_LEX::SQL_NO_CACHE; + } + | SQL_CACHE_SYM + { + /* + Allow this flag only on the first top-level SELECT statement, if + SQL_NO_CACHE wasn't specified, and only once per query. + */ + if (Lex->current_select != &Lex->select_lex) + my_yyabort_error((ER_CANT_USE_OPTION_HERE, MYF(0), "SQL_CACHE")); + if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_NO_CACHE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "SQL_NO_CACHE", "SQL_CACHE")); + if (Lex->select_lex.sql_cache == SELECT_LEX::SQL_CACHE) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SQL_CACHE")); + + Lex->safe_to_cache_query=1; + Lex->select_lex.options|= OPTION_TO_QUERY_CACHE; + Lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE; + } + ; + +opt_select_lock_type: + /* empty */ + | FOR_SYM UPDATE_SYM + { + LEX *lex=Lex; + lex->current_select->lock_type= TL_WRITE; + lex->current_select->set_lock_for_tables(TL_WRITE); + lex->safe_to_cache_query=0; + } + | LOCK_SYM IN_SYM SHARE_SYM MODE_SYM + { + LEX *lex=Lex; + lex->current_select->lock_type= TL_READ_WITH_SHARED_LOCKS; + lex->current_select-> + set_lock_for_tables(TL_READ_WITH_SHARED_LOCKS); + lex->safe_to_cache_query=0; + } + ; + +select_item_list: + select_item_list ',' select_item + | select_item + | '*' + { + Item *item= new (thd->mem_root) + Item_field(thd, &thd->lex->current_select->context, + NULL, NULL, &star_clex_str); + if (item == NULL) + MYSQL_YYABORT; + if (add_item_to_list(thd, item)) + MYSQL_YYABORT; + (thd->lex->current_select->with_wild)++; + } + ; + +select_item: + remember_name table_wild remember_end + { + if (add_item_to_list(thd, $2)) + MYSQL_YYABORT; + } + | remember_name expr remember_end select_alias + { + DBUG_ASSERT($1 < $3); + + if (add_item_to_list(thd, $2)) + MYSQL_YYABORT; + if ($4.str) + { + if (Lex->sql_command == SQLCOM_CREATE_VIEW && + check_column_name($4.str)) + my_yyabort_error((ER_WRONG_COLUMN_NAME, MYF(0), $4.str)); + $2->is_autogenerated_name= FALSE; + $2->set_name(thd, $4.str, $4.length, system_charset_info); + } + else if (!$2->name.str || $2->name.str == item_empty_name) + { + $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); + } + } + ; + +colon_with_pos: + ':' + { + $$= (char *) YYLIP->get_tok_start(); + } + ; + +remember_tok_start: + { + $$= (char*) YYLIP->get_tok_start(); + } + ; + +remember_name: + { + $$= (char*) YYLIP->get_cpp_tok_start(); + } + ; + +remember_end: + { + $$= (char*) YYLIP->get_cpp_tok_end_rtrim(); + } + ; + +select_alias: + /* empty */ { $$=null_clex_str;} + | AS ident { $$=$2; } + | AS TEXT_STRING_sys { $$=$2; } + | ident { $$=$1; } + | TEXT_STRING_sys { $$=$1; } + ; + +opt_default_time_precision: + /* empty */ { $$= NOT_FIXED_DEC; } + | '(' ')' { $$= NOT_FIXED_DEC; } + | '(' real_ulong_num ')' { $$= $2; }; + ; + +opt_time_precision: + /* empty */ { $$= 0; } + | '(' ')' { $$= 0; } + | '(' real_ulong_num ')' { $$= $2; }; + ; + +optional_braces: + /* empty */ {} + | '(' ')' {} + ; + +/* all possible expressions */ +expr: + expr or expr %prec OR_SYM + { + /* + Design notes: + Do not use a manually maintained stack like thd->lex->xxx_list, + but use the internal bison stack ($$, $1 and $3) instead. + Using the bison stack is: + - more robust to changes in the grammar, + - guaranteed to be in sync with the parser state, + - better for performances (no memory allocation). + */ + Item_cond_or *item1; + Item_cond_or *item3; + if (is_cond_or($1)) + { + item1= (Item_cond_or*) $1; + if (is_cond_or($3)) + { + item3= (Item_cond_or*) $3; + /* + (X1 OR X2) OR (Y1 OR Y2) ==> OR (X1, X2, Y1, Y2) + */ + item3->add_at_head(item1->argument_list()); + $$ = $3; + } + else + { + /* + (X1 OR X2) OR Y ==> OR (X1, X2, Y) + */ + item1->add($3, thd->mem_root); + $$ = $1; + } + } + else if (is_cond_or($3)) + { + item3= (Item_cond_or*) $3; + /* + X OR (Y1 OR Y2) ==> OR (X, Y1, Y2) + */ + item3->add_at_head($1, thd->mem_root); + $$ = $3; + } + else + { + /* X OR Y */ + $$= new (thd->mem_root) Item_cond_or(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + } + | expr XOR expr %prec XOR + { + /* XOR is a proprietary extension */ + $$= new (thd->mem_root) Item_func_xor(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | expr and expr %prec AND_SYM + { + /* See comments in rule expr: expr or expr */ + Item_cond_and *item1; + Item_cond_and *item3; + if (is_cond_and($1)) + { + item1= (Item_cond_and*) $1; + if (is_cond_and($3)) + { + item3= (Item_cond_and*) $3; + /* + (X1 AND X2) AND (Y1 AND Y2) ==> AND (X1, X2, Y1, Y2) + */ + item3->add_at_head(item1->argument_list()); + $$ = $3; + } + else + { + /* + (X1 AND X2) AND Y ==> AND (X1, X2, Y) + */ + item1->add($3, thd->mem_root); + $$ = $1; + } + } + else if (is_cond_and($3)) + { + item3= (Item_cond_and*) $3; + /* + X AND (Y1 AND Y2) ==> AND (X, Y1, Y2) + */ + item3->add_at_head($1, thd->mem_root); + $$ = $3; + } + else + { + /* X AND Y */ + $$= new (thd->mem_root) Item_cond_and(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + } + | NOT_SYM expr %prec NOT_SYM + { + $$= negate_expression(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS TRUE_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_istrue(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS not TRUE_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnottrue(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS FALSE_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isfalse(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS not FALSE_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnotfalse(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS UNKNOWN_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnull(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS not UNKNOWN_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnotnull(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri + ; + +bool_pri: + bool_pri IS NULL_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnull(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri IS not NULL_SYM %prec IS + { + $$= new (thd->mem_root) Item_func_isnotnull(thd, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri EQUAL_SYM predicate %prec EQUAL_SYM + { + $$= new (thd->mem_root) Item_func_equal(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri comp_op predicate %prec '=' + { + $$= (*$2)(0)->create(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bool_pri comp_op all_or_any '(' subselect ')' %prec '=' + { + $$= all_any_subquery_creator(thd, $1, $2, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | predicate + ; + +predicate: + bit_expr IN_SYM '(' subselect ')' + { + $$= new (thd->mem_root) Item_in_subselect(thd, $1, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not IN_SYM '(' subselect ')' + { + Item *item= new (thd->mem_root) Item_in_subselect(thd, $1, $5); + if (item == NULL) + MYSQL_YYABORT; + $$= negate_expression(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr IN_SYM '(' expr ')' + { + $$= handle_sql2003_note184_exception(thd, $1, true, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr IN_SYM '(' expr ',' expr_list ')' + { + $6->push_front($4, thd->mem_root); + $6->push_front($1, thd->mem_root); + $$= new (thd->mem_root) Item_func_in(thd, *$6); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not IN_SYM '(' expr ')' + { + $$= handle_sql2003_note184_exception(thd, $1, false, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not IN_SYM '(' expr ',' expr_list ')' + { + $7->push_front($5, thd->mem_root); + $7->push_front($1, thd->mem_root); + Item_func_in *item= new (thd->mem_root) Item_func_in(thd, *$7); + if (item == NULL) + MYSQL_YYABORT; + $$= item->neg_transformer(thd); + } + | bit_expr BETWEEN_SYM bit_expr AND_SYM predicate + { + $$= new (thd->mem_root) Item_func_between(thd, $1, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not BETWEEN_SYM bit_expr AND_SYM predicate + { + Item_func_between *item; + item= new (thd->mem_root) Item_func_between(thd, $1, $4, $6); + if (item == NULL) + MYSQL_YYABORT; + $$= item->neg_transformer(thd); + } + | bit_expr SOUNDS_SYM LIKE bit_expr + { + Item *item1= new (thd->mem_root) Item_func_soundex(thd, $1); + Item *item4= new (thd->mem_root) Item_func_soundex(thd, $4); + if ((item1 == NULL) || (item4 == NULL)) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_func_eq(thd, item1, item4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr LIKE simple_expr opt_escape + { + $$= new (thd->mem_root) Item_func_like(thd, $1, $3, $4, + Lex->escape_used); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not LIKE simple_expr opt_escape + { + Item *item= new (thd->mem_root) Item_func_like(thd, $1, $4, $5, + Lex->escape_used); + if (item == NULL) + MYSQL_YYABORT; + $$= item->neg_transformer(thd); + } + | bit_expr REGEXP bit_expr + { + $$= new (thd->mem_root) Item_func_regex(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr not REGEXP bit_expr + { + Item *item= new (thd->mem_root) Item_func_regex(thd, $1, $4); + if (item == NULL) + MYSQL_YYABORT; + $$= negate_expression(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr + ; + +bit_expr: + bit_expr '|' bit_expr %prec '|' + { + $$= new (thd->mem_root) Item_func_bit_or(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '&' bit_expr %prec '&' + { + $$= new (thd->mem_root) Item_func_bit_and(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr SHIFT_LEFT bit_expr %prec SHIFT_LEFT + { + $$= new (thd->mem_root) Item_func_shift_left(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr SHIFT_RIGHT bit_expr %prec SHIFT_RIGHT + { + $$= new (thd->mem_root) Item_func_shift_right(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '+' bit_expr %prec '+' + { + $$= new (thd->mem_root) Item_func_plus(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '-' bit_expr %prec '-' + { + $$= new (thd->mem_root) Item_func_minus(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '+' INTERVAL_SYM expr interval %prec '+' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $1, $4, $5, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '-' INTERVAL_SYM expr interval %prec '-' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $1, $4, $5, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '*' bit_expr %prec '*' + { + $$= new (thd->mem_root) Item_func_mul(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '/' bit_expr %prec '/' + { + $$= new (thd->mem_root) Item_func_div(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr DIV_SYM bit_expr %prec DIV_SYM + { + $$= new (thd->mem_root) Item_func_int_div(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr MOD_SYM bit_expr %prec MOD_SYM + { + $$= new (thd->mem_root) Item_func_mod(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | bit_expr '^' bit_expr + { + $$= new (thd->mem_root) Item_func_bit_xor(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | simple_expr + ; + +or: + OR_SYM + | OR2_SYM + ; + +and: + AND_SYM + | AND_AND_SYM + ; + +not: + NOT_SYM + | NOT2_SYM + ; + +not2: + '!' + | NOT2_SYM + ; + +comp_op: + '=' { $$ = &comp_eq_creator; } + | GE { $$ = &comp_ge_creator; } + | '>' { $$ = &comp_gt_creator; } + | LE { $$ = &comp_le_creator; } + | '<' { $$ = &comp_lt_creator; } + | NE { $$ = &comp_ne_creator; } + ; + +all_or_any: + ALL { $$ = 1; } + | ANY_SYM { $$ = 0; } + ; + +opt_dyncol_type: + /* empty */ + { + $$.set(DYN_COL_NULL); /* automatic type */ + Lex->charset= NULL; + } + | AS dyncol_type { $$= $2; } + ; + +dyncol_type: + numeric_dyncol_type { $$= $1; Lex->charset= NULL; } + | temporal_dyncol_type { $$= $1; Lex->charset= NULL; } + | string_dyncol_type { $$= $1; } + ; + +numeric_dyncol_type: + INT_SYM { $$.set(DYN_COL_INT); } + | UNSIGNED INT_SYM { $$.set(DYN_COL_UINT); } + | DOUBLE_SYM { $$.set(DYN_COL_DOUBLE); } + | REAL { $$.set(DYN_COL_DOUBLE); } + | FLOAT_SYM { $$.set(DYN_COL_DOUBLE); } + | DECIMAL_SYM float_options { $$.set(DYN_COL_DECIMAL, $2); } + ; + +temporal_dyncol_type: + DATE_SYM { $$.set(DYN_COL_DATE); } + | TIME_SYM opt_field_length { $$.set(DYN_COL_TIME, 0, $2); } + | DATETIME opt_field_length { $$.set(DYN_COL_DATETIME, 0, $2); } + ; + +string_dyncol_type: + char + { Lex->charset= thd->variables.collation_connection; } + opt_binary + { + $$.set(DYN_COL_STRING); + } + | nchar + { + $$.set(DYN_COL_STRING); + Lex->charset= national_charset_info; + } + ; + +dyncall_create_element: + expr ',' expr opt_dyncol_type + { + LEX *lex= Lex; + $$= (DYNCALL_CREATE_DEF *) + alloc_root(thd->mem_root, sizeof(DYNCALL_CREATE_DEF)); + if ($$ == NULL) + MYSQL_YYABORT; + $$->key= $1; + $$->value= $3; + $$->type= (DYNAMIC_COLUMN_TYPE)$4.dyncol_type(); + $$->cs= lex->charset; + if ($4.length()) + $$->len= strtoul($4.length(), NULL, 10); + else + $$->len= 0; + if ($4.dec()) + $$->frac= strtoul($4.dec(), NULL, 10); + else + $$->len= 0; + } + +dyncall_create_list: + dyncall_create_element + { + $$= new (thd->mem_root) List<DYNCALL_CREATE_DEF>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | dyncall_create_list ',' dyncall_create_element + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + +sp_cursor_name_and_offset: + ident + { + LEX *lex= Lex; + $$.name= $1; + if (!lex->spcont || + !lex->spcont->find_cursor(&$1, &$$.offset, false)) + my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $1.str)); + } + ; + +explicit_cursor_attr: + sp_cursor_name_and_offset '%' ISOPEN_SYM + { + if (!($$= new (thd->mem_root) + Item_func_cursor_isopen(thd, &$1.name, $1.offset))) + MYSQL_YYABORT; + } + | sp_cursor_name_and_offset '%' FOUND_SYM + { + if (!($$= new (thd->mem_root) + Item_func_cursor_found(thd, &$1.name, $1.offset))) + MYSQL_YYABORT; + } + | sp_cursor_name_and_offset '%' NOTFOUND_SYM + { + if (!($$= new (thd->mem_root) + Item_func_cursor_notfound(thd, &$1.name, $1.offset))) + MYSQL_YYABORT; + } + | sp_cursor_name_and_offset '%' ROWCOUNT_SYM + { + if (!($$= new (thd->mem_root) + Item_func_cursor_rowcount(thd, &$1.name, $1.offset))) + MYSQL_YYABORT; + } + ; + +/* + Expressions that the parser allows in a column DEFAULT clause + without parentheses. These expressions cannot end with a COLLATE clause. + + If we allowed any "expr" in DEFAULT clause, there would be a confusion + in queries like this: + CREATE TABLE t1 (a TEXT DEFAULT 'a' COLLATE latin1_bin); + It would be not clear what COLLATE stands for: + - the collation of the column `a`, or + - the collation of the string literal 'a' + + This restriction allows to parse the above query unambiguiusly: + COLLATE belongs to the column rather than the literal. + If one needs COLLATE to belong to the literal, parentheses must be used: + CREATE TABLE t1 (a TEXT DEFAULT ('a' COLLATE latin1_bin)); + Note: the COLLATE clause is rather meaningless here, but the query + is syntactically correct. + + Note, some of the expressions are not actually allowed in DEFAULT, + e.g. sum_expr, window_func_expr, ROW(...), VALUES(). + We could move them to simple_expr, but that would make + these two queries return a different error messages: + CREATE TABLE t1 (a INT DEFAULT AVG(1)); + CREATE TABLE t1 (a INT DEFAULT (AVG(1))); + The first query would return "syntax error". + Currenly both return: + Function or expression 'avg(' is not allowed for 'DEFAULT' ... +*/ +column_default_non_parenthesized_expr: + simple_ident + | function_call_keyword + | function_call_nonkeyword + | function_call_generic + | function_call_conflict + | literal + | param_marker { $$= $1; } + | variable + | sum_expr + | window_func_expr + | ROW_SYM '(' expr ',' expr_list ')' + { + $5->push_front($3, thd->mem_root); + $$= new (thd->mem_root) Item_row(thd, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | EXISTS '(' subselect ')' + { + $$= new (thd->mem_root) Item_exists_subselect(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '{' ident expr '}' + { + $$= NULL; + /* + If "expr" is reasonably short pure ASCII string literal, + try to parse known ODBC style date, time or timestamp literals, + e.g: + SELECT {d'2001-01-01'}; + SELECT {t'10:20:30'}; + SELECT {ts'2001-01-01 10:20:30'}; + */ + if ($3->type() == Item::STRING_ITEM) + { + Item_string *item= (Item_string *) $3; + enum_field_types type= item->odbc_temporal_literal_type(&$2); + if (type != MYSQL_TYPE_STRING) + { + $$= create_temporal_literal(thd, item->val_str(NULL), + type, false); + } + } + if ($$ == NULL) + $$= $3; + } + | MATCH ident_list_arg AGAINST '(' bit_expr fulltext_options ')' + { + $2->push_front($5, thd->mem_root); + Item_func_match *i1= new (thd->mem_root) Item_func_match(thd, *$2, + $6); + if (i1 == NULL) + MYSQL_YYABORT; + Select->add_ftfunc_to_list(thd, i1); + $$= i1; + } + | CAST_SYM '(' expr AS cast_type ')' + { + if (!($$= $5.create_typecast_item(thd, $3, Lex->charset))) + MYSQL_YYABORT; + } + | CASE_SYM when_list_opt_else END + { + if (!($$= new(thd->mem_root) Item_func_case_searched(thd, *$2))) + MYSQL_YYABORT; + } + | CASE_SYM expr when_list_opt_else END + { + $3->push_front($2, thd->mem_root); + if (!($$= new (thd->mem_root) Item_func_case_simple(thd, *$3))) + MYSQL_YYABORT; + } + | CONVERT_SYM '(' expr ',' cast_type ')' + { + if (!($$= $5.create_typecast_item(thd, $3, Lex->charset))) + MYSQL_YYABORT; + } + | CONVERT_SYM '(' expr USING charset_name ')' + { + $$= new (thd->mem_root) Item_func_conv_charset(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DATE_FORMAT_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DATE_FORMAT_SYM '(' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_date_format(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DECODE_SYM '(' expr ',' decode_when_list ')' + { + $5->push_front($3, thd->mem_root); + if (!($$= new (thd->mem_root) Item_func_decode_oracle(thd, *$5))) + MYSQL_YYABORT; + } + | DEFAULT '(' simple_ident ')' + { + Item_splocal *il= $3->get_item_splocal(); + if (il) + my_yyabort_error((ER_WRONG_COLUMN_NAME, MYF(0), il->my_name()->str)); + $$= new (thd->mem_root) Item_default_value(thd, Lex->current_context(), + $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | VALUES '(' simple_ident_nospvar ')' + { + $$= new (thd->mem_root) Item_insert_value(thd, Lex->current_context(), + $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | NEXT_SYM VALUE_SYM FOR_SYM table_ident + { + if (!($$= Lex->create_item_func_nextval(thd, $4))) + MYSQL_YYABORT; + } + | NEXTVAL_SYM '(' table_ident ')' + { + if (!($$= Lex->create_item_func_nextval(thd, $3))) + MYSQL_YYABORT; + } + | PREVIOUS_SYM VALUE_SYM FOR_SYM table_ident + { + if (!($$= Lex->create_item_func_lastval(thd, $4))) + MYSQL_YYABORT; + } + | LASTVAL_SYM '(' table_ident ')' + { + if (!($$= Lex->create_item_func_lastval(thd, $3))) + MYSQL_YYABORT; + } + | SETVAL_SYM '(' table_ident ',' longlong_num ')' + { + if (!($$= Lex->create_item_func_setval(thd, $3, $5, 0, 1))) + MYSQL_YYABORT; + } + | SETVAL_SYM '(' table_ident ',' longlong_num ',' bool ')' + { + if (!($$= Lex->create_item_func_setval(thd, $3, $5, 0, $7))) + MYSQL_YYABORT; + } + | SETVAL_SYM '(' table_ident ',' longlong_num ',' bool ',' ulonglong_num ')' + { + if (!($$= Lex->create_item_func_setval(thd, $3, $5, $9, $7))) + MYSQL_YYABORT; + } + ; + +simple_expr: + column_default_non_parenthesized_expr + | explicit_cursor_attr + | simple_expr COLLATE_SYM ident_or_text %prec NEG + { + Item *i1= new (thd->mem_root) Item_string(thd, $3.str, + $3.length, + thd->charset()); + if (i1 == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_func_set_collation(thd, $1, i1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '(' parenthesized_expr ')' { $$= $2; } + | BINARY simple_expr %prec NEG + { + Type_cast_attributes at(&my_charset_bin); + if (!($$= type_handler_long_blob.create_typecast_item(thd, $2, at))) + MYSQL_YYABORT; + } + | simple_expr OR_OR_SYM simple_expr + { + $$= new (thd->mem_root) Item_func_concat_operator_oracle(thd, + $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '+' simple_expr %prec NEG + { + $$= $2; + } + | '-' simple_expr %prec NEG + { + $$= $2->neg(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '~' simple_expr %prec NEG + { + $$= new (thd->mem_root) Item_func_bit_neg(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | not2 simple_expr %prec NEG + { + $$= negate_expression(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | INTERVAL_SYM expr interval '+' expr %prec INTERVAL_SYM + /* we cannot put interval before - */ + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $5, $2, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* + Function call syntax using official SQL 2003 keywords. + Because the function name is an official token, + a dedicated grammar rule is needed in the parser. + There is no potential for conflicts +*/ +function_call_keyword: + CHAR_SYM '(' expr_list ')' + { + $$= new (thd->mem_root) Item_func_char(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CHAR_SYM '(' expr_list USING charset_name ')' + { + $$= new (thd->mem_root) Item_func_char(thd, *$3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CURRENT_USER optional_braces + { + $$= new (thd->mem_root) Item_func_current_user(thd, + Lex->current_context()); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query= 0; + } + | CURRENT_ROLE optional_braces + { + $$= new (thd->mem_root) Item_func_current_role(thd, + Lex->current_context()); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query= 0; + } + | DATE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_date_typecast(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DAY_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_dayofmonth(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | HOUR_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_hour(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | INSERT '(' expr ',' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_insert(thd, $3, $5, $7, $9); + if ($$ == NULL) + MYSQL_YYABORT; + } + | INTERVAL_SYM '(' expr ',' expr ')' %prec INTERVAL_SYM + { + List<Item> *list= new (thd->mem_root) List<Item>; + if (list == NULL) + MYSQL_YYABORT; + list->push_front($5, thd->mem_root); + list->push_front($3, thd->mem_root); + Item_row *item= new (thd->mem_root) Item_row(thd, *list); + if (item == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_func_interval(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | INTERVAL_SYM '(' expr ',' expr ',' expr_list ')' %prec INTERVAL_SYM + { + $7->push_front($5, thd->mem_root); + $7->push_front($3, thd->mem_root); + Item_row *item= new (thd->mem_root) Item_row(thd, *$7); + if (item == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_func_interval(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | LEFT '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_left(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MINUTE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_minute(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MONTH_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_month(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | RIGHT '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_right(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SECOND_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_second(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SQL_SYM '%' ROWCOUNT_SYM + { + $$= new (thd->mem_root) Item_func_oracle_sql_rowcount(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query= 0; + } + | TIME_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_time_typecast(thd, $3, + AUTO_SEC_PART_DIGITS); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TIMESTAMP '(' expr ')' + { + $$= new (thd->mem_root) Item_datetime_typecast(thd, $3, + AUTO_SEC_PART_DIGITS); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TIMESTAMP '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_add_time(thd, $3, $5, 1, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_trim(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' LEADING expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_ltrim(thd, $6, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' TRAILING expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_rtrim(thd, $6, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' BOTH expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_trim(thd, $6, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' LEADING FROM expr ')' + { + $$= new (thd->mem_root) Item_func_ltrim(thd, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' TRAILING FROM expr ')' + { + $$= new (thd->mem_root) Item_func_rtrim(thd, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' BOTH FROM expr ')' + { + $$= new (thd->mem_root) Item_func_trim(thd, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRIM '(' expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_trim(thd, $5, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | USER_SYM '(' ')' + { + $$= new (thd->mem_root) Item_func_user(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query=0; + } + | YEAR_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_year(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* + Function calls using non reserved keywords, with special syntaxic forms. + Dedicated grammar rules are needed because of the syntax, + but also have the potential to cause incompatibilities with other + parts of the language. + MAINTAINER: + The only reasons a function should be added here are: + - for compatibility reasons with another SQL syntax (CURDATE), + - for typing reasons (GET_FORMAT) + Any other 'Syntaxic sugar' enhancements should be *STRONGLY* + discouraged. +*/ +function_call_nonkeyword: + ADDDATE_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5, + INTERVAL_DAY, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CURDATE optional_braces + { + $$= new (thd->mem_root) Item_func_curdate_local(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | CURTIME opt_time_precision + { + $$= new (thd->mem_root) Item_func_curtime_local(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | DATE_ADD_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' + %prec INTERVAL_SYM + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DATE_SUB_INTERVAL '(' expr ',' INTERVAL_SYM expr interval ')' + %prec INTERVAL_SYM + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | EXTRACT_SYM '(' interval FROM expr ')' + { + $$=new (thd->mem_root) Item_extract(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | GET_FORMAT '(' date_time_type ',' expr ')' + { + $$= new (thd->mem_root) Item_func_get_format(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | NOW_SYM opt_time_precision + { + $$= new (thd->mem_root) Item_func_now_local(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | POSITION_SYM '(' bit_expr IN_SYM expr ')' + { + $$= new (thd->mem_root) Item_func_locate(thd, $5, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBDATE_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $5, + INTERVAL_DAY, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $3, $6, $7, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBSTRING '(' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBSTRING '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_substr(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBSTRING '(' expr FROM expr FOR_SYM expr ')' + { + $$= new (thd->mem_root) Item_func_substr(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUBSTRING '(' expr FROM expr ')' + { + $$= new (thd->mem_root) Item_func_substr(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SYSDATE opt_time_precision + { + /* + Unlike other time-related functions, SYSDATE() is + replication-unsafe because it is not affected by the + TIMESTAMP variable. It is unsafe even if + sysdate_is_now=1, because the slave may have + sysdate_is_now=0. + */ + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + if (global_system_variables.sysdate_is_now == 0) + $$= new (thd->mem_root) Item_func_sysdate_local(thd, $2); + else + $$= new (thd->mem_root) Item_func_now_local(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | TIMESTAMP_ADD '(' interval_time_stamp ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_date_add_interval(thd, $7, $5, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TIMESTAMP_DIFF '(' interval_time_stamp ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_timestamp_diff(thd, $5, $7, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | UTC_DATE_SYM optional_braces + { + $$= new (thd->mem_root) Item_func_curdate_utc(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | UTC_TIME_SYM opt_time_precision + { + $$= new (thd->mem_root) Item_func_curtime_utc(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | UTC_TIMESTAMP_SYM opt_time_precision + { + $$= new (thd->mem_root) Item_func_now_utc(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | + COLUMN_ADD_SYM '(' expr ',' dyncall_create_list ')' + { + $$= create_func_dyncol_add(thd, $3, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_DELETE_SYM '(' expr ',' expr_list ')' + { + $$= create_func_dyncol_delete(thd, $3, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_CHECK_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_dyncol_check(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_CREATE_SYM '(' dyncall_create_list ')' + { + $$= create_func_dyncol_create(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + COLUMN_GET_SYM '(' expr ',' expr AS cast_type ')' + { + LEX *lex= Lex; + $$= create_func_dyncol_get(thd, $3, $5, $7.type_handler(), + $7.length(), $7.dec(), + lex->charset); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* + Functions calls using a non reserved keyword, and using a regular syntax. + Because the non reserved keyword is used in another part of the grammar, + a dedicated rule is needed here. +*/ +function_call_conflict: + ASCII_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_ascii(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CHARSET '(' expr ')' + { + $$= new (thd->mem_root) Item_func_charset(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COALESCE '(' expr_list ')' + { + $$= new (thd->mem_root) Item_func_coalesce(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COLLATION_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_collation(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DATABASE '(' ')' + { + $$= new (thd->mem_root) Item_func_database(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->safe_to_cache_query=0; + } + | IF_SYM '(' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_if(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + | FORMAT_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_format(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | FORMAT_SYM '(' expr ',' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_format(thd, $3, $5, $7); + if ($$ == NULL) + MYSQL_YYABORT; + } + /* LAST_VALUE here conflicts with the definition for window functions. + We have these 2 separate rules to remove the shift/reduce conflict. + */ + | LAST_VALUE '(' expr ')' + { + List<Item> *list= new (thd->mem_root) List<Item>; + if (list == NULL) + MYSQL_YYABORT; + list->push_back($3, thd->mem_root); + + $$= new (thd->mem_root) Item_func_last_value(thd, *list); + if ($$ == NULL) + MYSQL_YYABORT; + } + | LAST_VALUE '(' expr_list ',' expr ')' + { + $3->push_back($5, thd->mem_root); + $$= new (thd->mem_root) Item_func_last_value(thd, *$3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MICROSECOND_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_microsecond(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MOD_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_mod(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | OLD_PASSWORD_SYM '(' expr ')' + { + $$= new (thd->mem_root) + Item_func_password(thd, $3, Item_func_password::OLD); + if ($$ == NULL) + MYSQL_YYABORT; + } + | PASSWORD_SYM '(' expr ')' + { + Item* i1; + i1= new (thd->mem_root) Item_func_password(thd, $3); + if (i1 == NULL) + MYSQL_YYABORT; + $$= i1; + } + | QUARTER_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_quarter(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | REPEAT_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_repeat(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | REPLACE '(' expr ',' expr ',' expr ')' + { + if (!($$= Lex->make_item_func_replace(thd, $3, $5, $7))) + MYSQL_YYABORT; + } + | REVERSE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_reverse(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ROW_COUNT_SYM '(' ')' + { + $$= new (thd->mem_root) Item_func_row_count(thd); + if ($$ == NULL) + MYSQL_YYABORT; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_FUNCTION); + Lex->safe_to_cache_query= 0; + } + | TRUNCATE_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_round(thd, $3, $5, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEEK_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_func_week(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEEK_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_func_week(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEIGHT_STRING_SYM '(' expr opt_ws_levels ')' + { + $$= new (thd->mem_root) Item_func_weight_string(thd, $3, 0, 0, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEIGHT_STRING_SYM '(' expr AS CHAR_SYM ws_nweights opt_ws_levels ')' + { + $$= new (thd->mem_root) + Item_func_weight_string(thd, $3, 0, $6, + $7 | MY_STRXFRM_PAD_WITH_SPACE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEIGHT_STRING_SYM '(' expr AS BINARY ws_nweights ')' + { + Item *item= new (thd->mem_root) Item_char_typecast(thd, $3, $6, + &my_charset_bin); + if (item == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) + Item_func_weight_string(thd, item, 0, $6, + MY_STRXFRM_PAD_WITH_SPACE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | WEIGHT_STRING_SYM '(' expr ',' ulong_num ',' ulong_num ',' ulong_num ')' + { + $$= new (thd->mem_root) Item_func_weight_string(thd, $3, $5, $7, + $9); + if ($$ == NULL) + MYSQL_YYABORT; + } + | geometry_function + { +#ifdef HAVE_SPATIAL + $$= $1; + /* $1 may be NULL, GEOM_NEW not tested for out of memory */ + if ($$ == NULL) + MYSQL_YYABORT; +#else + my_yyabort_error((ER_FEATURE_DISABLED, MYF(0), sym_group_geom.name, + sym_group_geom.needed_define)); +#endif + } + ; + +geometry_function: + CONTAINS_SYM '(' expr ',' expr ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_precise_rel(thd, $3, $5, + Item_func::SP_CONTAINS_FUNC)); + } + | GEOMETRYCOLLECTION '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_geometrycollection, + Geometry::wkb_point)); + } + | LINESTRING '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_linestring, + Geometry::wkb_point)); + } + | MULTILINESTRING '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_multilinestring, + Geometry::wkb_linestring)); + } + | MULTIPOINT '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_multipoint, + Geometry::wkb_point)); + } + | MULTIPOLYGON '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_multipolygon, + Geometry::wkb_polygon)); + } + | POINT_SYM '(' expr ',' expr ')' + { + $$= GEOM_NEW(thd, Item_func_point(thd, $3, $5)); + } + | POLYGON '(' expr_list ')' + { + $$= GEOM_NEW(thd, + Item_func_spatial_collection(thd, *$3, + Geometry::wkb_polygon, + Geometry::wkb_linestring)); + } + ; + +/* + Regular function calls. + The function name is *not* a token, and therefore is guaranteed to not + introduce side effects to the language in general. + MAINTAINER: + All the new functions implemented for new features should fit into + this category. The place to implement the function itself is + in sql/item_create.cc +*/ +function_call_generic: + IDENT_sys '(' + { +#ifdef HAVE_DLOPEN + udf_func *udf= 0; + LEX *lex= Lex; + if (using_udf_functions && + (udf= find_udf($1.str, $1.length)) && + udf->type == UDFTYPE_AGGREGATE) + { + if (lex->current_select->inc_in_sum_expr()) + { + thd->parse_error(); + MYSQL_YYABORT; + } + } + /* Temporary placing the result of find_udf in $3 */ + $<udf>$= udf; +#endif + } + opt_udf_expr_list ')' + { + Create_func *builder; + Item *item= NULL; + + if (check_routine_name(&$1)) + { + MYSQL_YYABORT; + } + + /* + Implementation note: + names are resolved with the following order: + - MySQL native functions, + - User Defined Functions, + - Stored Functions (assuming the current <use> database) + + This will be revised with WL#2128 (SQL PATH) + */ + builder= find_native_function_builder(thd, &$1); + if (builder) + { + item= builder->create_func(thd, &$1, $4); + } + else + { +#ifdef HAVE_DLOPEN + /* Retrieving the result of find_udf */ + udf_func *udf= $<udf>3; + + if (udf) + { + if (udf->type == UDFTYPE_AGGREGATE) + { + Select->in_sum_expr--; + } + + item= Create_udf_func::s_singleton.create(thd, udf, $4); + } + else +#endif + { + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create_func(thd, &$1, $4); + } + } + + if (! ($$= item)) + { + MYSQL_YYABORT; + } + } + | ident '.' ident '(' opt_expr_list ')' + { + Create_qfunc *builder; + Item *item= NULL; + + /* + The following in practice calls: + <code>Create_sp_func::create()</code> + and builds a stored function. + + However, it's important to maintain the interface between the + parser and the implementation in item_create.cc clean, + since this will change with WL#2128 (SQL PATH): + - INFORMATION_SCHEMA.version() is the SQL 99 syntax for the native + function version(), + - MySQL.version() is the SQL 2003 syntax for the native function + version() (a vendor can specify any schema). + */ + + if (!$1.str || check_db_name((LEX_STRING*) &$1)) + my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str)); + if (check_routine_name(&$3)) + { + MYSQL_YYABORT; + } + + builder= find_qualified_function_builder(thd); + DBUG_ASSERT(builder); + item= builder->create_with_db(thd, &$1, &$3, true, $5); + + if (! ($$= item)) + { + MYSQL_YYABORT; + } + } + ; + +fulltext_options: + opt_natural_language_mode opt_query_expansion + { $$= $1 | $2; } + | IN_SYM BOOLEAN_SYM MODE_SYM + { $$= FT_BOOL; } + ; + +opt_natural_language_mode: + /* nothing */ { $$= FT_NL; } + | IN_SYM NATURAL LANGUAGE_SYM MODE_SYM { $$= FT_NL; } + ; + +opt_query_expansion: + /* nothing */ { $$= 0; } + | WITH QUERY_SYM EXPANSION_SYM { $$= FT_EXPAND; } + ; + +opt_udf_expr_list: + /* empty */ { $$= NULL; } + | udf_expr_list { $$= $1; } + ; + +udf_expr_list: + udf_expr + { + $$= new (thd->mem_root) List<Item>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | udf_expr_list ',' udf_expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + +udf_expr: + remember_name expr remember_end select_alias + { + /* + Use Item::name as a storage for the attribute value of user + defined function argument. It is safe to use Item::name + because the syntax will not allow having an explicit name here. + See WL#1017 re. udf attributes. + */ + if ($4.str) + { + $2->is_autogenerated_name= FALSE; + $2->set_name(thd, $4.str, $4.length, system_charset_info); + } + /* + A field has to have its proper name in order for name + resolution to work, something we are only guaranteed if we + parse it out. If we hijack the input stream with + remember_name we may get quoted or escaped names. + */ + else if ($2->type() != Item::FIELD_ITEM && + $2->type() != Item::REF_ITEM /* For HAVING */ ) + $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); + $$= $2; + } + ; + +sum_expr: + AVG_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_avg(thd, $3, FALSE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | AVG_SYM '(' DISTINCT in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_avg(thd, $4, TRUE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BIT_AND '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_and(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BIT_OR '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_or(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BIT_XOR '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_xor(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COUNT_SYM '(' opt_all '*' ')' + { + Item *item= new (thd->mem_root) Item_int(thd, (int32) 0L, 1); + if (item == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_sum_count(thd, item); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COUNT_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_count(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | COUNT_SYM '(' DISTINCT + { Select->in_sum_expr++; } + expr_list + { Select->in_sum_expr--; } + ')' + { + $$= new (thd->mem_root) Item_sum_count(thd, *$5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MIN_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_min(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + /* + According to ANSI SQL, DISTINCT is allowed and has + no sense inside MIN and MAX grouping functions; so MIN|MAX(DISTINCT ...) + is processed like an ordinary MIN | MAX() + */ + | MIN_SYM '(' DISTINCT in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_min(thd, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MAX_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_max(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | MAX_SYM '(' DISTINCT in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_max(thd, $4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | STD_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_std(thd, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | VARIANCE_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_variance(thd, $3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | STDDEV_SAMP_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_std(thd, $3, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | VAR_SAMP_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_variance(thd, $3, 1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUM_SYM '(' in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_sum(thd, $3, FALSE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SUM_SYM '(' DISTINCT in_sum_expr ')' + { + $$= new (thd->mem_root) Item_sum_sum(thd, $4, TRUE); + if ($$ == NULL) + MYSQL_YYABORT; + } + | GROUP_CONCAT_SYM '(' opt_distinct + { Select->in_sum_expr++; } + expr_list opt_gorder_clause + opt_gconcat_separator + ')' + { + SELECT_LEX *sel= Select; + sel->in_sum_expr--; + $$= new (thd->mem_root) + Item_func_group_concat(thd, Lex->current_context(), $3, $5, + sel->gorder_list, $7); + if ($$ == NULL) + MYSQL_YYABORT; + $5->empty(); + sel->gorder_list.empty(); + } + ; + +window_func_expr: + window_func OVER_SYM window_name + { + $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + if (Select->add_window_func((Item_window_func *) $$)) + MYSQL_YYABORT; + } + | + window_func OVER_SYM window_spec + { + LEX *lex= Lex; + if (Select->add_window_spec(thd, lex->win_ref, + Select->group_list, + Select->order_list, + lex->win_frame)) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_window_func(thd, (Item_sum *) $1, + thd->lex->win_spec); + if ($$ == NULL) + MYSQL_YYABORT; + if (Select->add_window_func((Item_window_func *) $$)) + MYSQL_YYABORT; + } + ; + +window_func: + simple_window_func + | + sum_expr + { + ((Item_sum *) $1)->mark_as_window_func_sum_expr(); + } + ; + +simple_window_func: + ROW_NUMBER_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_row_number(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + RANK_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_rank(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + DENSE_RANK_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_dense_rank(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + PERCENT_RANK_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_percent_rank(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + CUME_DIST_SYM '(' ')' + { + $$= new (thd->mem_root) Item_sum_cume_dist(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + NTILE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_ntile(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + FIRST_VALUE_SYM '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_first_value(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LAST_VALUE '(' expr ')' + { + $$= new (thd->mem_root) Item_sum_last_value(thd, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + NTH_VALUE_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_sum_nth_value(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LEAD_SYM '(' expr ')' + { + /* No second argument defaults to 1. */ + Item* item_offset= new (thd->mem_root) Item_uint(thd, 1); + if (item_offset == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_sum_lead(thd, $3, item_offset); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LEAD_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_sum_lead(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LAG_SYM '(' expr ')' + { + /* No second argument defaults to 1. */ + Item* item_offset= new (thd->mem_root) Item_uint(thd, 1); + if (item_offset == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_sum_lag(thd, $3, item_offset); + if ($$ == NULL) + MYSQL_YYABORT; + } + | + LAG_SYM '(' expr ',' expr ')' + { + $$= new (thd->mem_root) Item_sum_lag(thd, $3, $5); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +window_name: + ident + { + $$= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING)); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +variable: + '@' + { + if (! Lex->parsing_options.allows_variable) + my_yyabort_error((ER_VIEW_SELECT_VARIABLE, MYF(0))); + } + variable_aux + { + $$= $3; + } + ; + +variable_aux: + ident_or_text SET_VAR expr + { + Item_func_set_user_var *item; + $$= item= new (thd->mem_root) Item_func_set_user_var(thd, &$1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + lex->set_var_list.push_back(item, thd->mem_root); + } + | ident_or_text + { + $$= new (thd->mem_root) Item_func_get_user_var(thd, &$1); + if ($$ == NULL) + MYSQL_YYABORT; + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + } + | '@' opt_var_ident_type ident_or_text opt_component + { + /* disallow "SELECT @@global.global.variable" */ + if ($3.str && $4.str && check_reserved_words(&$3)) + { + thd->parse_error(); + MYSQL_YYABORT; + } + if (!($$= get_system_var(thd, $2, $3, $4))) + MYSQL_YYABORT; + if (!((Item_func_get_system_var*) $$)->is_written_to_binlog()) + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_SYSTEM_VARIABLE); + } + ; + +opt_distinct: + /* empty */ { $$ = 0; } + | DISTINCT { $$ = 1; } + ; + +opt_gconcat_separator: + /* empty */ + { + $$= new (thd->mem_root) String(",", 1, &my_charset_latin1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | SEPARATOR_SYM text_string { $$ = $2; } + ; + +opt_gorder_clause: + /* empty */ + | ORDER_SYM BY gorder_list; + ; + +gorder_list: + gorder_list ',' order_ident order_dir + { if (add_gorder_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + | order_ident order_dir + { if (add_gorder_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } + ; + +in_sum_expr: + opt_all + { + LEX *lex= Lex; + if (lex->current_select->inc_in_sum_expr()) + { + thd->parse_error(); + MYSQL_YYABORT; + } + } + expr + { + Select->in_sum_expr--; + $$= $3; + } + ; + +cast_type: + BINARY opt_field_length + { $$.set(&type_handler_long_blob, $2); Lex->charset= &my_charset_bin; } + | CHAR_SYM opt_field_length + { Lex->charset= thd->variables.collation_connection; } + opt_binary + { $$.set(&type_handler_long_blob, $2); } + | VARCHAR field_length + { Lex->charset= thd->variables.collation_connection; } + opt_binary + { $$.set(&type_handler_long_blob, $2); } + | VARCHAR2 field_length + { Lex->charset= thd->variables.collation_connection; } + opt_binary + { $$.set(&type_handler_long_blob, $2); } + | NCHAR_SYM opt_field_length + { + Lex->charset= national_charset_info; + $$.set(&type_handler_long_blob, $2, 0); + } + | cast_type_numeric { $$= $1; Lex->charset= NULL; } + | cast_type_temporal { $$= $1; Lex->charset= NULL; } + ; + +cast_type_numeric: + INT_SYM { $$.set(&type_handler_longlong); } + | SIGNED_SYM { $$.set(&type_handler_longlong); } + | SIGNED_SYM INT_SYM { $$.set(&type_handler_longlong); } + | UNSIGNED { $$.set(&type_handler_ulonglong); } + | UNSIGNED INT_SYM { $$.set(&type_handler_ulonglong); } + | DECIMAL_SYM float_options { $$.set(&type_handler_newdecimal, $2); } + | DOUBLE_SYM opt_precision { $$.set(&type_handler_double, $2); } + ; + +cast_type_temporal: + DATE_SYM { $$.set(&type_handler_newdate); } + | TIME_SYM opt_field_length { $$.set(&type_handler_time2, 0, $2); } + | DATETIME opt_field_length { $$.set(&type_handler_datetime2, 0, $2); } + ; + +opt_expr_list: + /* empty */ { $$= NULL; } + | expr_list { $$= $1;} + ; + +expr_list: + expr + { + $$= new (thd->mem_root) List<Item>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | expr_list ',' expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + +ident_list_arg: + ident_list { $$= $1; } + | '(' ident_list ')' { $$= $2; } + ; + +ident_list: + simple_ident + { + $$= new (thd->mem_root) List<Item>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + } + | ident_list ',' simple_ident + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + +when_list: + WHEN_SYM expr THEN_SYM expr + { + $$= new (thd->mem_root) List<Item>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($2, thd->mem_root); + $$->push_back($4, thd->mem_root); + } + | when_list WHEN_SYM expr THEN_SYM expr + { + $1->push_back($3, thd->mem_root); + $1->push_back($5, thd->mem_root); + $$= $1; + } + ; + + +when_list_opt_else: + when_list + | when_list ELSE expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + +decode_when_list: + expr ',' expr + { + $$= new (thd->mem_root) List<Item>; + if ($$ == NULL) + MYSQL_YYABORT; + $$->push_back($1, thd->mem_root); + $$->push_back($3, thd->mem_root); + } + | decode_when_list ',' expr + { + $$= $1; + $$->push_back($3, thd->mem_root); + } + ; + + +/* Equivalent to <table reference> in the SQL:2003 standard. */ +/* Warning - may return NULL in case of incomplete SELECT */ +table_ref: + table_factor { $$= $1; } + | join_table + { + LEX *lex= Lex; + if (!($$= lex->current_select->nest_last_join(thd))) + { + thd->parse_error(); + MYSQL_YYABORT; + } + } + ; + +join_table_list: + derived_table_list { MYSQL_YYABORT_UNLESS($$=$1); } + ; + +/* + The ODBC escape syntax for Outer Join is: '{' OJ join_table '}' + The parser does not define OJ as a token, any ident is accepted + instead in $2 (ident). Also, all productions from table_ref can + be escaped, not only join_table. Both syntax extensions are safe + and are ignored. +*/ +esc_table_ref: + table_ref { $$=$1; } + | '{' ident table_ref '}' { $$=$3; } + ; + +/* Equivalent to <table reference list> in the SQL:2003 standard. */ +/* Warning - may return NULL in case of incomplete SELECT */ +derived_table_list: + esc_table_ref { $$=$1; } + | derived_table_list ',' esc_table_ref + { + MYSQL_YYABORT_UNLESS($1 && ($$=$3)); + } + ; + +/* + Notice that JOIN is a left-associative operation, and it must be parsed + as such, that is, the parser must process first the left join operand + then the right one. Such order of processing ensures that the parser + produces correct join trees which is essential for semantic analysis + and subsequent optimization phases. +*/ +join_table: + /* INNER JOIN variants */ + /* + Use %prec to evaluate production 'table_ref' before 'normal_join' + so that [INNER | CROSS] JOIN is properly nested as other + left-associative joins. + */ + table_ref normal_join table_ref %prec TABLE_REF_PRIORITY + { MYSQL_YYABORT_UNLESS($1 && ($$=$3)); $3->straight=$2; } + | table_ref normal_join table_ref + ON + { + MYSQL_YYABORT_UNLESS($1 && $3); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(thd, $1, $3)) + MYSQL_YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + $3->straight=$2; + add_join_on(thd, $3, $6); + Lex->pop_context(); + Select->parsing_place= NO_MATTER; + } + | table_ref normal_join table_ref + USING + { + MYSQL_YYABORT_UNLESS($1 && $3); + } + '(' using_list ')' + { + $3->straight=$2; + add_join_natural($1,$3,$7,Select); + $$=$3; + } + | table_ref NATURAL inner_join table_factor + { + MYSQL_YYABORT_UNLESS($1 && ($$=$4)); + $4->straight=$3; + add_join_natural($1,$4,NULL,Select); + } + + /* LEFT JOIN variants */ + | table_ref LEFT opt_outer JOIN_SYM table_ref + ON + { + MYSQL_YYABORT_UNLESS($1 && $5); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(thd, $1, $5)) + MYSQL_YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + add_join_on(thd, $5, $8); + Lex->pop_context(); + $5->outer_join|=JOIN_TYPE_LEFT; + $$=$5; + Select->parsing_place= NO_MATTER; + } + | table_ref LEFT opt_outer JOIN_SYM table_factor + { + MYSQL_YYABORT_UNLESS($1 && $5); + } + USING '(' using_list ')' + { + add_join_natural($1,$5,$9,Select); + $5->outer_join|=JOIN_TYPE_LEFT; + $$=$5; + } + | table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor + { + MYSQL_YYABORT_UNLESS($1 && $6); + add_join_natural($1,$6,NULL,Select); + $6->outer_join|=JOIN_TYPE_LEFT; + $$=$6; + } + + /* RIGHT JOIN variants */ + | table_ref RIGHT opt_outer JOIN_SYM table_ref + ON + { + MYSQL_YYABORT_UNLESS($1 && $5); + /* Change the current name resolution context to a local context. */ + if (push_new_name_resolution_context(thd, $1, $5)) + MYSQL_YYABORT; + Select->parsing_place= IN_ON; + } + expr + { + LEX *lex= Lex; + if (!($$= lex->current_select->convert_right_join())) + MYSQL_YYABORT; + add_join_on(thd, $$, $8); + Lex->pop_context(); + Select->parsing_place= NO_MATTER; + } + | table_ref RIGHT opt_outer JOIN_SYM table_factor + { + MYSQL_YYABORT_UNLESS($1 && $5); + } + USING '(' using_list ')' + { + LEX *lex= Lex; + if (!($$= lex->current_select->convert_right_join())) + MYSQL_YYABORT; + add_join_natural($$,$5,$9,Select); + } + | table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor + { + MYSQL_YYABORT_UNLESS($1 && $6); + add_join_natural($6,$1,NULL,Select); + LEX *lex= Lex; + if (!($$= lex->current_select->convert_right_join())) + MYSQL_YYABORT; + } + ; + + +inner_join: /* $$ set if using STRAIGHT_JOIN, false otherwise */ + JOIN_SYM { $$ = 0; } + | INNER_SYM JOIN_SYM { $$ = 0; } + | STRAIGHT_JOIN { $$ = 1; } + ; + +normal_join: + inner_join { $$ = $1; } + | CROSS JOIN_SYM { $$ = 0; } + ; + +/* + table PARTITION (list of partitions), reusing using_list instead of creating + a new rule for partition_list. +*/ +opt_use_partition: + /* empty */ { $$= 0;} + | use_partition + ; + +use_partition: + PARTITION_SYM '(' using_list ')' have_partitioning + { + $$= $3; + } + ; + +/* + This is a flattening of the rules <table factor> and <table primary> + in the SQL:2003 standard, since we don't have <sample clause> + + I.e. + <table factor> ::= <table primary> [ <sample clause> ] +*/ +/* Warning - may return NULL in case of incomplete SELECT */ +table_factor: + table_primary_ident + | table_primary_derived + ; + +table_primary_ident: + { + SELECT_LEX *sel= Select; + sel->table_join_options= 0; + } + table_ident opt_use_partition opt_table_alias opt_key_definition + { + if (!($$= Select->add_table_to_list(thd, $2, $4, + Select->get_table_join_options(), + YYPS->m_lock_type, + YYPS->m_mdl_type, + Select->pop_index_hints(), + $3))) + MYSQL_YYABORT; + Select->add_joined_table($$); + } + ; + + + +/* + Represents a flattening of the following rules from the SQL:2003 + standard. This sub-rule corresponds to the sub-rule + <table primary> ::= ... | <derived table> [ AS ] <correlation name> + + <derived table> ::= <table subquery> + <table subquery> ::= <subquery> + <subquery> ::= <left paren> <query expression> <right paren> + <query expression> ::= [ <with clause> ] <query expression body> + + For the time being we use the non-standard rule + select_derived_union which is a compromise between the standard + and our parser. Possibly this rule could be replaced by our + query_expression_body. +*/ + +table_primary_derived: + '(' get_select_lex select_derived_union ')' opt_table_alias + { + /* Use $2 instead of Lex->current_select as derived table will + alter value of Lex->current_select. */ + if (!($3 || $5) && $2->embedding && + !$2->embedding->nested_join->join_list.elements) + { + /* we have a derived table ($3 == NULL) but no alias, + Since we are nested in further parentheses so we + can pass NULL to the outer level parentheses + Permits parsing of "((((select ...))) as xyz)" */ + $$= 0; + } + else if (!$3) + { + /* Handle case of derived table, alias may be NULL if there + are no outer parentheses, add_table_to_list() will throw + error in this case */ + LEX *lex=Lex; + lex->check_automatic_up(UNSPECIFIED_TYPE); + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel->master_unit(); + lex->current_select= sel= unit->outer_select(); + Table_ident *ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + MYSQL_YYABORT; + if (!($$= sel->add_table_to_list(thd, + ti, $5, 0, + TL_READ, MDL_SHARED_READ))) + + MYSQL_YYABORT; + sel->add_joined_table($$); + lex->pop_context(); + lex->nest_level--; + } + else if ($5 != NULL) + { + /* + Tables with or without joins within parentheses cannot + have aliases, and we ruled out derived tables above. + */ + thd->parse_error(); + MYSQL_YYABORT; + } + else + { + /* nested join: FROM (t1 JOIN t2 ...), + nest_level is the same as in the outer query */ + $$= $3; + } + /* + Fields in derived table can be used in upper select in + case of merge. We do not add HAVING fields because we do + not merge such derived. We do not add union because + also do not merge them + */ + if ($$ && $$->derived && + !$$->derived->first_select()->next_select()) + $$->select_lex->add_where_field($$->derived->first_select()); + } + /* Represents derived table with WITH clause */ + | '(' get_select_lex subselect_start + with_clause query_expression_body + subselect_end ')' opt_table_alias + { + LEX *lex=Lex; + SELECT_LEX *sel= $2; + SELECT_LEX_UNIT *unit= $5->master_unit(); + Table_ident *ti= new (thd->mem_root) Table_ident(unit); + if (ti == NULL) + MYSQL_YYABORT; + $5->set_with_clause($4); + lex->current_select= sel; + if (!($$= sel->add_table_to_list(lex->thd, + ti, $8, 0, + TL_READ, MDL_SHARED_READ))) + MYSQL_YYABORT; + sel->add_joined_table($$); + } + ; + +/* + This rule accepts just about anything. The reason is that we have + empty-producing rules in the beginning of rules, in this case + subselect_start. This forces bison to take a decision which rules to + reduce by long before it has seen any tokens. This approach ties us + to a very limited class of parseable languages, and unfortunately + SQL is not one of them. The chosen 'solution' was this rule, which + produces just about anything, even complete bogus statements, for + instance ( table UNION SELECT 1 ). + Fortunately, we know that the semantic value returned by + select_derived is NULL if it contained a derived table, and a pointer to + the base table's TABLE_LIST if it was a base table. So in the rule + regarding union's, we throw a parse error manually and pretend it + was bison that did it. + + Also worth noting is that this rule concerns query expressions in + the from clause only. Top level select statements and other types of + subqueries have their own union rules. +*/ +select_derived_union: + select_derived + | select_derived union_order_or_limit + { + if ($1) + { + thd->parse_error(); + MYSQL_YYABORT; + } + } + | select_derived union_head_non_top + { + if ($1) + { + thd->parse_error(); + MYSQL_YYABORT; + } + } + union_list_derived_part2 + | derived_query_specification opt_select_lock_type + | derived_query_specification order_or_limit opt_select_lock_type + | derived_query_specification opt_select_lock_type union_list_derived + ; + +union_list_derived_part2: + query_term_union_not_ready { Lex->pop_context(); } + | query_term_union_ready { Lex->pop_context(); } + | query_term_union_ready { Lex->pop_context(); } union_list_derived + ; + +union_list_derived: + union_head_non_top union_list_derived_part2 + ; + + +/* The equivalent of select_init2 for nested queries. */ +select_init2_derived: + select_part2_derived + { + Select->set_braces(0); + } + ; + +/* The equivalent of select_part2 for nested queries. */ +select_part2_derived: + { + LEX *lex= Lex; + SELECT_LEX *sel= lex->current_select; + if (sel->linkage != UNION_TYPE) + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + } + opt_query_expression_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + ; + +/* handle contents of parentheses in join expression */ +select_derived: + get_select_lex_derived derived_table_list + { + LEX *lex= Lex; + /* for normal joins, $2 != NULL and end_nested_join() != NULL, + for derived tables, both must equal NULL */ + + if (!($$= $1->end_nested_join(lex->thd)) && $2) + MYSQL_YYABORT; + if (!$2 && $$) + { + thd->parse_error(); + MYSQL_YYABORT; + } + } + ; + +/* + Similar to query_specification, but for derived tables. + Example: the inner parenthesized SELECT in this query: + SELECT * FROM (SELECT * FROM t1); +*/ +derived_query_specification: + SELECT_SYM select_derived_init select_derived2 + { + if ($2) + Select->set_braces(1); + $$= NULL; + } + ; + +select_derived2: + { + LEX *lex= Lex; + lex->derived_tables|= DERIVED_SUBQUERY; + if (!lex->expr_allows_subselect || + lex->sql_command == (int)SQLCOM_PURGE) + { + thd->parse_error(); + MYSQL_YYABORT; + } + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE || + mysql_new_select(lex, 1, NULL)) + MYSQL_YYABORT; + mysql_init_select(lex); + lex->current_select->linkage= DERIVED_TABLE_TYPE; + lex->current_select->parsing_place= SELECT_LIST; + } + select_options select_item_list + { + Select->parsing_place= NO_MATTER; + } + opt_table_expression + ; + +get_select_lex: + /* Empty */ { $$= Select; } + ; + +get_select_lex_derived: + get_select_lex + { + LEX *lex= Lex; + if ($1->init_nested_join(lex->thd)) + MYSQL_YYABORT; + } + ; + +select_derived_init: + { + LEX *lex= Lex; + + TABLE_LIST *embedding= lex->current_select->embedding; + $$= embedding && + !embedding->nested_join->join_list.elements; + /* return true if we are deeply nested */ + } + ; + +opt_outer: + /* empty */ {} + | OUTER {} + ; + +index_hint_clause: + /* empty */ + { + $$= thd->variables.old_mode ? INDEX_HINT_MASK_JOIN : INDEX_HINT_MASK_ALL; + } + | FOR_SYM JOIN_SYM { $$= INDEX_HINT_MASK_JOIN; } + | FOR_SYM ORDER_SYM BY { $$= INDEX_HINT_MASK_ORDER; } + | FOR_SYM GROUP_SYM BY { $$= INDEX_HINT_MASK_GROUP; } + ; + +index_hint_type: + FORCE_SYM { $$= INDEX_HINT_FORCE; } + | IGNORE_SYM { $$= INDEX_HINT_IGNORE; } + ; + +index_hint_definition: + index_hint_type key_or_index index_hint_clause + { + Select->set_index_hint_type($1, $3); + } + '(' key_usage_list ')' + | USE_SYM key_or_index index_hint_clause + { + Select->set_index_hint_type(INDEX_HINT_USE, $3); + } + '(' opt_key_usage_list ')' + ; + +index_hints_list: + index_hint_definition + | index_hints_list index_hint_definition + ; + +opt_index_hints_list: + /* empty */ + | { Select->alloc_index_hints(thd); } index_hints_list + ; + +opt_key_definition: + { Select->clear_index_hints(); } + opt_index_hints_list + ; + +opt_key_usage_list: + /* empty */ { Select->add_index_hint(thd, NULL, 0); } + | key_usage_list {} + ; + +key_usage_element: + ident + { Select->add_index_hint(thd, $1.str, $1.length); } + | PRIMARY_SYM + { Select->add_index_hint(thd, "PRIMARY", 7); } + ; + +key_usage_list: + key_usage_element + | key_usage_list ',' key_usage_element + ; + +using_list: + ident + { + if (!($$= new (thd->mem_root) List<String>)) + MYSQL_YYABORT; + String *s= new (thd->mem_root) String((const char *) $1.str, + $1.length, + system_charset_info); + if (s == NULL) + MYSQL_YYABORT; + $$->push_back(s, thd->mem_root); + } + | using_list ',' ident + { + String *s= new (thd->mem_root) String((const char *) $3.str, + $3.length, + system_charset_info); + if (s == NULL) + MYSQL_YYABORT; + $1->push_back(s, thd->mem_root); + $$= $1; + } + ; + +interval: + interval_time_stamp {} + | DAY_HOUR_SYM { $$=INTERVAL_DAY_HOUR; } + | DAY_MICROSECOND_SYM { $$=INTERVAL_DAY_MICROSECOND; } + | DAY_MINUTE_SYM { $$=INTERVAL_DAY_MINUTE; } + | DAY_SECOND_SYM { $$=INTERVAL_DAY_SECOND; } + | HOUR_MICROSECOND_SYM { $$=INTERVAL_HOUR_MICROSECOND; } + | HOUR_MINUTE_SYM { $$=INTERVAL_HOUR_MINUTE; } + | HOUR_SECOND_SYM { $$=INTERVAL_HOUR_SECOND; } + | MINUTE_MICROSECOND_SYM { $$=INTERVAL_MINUTE_MICROSECOND; } + | MINUTE_SECOND_SYM { $$=INTERVAL_MINUTE_SECOND; } + | SECOND_MICROSECOND_SYM { $$=INTERVAL_SECOND_MICROSECOND; } + | YEAR_MONTH_SYM { $$=INTERVAL_YEAR_MONTH; } + ; + +interval_time_stamp: + DAY_SYM { $$=INTERVAL_DAY; } + | WEEK_SYM { $$=INTERVAL_WEEK; } + | HOUR_SYM { $$=INTERVAL_HOUR; } + | MINUTE_SYM { $$=INTERVAL_MINUTE; } + | MONTH_SYM { $$=INTERVAL_MONTH; } + | QUARTER_SYM { $$=INTERVAL_QUARTER; } + | SECOND_SYM { $$=INTERVAL_SECOND; } + | MICROSECOND_SYM { $$=INTERVAL_MICROSECOND; } + | YEAR_SYM { $$=INTERVAL_YEAR; } + ; + +date_time_type: + DATE_SYM {$$=MYSQL_TIMESTAMP_DATE;} + | TIME_SYM {$$=MYSQL_TIMESTAMP_TIME;} + | DATETIME {$$=MYSQL_TIMESTAMP_DATETIME;} + | TIMESTAMP {$$=MYSQL_TIMESTAMP_DATETIME;} + ; + +table_alias: + /* empty */ + | AS + | '=' + ; + +opt_table_alias: + /* empty */ { $$=0; } + | table_alias ident + { + $$= (LEX_CSTRING*) thd->memdup(&$2,sizeof(LEX_STRING)); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_all: + /* empty */ + | ALL + ; + +opt_where_clause: + /* empty */ { Select->where= 0; } + | WHERE + { + Select->parsing_place= IN_WHERE; + } + expr + { + SELECT_LEX *select= Select; + select->where= normalize_cond(thd, $3); + select->parsing_place= NO_MATTER; + if ($3) + $3->top_level_item(); + } + ; + +opt_having_clause: + /* empty */ + | HAVING + { + Select->parsing_place= IN_HAVING; + } + expr + { + SELECT_LEX *sel= Select; + sel->having= normalize_cond(thd, $3); + sel->parsing_place= NO_MATTER; + if ($3) + $3->top_level_item(); + } + ; + +opt_escape: + ESCAPE_SYM simple_expr + { + Lex->escape_used= TRUE; + $$= $2; + } + | /* empty */ + { + Lex->escape_used= FALSE; + $$= ((thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES) ? + new (thd->mem_root) Item_string_ascii(thd, "", 0) : + new (thd->mem_root) Item_string_ascii(thd, "\\", 1)); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* + group by statement in select +*/ + +opt_group_clause: + /* empty */ + | GROUP_SYM BY group_list olap_opt + ; + +group_list: + group_list ',' order_ident order_dir + { if (add_group_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + | order_ident order_dir + { if (add_group_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } + ; + +olap_opt: + /* empty */ {} + | WITH_CUBE_SYM + { + /* + 'WITH CUBE' is reserved in the MySQL syntax, but not implemented, + and cause LALR(2) conflicts. + This syntax is not standard. + MySQL syntax: GROUP BY col1, col2, col3 WITH CUBE + SQL-2003: GROUP BY ... CUBE(col1, col2, col3) + */ + LEX *lex=Lex; + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH CUBE", + "global union parameters")); + lex->current_select->olap= CUBE_TYPE; + + my_yyabort_error((ER_NOT_SUPPORTED_YET, MYF(0), "CUBE")); + } + | WITH_ROLLUP_SYM + { + /* + 'WITH ROLLUP' is needed for backward compatibility, + and cause LALR(2) conflicts. + This syntax is not standard. + MySQL syntax: GROUP BY col1, col2, col3 WITH ROLLUP + SQL-2003: GROUP BY ... ROLLUP(col1, col2, col3) + */ + LEX *lex= Lex; + if (lex->current_select->linkage == GLOBAL_OPTIONS_TYPE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "WITH ROLLUP", + "global union parameters")); + lex->current_select->olap= ROLLUP_TYPE; + } + ; + +/* + optional window clause in select +*/ + +opt_window_clause: + /* empty */ + {} + | WINDOW_SYM + window_def_list + {} + ; + +window_def_list: + window_def_list ',' window_def + | window_def + ; + +window_def: + window_name AS window_spec + { + LEX *lex= Lex; + if (Select->add_window_def(thd, $1, lex->win_ref, + Select->group_list, + Select->order_list, + lex->win_frame)) + MYSQL_YYABORT; + } + ; + +window_spec: + '(' + { Select->prepare_add_window_spec(thd); } + opt_window_ref opt_window_partition_clause + opt_window_order_clause opt_window_frame_clause + ')' + ; + +opt_window_ref: + /* empty */ {} + | ident + { + thd->lex->win_ref= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING)); + if (thd->lex->win_ref == NULL) + MYSQL_YYABORT; + } + +opt_window_partition_clause: + /* empty */ { } + | PARTITION_SYM BY group_list + ; + +opt_window_order_clause: + /* empty */ { } + | ORDER_SYM BY order_list + ; + +opt_window_frame_clause: + /* empty */ {} + | window_frame_units window_frame_extent opt_window_frame_exclusion + { + LEX *lex= Lex; + lex->win_frame= + new (thd->mem_root) Window_frame($1, + lex->frame_top_bound, + lex->frame_bottom_bound, + $3); + if (lex->win_frame == NULL) + MYSQL_YYABORT; + } + ; + +window_frame_units: + ROWS_SYM { $$= Window_frame::UNITS_ROWS; } + | RANGE_SYM { $$= Window_frame::UNITS_RANGE; } + ; + +window_frame_extent: + window_frame_start + { + LEX *lex= Lex; + lex->frame_top_bound= $1; + lex->frame_bottom_bound= + new (thd->mem_root) + Window_frame_bound(Window_frame_bound::CURRENT, NULL); + if (lex->frame_bottom_bound == NULL) + MYSQL_YYABORT; + } + | BETWEEN_SYM window_frame_bound AND_SYM window_frame_bound + { + LEX *lex= Lex; + lex->frame_top_bound= $2; + lex->frame_bottom_bound= $4; + } + ; + +window_frame_start: + UNBOUNDED_SYM PRECEDING_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::PRECEDING, NULL); + if ($$ == NULL) + MYSQL_YYABORT; + } + | CURRENT_SYM ROW_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::CURRENT, NULL); + if ($$ == NULL) + MYSQL_YYABORT; + } + | literal PRECEDING_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::PRECEDING, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +window_frame_bound: + window_frame_start { $$= $1; } + | UNBOUNDED_SYM FOLLOWING_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::FOLLOWING, NULL); + if ($$ == NULL) + MYSQL_YYABORT; + } + | literal FOLLOWING_SYM + { + $$= new (thd->mem_root) + Window_frame_bound(Window_frame_bound::FOLLOWING, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_window_frame_exclusion: + /* empty */ { $$= Window_frame::EXCL_NONE; } + | EXCLUDE_SYM CURRENT_SYM ROW_SYM + { $$= Window_frame::EXCL_CURRENT_ROW; } + | EXCLUDE_SYM GROUP_SYM + { $$= Window_frame::EXCL_GROUP; } + | EXCLUDE_SYM TIES_SYM + { $$= Window_frame::EXCL_TIES; } + | EXCLUDE_SYM NO_SYM OTHERS_SYM + { $$= Window_frame::EXCL_NONE; } + ; + +/* + Order by statement in ALTER TABLE +*/ + +alter_order_clause: + ORDER_SYM BY alter_order_list + ; + +alter_order_list: + alter_order_list ',' alter_order_item + | alter_order_item + ; + +alter_order_item: + simple_ident_nospvar order_dir + { + bool ascending= ($2 == 1) ? true : false; + if (add_order_to_list(thd, $1, ascending)) + MYSQL_YYABORT; + } + ; + +/* + Order by statement in select +*/ + +opt_order_clause: + /* empty */ + | order_clause + ; + +order_clause: + ORDER_SYM BY + { + LEX *lex=Lex; + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel-> master_unit(); + if (sel->linkage != GLOBAL_OPTIONS_TYPE && + sel->olap != UNSPECIFIED_OLAP_TYPE && + (sel->linkage != UNION_TYPE || sel->braces)) + { + my_error(ER_WRONG_USAGE, MYF(0), + "CUBE/ROLLUP", "ORDER BY"); + MYSQL_YYABORT; + } + if (lex->sql_command != SQLCOM_ALTER_TABLE && + !unit->fake_select_lex) + { + /* + A query of the of the form (SELECT ...) ORDER BY order_list is + executed in the same way as the query + SELECT ... ORDER BY order_list + unless the SELECT construct contains ORDER BY or LIMIT clauses. + Otherwise we create a fake SELECT_LEX if it has not been created + yet. + */ + SELECT_LEX *first_sl= unit->first_select(); + if (!unit->is_unit_op() && + (first_sl->order_list.elements || + first_sl->select_limit) && + unit->add_fake_select_lex(thd)) + MYSQL_YYABORT; + } + if (sel->master_unit()->is_unit_op() && !sel->braces) + { + /* + At this point we don't know yet whether this is the last + select in union or not, but we move ORDER BY to + fake_select_lex anyway. If there would be one more select + in union mysql_new_select will correctly throw error. + */ + DBUG_ASSERT(sel->master_unit()->fake_select_lex); + lex->current_select= sel->master_unit()->fake_select_lex; + } + } + order_list + { + + } + ; + +order_list: + order_list ',' order_ident order_dir + { if (add_order_to_list(thd, $3,(bool) $4)) MYSQL_YYABORT; } + | order_ident order_dir + { if (add_order_to_list(thd, $1,(bool) $2)) MYSQL_YYABORT; } + ; + +order_dir: + /* empty */ { $$ = 1; } + | ASC { $$ =1; } + | DESC { $$ =0; } + ; + +opt_limit_clause: + /* empty */ {} + | limit_clause {} + ; + +limit_clause_init: + LIMIT + { + SELECT_LEX *sel= Select; + if (sel->master_unit()->is_unit_op() && !sel->braces) + { + /* Move LIMIT that belongs to UNION to fake_select_lex */ + Lex->current_select= sel->master_unit()->fake_select_lex; + DBUG_ASSERT(Select); + } + } + ; + +limit_clause: + limit_clause_init limit_options + { + SELECT_LEX *sel= Select; + if (!sel->select_limit->basic_const_item() || + sel->select_limit->val_int() > 0) + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + | limit_clause_init limit_options + ROWS_SYM EXAMINED_SYM limit_rows_option + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + | limit_clause_init ROWS_SYM EXAMINED_SYM limit_rows_option + { + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + } + ; + +limit_options: + limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= 0; + sel->explicit_limit= 1; + } + | limit_option ',' limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $3; + sel->offset_limit= $1; + sel->explicit_limit= 1; + } + | limit_option OFFSET_SYM limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $1; + sel->offset_limit= $3; + sel->explicit_limit= 1; + } + ; + +limit_option: + ident_with_tok_start + { + LEX *lex= thd->lex; + Lex_input_stream *lip= & thd->m_parser_state->m_lip; + if (!($$= lex->create_item_limit(thd, &$1, + $1.m_pos, lip->get_tok_end()))) + MYSQL_YYABORT; + } + | ident_with_tok_start '.' ident + { + LEX *lex= thd->lex; + Lex_input_stream *lip= & thd->m_parser_state->m_lip; + if (!($$= lex->create_item_limit(thd, &$1, &$3, + $1.m_pos, lip->get_ptr()))) + MYSQL_YYABORT; + } + | param_marker + { + $1->limit_clause_param= TRUE; + } + | ULONGLONG_NUM + { + $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | LONG_NUM + { + $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | NUM + { + $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +limit_rows_option: + limit_option + { + LEX *lex=Lex; + lex->limit_rows_examined= $1; + } + +delete_limit_clause: + /* empty */ + { + LEX *lex=Lex; + lex->current_select->select_limit= 0; + } + | LIMIT limit_option + { + SELECT_LEX *sel= Select; + sel->select_limit= $2; + Lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_LIMIT); + sel->explicit_limit= 1; + } + | LIMIT ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; } + | LIMIT limit_option ROWS_SYM EXAMINED_SYM { thd->parse_error(); MYSQL_YYABORT; } + ; + +opt_plus: + /* empty */ + | '+' + ; + +int_num: + opt_plus NUM { int error; $$= (int) my_strtoll10($2.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(int) my_strtoll10($2.str, (char**) 0, &error); } + +ulong_num: + opt_plus NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } + | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } + | opt_plus LONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus DECIMAL_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus FLOAT_NUM { int error; $$= (ulong) my_strtoll10($2.str, (char**) 0, &error); } + ; + +real_ulong_num: + NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | HEX_NUM { $$= (ulong) strtol($1.str, (char**) 0, 16); } + | LONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulong) my_strtoll10($1.str, (char**) 0, &error); } + | dec_num_error { MYSQL_YYABORT; } + ; + +longlong_num: + opt_plus NUM { int error; $$= (longlong) my_strtoll10($2.str, (char**) 0, &error); } + | LONG_NUM { int error; $$= (longlong) my_strtoll10($1.str, (char**) 0, &error); } + | '-' NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + | '-' LONG_NUM { int error; $$= -(longlong) my_strtoll10($2.str, (char**) 0, &error); } + + +ulonglong_num: + opt_plus NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus LONG_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus DECIMAL_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } + | opt_plus FLOAT_NUM { int error; $$= (ulonglong) my_strtoll10($2.str, (char**) 0, &error); } + ; + +real_ulonglong_num: + NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | ULONGLONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | HEX_NUM { $$= strtoull($1.str, (char**) 0, 16); } + | LONG_NUM { int error; $$= (ulonglong) my_strtoll10($1.str, (char**) 0, &error); } + | dec_num_error { MYSQL_YYABORT; } + ; + +dec_num_error: + dec_num + { thd->parse_error(ER_ONLY_INTEGERS_ALLOWED); } + ; + +dec_num: + DECIMAL_NUM + | FLOAT_NUM + ; + +choice: + ulong_num { $$= $1 != 0 ? HA_CHOICE_YES : HA_CHOICE_NO; } + | DEFAULT { $$= HA_CHOICE_UNDEF; } + ; + +bool: + ulong_num { $$= $1 != 0; } + | TRUE_SYM { $$= 1; } + | FALSE_SYM { $$= 0; } + + +procedure_clause: + PROCEDURE_SYM ident /* Procedure name */ + { + LEX *lex=Lex; + + DBUG_ASSERT(&lex->select_lex == lex->current_select); + + lex->proc_list.elements=0; + lex->proc_list.first=0; + lex->proc_list.next= &lex->proc_list.first; + Item_field *item= new (thd->mem_root) + Item_field(thd, &lex->current_select->context, + NULL, NULL, &$2); + if (item == NULL) + MYSQL_YYABORT; + if (add_proc_to_list(thd, item)) + MYSQL_YYABORT; + Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + + /* + PROCEDURE CLAUSE cannot handle subquery as one of its parameter, + so set expr_allows_subselect as false to disallow any subqueries + further. Reset expr_allows_subselect back to true once the + parameters are reduced. + */ + Lex->expr_allows_subselect= false; + } + '(' procedure_list ')' + { + /* Subqueries are allowed from now.*/ + Lex->expr_allows_subselect= true; + } + ; + +procedure_list: + /* empty */ {} + | procedure_list2 {} + ; + +procedure_list2: + procedure_list2 ',' procedure_item + | procedure_item + ; + +procedure_item: + remember_name expr remember_end + { + if (add_proc_to_list(thd, $2)) + MYSQL_YYABORT; + if (!$2->name.str || $2->name.str == item_empty_name) + $2->set_name(thd, $1, (uint) ($3 - $1), thd->charset()); + } + ; + +select_var_list_init: + { + LEX *lex=Lex; + if (!lex->describe && + (!(lex->result= new (thd->mem_root) select_dumpvar(thd)))) + MYSQL_YYABORT; + } + select_var_list + {} + ; + +select_var_list: + select_var_list ',' select_var_ident + | select_var_ident {} + ; + +select_var_ident: select_outvar + { + if (Lex->result) + { + if ($1 == NULL) + MYSQL_YYABORT; + ((select_dumpvar *)Lex->result)->var_list.push_back($1, thd->mem_root); + } + else + { + /* + The parser won't create select_result instance only + if it's an EXPLAIN. + */ + DBUG_ASSERT(Lex->describe); + } + } + ; + +select_outvar: + '@' ident_or_text + { + $$ = Lex->result ? new (thd->mem_root) my_var_user(&$2) : NULL; + } + | ident_or_text + { + sp_variable *t; + + if (!Lex->spcont || !(t= Lex->spcont->find_variable(&$1, false))) + my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str)); + $$ = Lex->result ? (new (thd->mem_root) + my_var_sp(&$1, t->offset, t->type_handler(), + Lex->sphead)) : + NULL; + } + | ident '.' ident + { + if (!($$= Lex->create_outvar(thd, &$1, &$3)) && Lex->result) + MYSQL_YYABORT; + } + ; + +into: + INTO into_destination + ; + +into_destination: + OUTFILE TEXT_STRING_filesystem + { + LEX *lex= Lex; + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + if (!(lex->exchange= + new (thd->mem_root) sql_exchange($2.str, 0)) || + !(lex->result= + new (thd->mem_root) select_export(thd, lex->exchange))) + MYSQL_YYABORT; + } + opt_load_data_charset + { Lex->exchange->cs= $4; } + opt_field_term opt_line_term + | DUMPFILE TEXT_STRING_filesystem + { + LEX *lex=Lex; + if (!lex->describe) + { + lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + if (!(lex->exchange= new (thd->mem_root) sql_exchange($2.str,1))) + MYSQL_YYABORT; + if (!(lex->result= + new (thd->mem_root) select_dump(thd, lex->exchange))) + MYSQL_YYABORT; + } + } + | select_var_list_init + { + Lex->uncacheable(UNCACHEABLE_SIDEEFFECT); + } + ; + +/* + DO statement +*/ + +do: + DO_SYM + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_DO; + mysql_init_select(lex); + } + expr_list + { + Lex->insert_list= $3; + } + ; + +/* + Drop : delete tables or index or user +*/ + +drop: + DROP opt_temporary table_or_tables opt_if_exists + { + LEX *lex=Lex; + lex->set_command(SQLCOM_DROP_TABLE, $2, $4); + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list opt_restrict + {} + | DROP INDEX_SYM opt_if_exists_table_element ident ON table_ident {} + { + LEX *lex=Lex; + Alter_drop *ad= (new (thd->mem_root) + Alter_drop(Alter_drop::KEY, $4.str, $3)); + if (ad == NULL) + MYSQL_YYABORT; + lex->sql_command= SQLCOM_DROP_INDEX; + lex->alter_info.reset(); + lex->alter_info.flags= Alter_info::ALTER_DROP_INDEX; + lex->alter_info.drop_list.push_back(ad, thd->mem_root); + if (!lex->current_select->add_table_to_list(thd, $6, NULL, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_UPGRADABLE)) + MYSQL_YYABORT; + } + | DROP DATABASE opt_if_exists ident + { + LEX *lex=Lex; + lex->set_command(SQLCOM_DROP_DB, $3); + lex->name= $4; + } + | DROP FUNCTION_SYM opt_if_exists ident '.' ident + { + LEX *lex= thd->lex; + sp_name *spname; + if ($4.str && check_db_name((LEX_STRING*) &$4)) + my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $4.str)); + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); + lex->set_command(SQLCOM_DROP_FUNCTION, $3); + spname= new (thd->mem_root) sp_name(&$4, &$6, true); + if (spname == NULL) + MYSQL_YYABORT; + lex->spname= spname; + } + | DROP FUNCTION_SYM opt_if_exists ident + { + LEX *lex= thd->lex; + LEX_CSTRING db= {0, 0}; + sp_name *spname; + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "FUNCTION")); + if (thd->db && lex->copy_db_to(&db.str, &db.length)) + MYSQL_YYABORT; + lex->set_command(SQLCOM_DROP_FUNCTION, $3); + spname= new (thd->mem_root) sp_name(&db, &$4, false); + if (spname == NULL) + MYSQL_YYABORT; + lex->spname= spname; + } + | DROP PROCEDURE_SYM opt_if_exists sp_name + { + LEX *lex=Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_NO_DROP_SP, MYF(0), "PROCEDURE")); + lex->set_command(SQLCOM_DROP_PROCEDURE, $3); + lex->spname= $4; + } + | DROP USER_SYM opt_if_exists clear_privileges user_list + { + Lex->set_command(SQLCOM_DROP_USER, $3); + } + | DROP ROLE_SYM opt_if_exists clear_privileges role_list + { + Lex->set_command(SQLCOM_DROP_ROLE, $3); + } + | DROP VIEW_SYM opt_if_exists + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_VIEW, $3); + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list opt_restrict + {} + | DROP EVENT_SYM opt_if_exists sp_name + { + Lex->spname= $4; + Lex->set_command(SQLCOM_DROP_EVENT, $3); + } + | DROP TRIGGER_SYM opt_if_exists sp_name + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_TRIGGER, $3); + lex->spname= $4; + } + | DROP TABLESPACE tablespace_name opt_ts_engine opt_ts_wait + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= DROP_TABLESPACE; + } + | DROP LOGFILE_SYM GROUP_SYM logfile_group_name opt_ts_engine opt_ts_wait + { + LEX *lex= Lex; + lex->alter_tablespace_info->ts_cmd_type= DROP_LOGFILE_GROUP; + } + | DROP SERVER_SYM opt_if_exists ident_or_text + { + Lex->set_command(SQLCOM_DROP_SERVER, $3); + Lex->server_options.reset($4); + } + | DROP opt_temporary SEQUENCE_SYM opt_if_exists + + { + LEX *lex= Lex; + lex->set_command(SQLCOM_DROP_SEQUENCE, $2, $4); + lex->table_type= TABLE_TYPE_SEQUENCE; + YYPS->m_lock_type= TL_UNLOCK; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_list + {} + ; + +table_list: + table_name + | table_list ',' table_name + ; + +table_name: + table_ident + { + if (!Select->add_table_to_list(thd, $1, NULL, + TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type)) + MYSQL_YYABORT; + } + ; + +table_name_with_opt_use_partition: + table_ident opt_use_partition + { + if (!Select->add_table_to_list(thd, $1, NULL, + TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type, + NULL, + $2)) + MYSQL_YYABORT; + } + ; + +table_alias_ref_list: + table_alias_ref + | table_alias_ref_list ',' table_alias_ref + ; + +table_alias_ref: + table_ident_opt_wild + { + if (!Select->add_table_to_list(thd, $1, NULL, + TL_OPTION_UPDATING | TL_OPTION_ALIAS, + YYPS->m_lock_type, + YYPS->m_mdl_type)) + MYSQL_YYABORT; + } + ; + +opt_if_exists_table_element: + /* empty */ + { + Lex->check_exists= FALSE; + $$= 0; + } + | IF_SYM EXISTS + { + Lex->check_exists= TRUE; + $$= 1; + } + ; + +opt_if_exists: + /* empty */ + { + $$.set(DDL_options_st::OPT_NONE); + } + | IF_SYM EXISTS + { + $$.set(DDL_options_st::OPT_IF_EXISTS); + } + ; + +opt_temporary: + /* empty */ { $$= 0; } + | TEMPORARY { $$= HA_LEX_CREATE_TMP_TABLE; } + ; +/* +** Insert : add new data to table +*/ + +insert: + INSERT + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSERT; + lex->duplicates= DUP_ERROR; + mysql_init_select(lex); + } + insert_lock_option + opt_ignore insert2 + { + Select->set_lock_for_tables($3); + Lex->current_select= &Lex->select_lex; + } + insert_field_spec opt_insert_update + {} + ; + +replace: + REPLACE + { + LEX *lex=Lex; + lex->sql_command = SQLCOM_REPLACE; + lex->duplicates= DUP_REPLACE; + mysql_init_select(lex); + } + replace_lock_option insert2 + { + Select->set_lock_for_tables($3); + Lex->current_select= &Lex->select_lex; + } + insert_field_spec + {} + ; + +insert_lock_option: + /* empty */ + { + /* + If it is SP we do not allow insert optimisation when result of + insert visible only after the table unlocking but everyone can + read table. + */ + $$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT); + } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + | DELAYED_SYM + { + Lex->keyword_delayed_begin_offset= (uint)(YYLIP->get_tok_start() - + thd->query()); + Lex->keyword_delayed_end_offset= Lex->keyword_delayed_begin_offset + + YYLIP->yyLength() + 1; + $$= TL_WRITE_DELAYED; + } + | HIGH_PRIORITY { $$= TL_WRITE; } + ; + +replace_lock_option: + opt_low_priority { $$= $1; } + | DELAYED_SYM + { + Lex->keyword_delayed_begin_offset= (uint)(YYLIP->get_tok_start() - + thd->query()); + Lex->keyword_delayed_end_offset= Lex->keyword_delayed_begin_offset + + YYLIP->yyLength() + 1; + $$= TL_WRITE_DELAYED; + } + ; + +insert2: + INTO insert_table {} + | insert_table {} + ; + +insert_table: + table_name_with_opt_use_partition + { + LEX *lex=Lex; + lex->field_list.empty(); + lex->many_values.empty(); + lex->insert_list=0; + }; + +insert_field_spec: + insert_values {} + | '(' ')' insert_values {} + | '(' fields ')' insert_values {} + | SET + { + LEX *lex=Lex; + if (!(lex->insert_list= new (thd->mem_root) List_item) || + lex->many_values.push_back(lex->insert_list, thd->mem_root)) + MYSQL_YYABORT; + } + ident_eq_list + ; + +fields: + fields ',' insert_ident + { Lex->field_list.push_back($3, thd->mem_root); } + | insert_ident { Lex->field_list.push_back($1, thd->mem_root); } + ; + +insert_values: + VALUES values_list {} + | VALUE_SYM values_list {} + | create_select_query_expression {} + ; + +values_list: + values_list ',' no_braces + | no_braces + ; + +ident_eq_list: + ident_eq_list ',' ident_eq_value + | ident_eq_value + ; + +ident_eq_value: + simple_ident_nospvar equal expr_or_default + { + LEX *lex=Lex; + if (lex->field_list.push_back($1, thd->mem_root) || + lex->insert_list->push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +equal: + '=' {} + | SET_VAR {} + ; + +opt_equal: + /* empty */ {} + | equal {} + ; + +opt_with: + opt_equal {} + | WITH {} + ; + +opt_by: + opt_equal {} + | BY {} + ; + +no_braces: + '(' + { + if (!(Lex->insert_list= new (thd->mem_root) List_item)) + MYSQL_YYABORT; + } + opt_values ')' + { + LEX *lex=Lex; + if (lex->many_values.push_back(lex->insert_list, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +opt_values: + /* empty */ {} + | values + ; + +values: + values ',' expr_or_default + { + if (Lex->insert_list->push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + | expr_or_default + { + if (Lex->insert_list->push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +expr_or_default: + expr { $$= $1;} + | DEFAULT + { + $$= new (thd->mem_root) Item_default_value(thd, Lex->current_context()); + if ($$ == NULL) + MYSQL_YYABORT; + } + | IGNORE_SYM + { + $$= new (thd->mem_root) Item_ignore_value(thd, Lex->current_context()); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_insert_update: + /* empty */ + | ON DUPLICATE_SYM { Lex->duplicates= DUP_UPDATE; } + KEY_SYM UPDATE_SYM insert_update_list + ; + +/* Update rows in a table */ + +update: + UPDATE_SYM + { + LEX *lex= Lex; + mysql_init_select(lex); + lex->sql_command= SQLCOM_UPDATE; + lex->duplicates= DUP_ERROR; + } + opt_low_priority opt_ignore join_table_list + SET update_list + { + LEX *lex= Lex; + if (lex->select_lex.table_list.elements > 1) + lex->sql_command= SQLCOM_UPDATE_MULTI; + else if (lex->select_lex.get_table_list()->derived) + { + /* it is single table update and it is update of derived table */ + my_error(ER_NON_UPDATABLE_TABLE, MYF(0), + lex->select_lex.get_table_list()->alias, "UPDATE"); + MYSQL_YYABORT; + } + /* + In case of multi-update setting write lock for all tables may + be too pessimistic. We will decrease lock level if possible in + mysql_multi_update(). + */ + Select->set_lock_for_tables($3); + } + opt_where_clause opt_order_clause delete_limit_clause {} + ; + +update_list: + update_list ',' update_elem + | update_elem + ; + +update_elem: + simple_ident_nospvar equal expr_or_default + { + if (add_item_to_list(thd, $1) || add_value_to_list(thd, $3)) + MYSQL_YYABORT; + } + ; + +insert_update_list: + insert_update_list ',' insert_update_elem + | insert_update_elem + ; + +insert_update_elem: + simple_ident_nospvar equal expr_or_default + { + LEX *lex= Lex; + if (lex->update_list.push_back($1, thd->mem_root) || + lex->value_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +opt_low_priority: + /* empty */ { $$= TL_WRITE_DEFAULT; } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + ; + +/* Delete rows from a table */ + +delete: + DELETE_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_DELETE; + mysql_init_select(lex); + YYPS->m_lock_type= TL_WRITE_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_WRITE; + + lex->ignore= 0; + lex->select_lex.init_order(); + } + opt_delete_options single_multi + ; + +single_multi: + FROM table_ident opt_use_partition + { + if (!Select->add_table_to_list(thd, $2, NULL, TL_OPTION_UPDATING, + YYPS->m_lock_type, + YYPS->m_mdl_type, + NULL, + $3)) + MYSQL_YYABORT; + YYPS->m_lock_type= TL_READ_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_READ; + } + opt_where_clause opt_order_clause + delete_limit_clause {} + opt_select_expressions {} + | table_wild_list + { + mysql_init_multi_delete(Lex); + YYPS->m_lock_type= TL_READ_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_READ; + } + FROM join_table_list opt_where_clause + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + MYSQL_YYABORT; + } + | FROM table_alias_ref_list + { + mysql_init_multi_delete(Lex); + YYPS->m_lock_type= TL_READ_DEFAULT; + YYPS->m_mdl_type= MDL_SHARED_READ; + } + USING join_table_list opt_where_clause + { + if (multi_delete_set_locks_and_link_aux_tables(Lex)) + MYSQL_YYABORT; + } + ; + +opt_select_expressions: + /* empty */ + | RETURNING_SYM select_item_list + ; + +table_wild_list: + table_wild_one + | table_wild_list ',' table_wild_one + ; + +table_wild_one: + ident opt_wild + { + Table_ident *ti= new (thd->mem_root) Table_ident(&$1); + if (ti == NULL) + MYSQL_YYABORT; + if (!Select->add_table_to_list(thd, + ti, + NULL, + TL_OPTION_UPDATING | TL_OPTION_ALIAS, + YYPS->m_lock_type, + YYPS->m_mdl_type)) + MYSQL_YYABORT; + } + | ident '.' ident opt_wild + { + Table_ident *ti= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); + if (ti == NULL) + MYSQL_YYABORT; + if (!Select->add_table_to_list(thd, + ti, + NULL, + TL_OPTION_UPDATING | TL_OPTION_ALIAS, + YYPS->m_lock_type, + YYPS->m_mdl_type)) + MYSQL_YYABORT; + } + ; + +opt_wild: + /* empty */ {} + | '.' '*' {} + ; + +opt_delete_options: + /* empty */ {} + | opt_delete_option opt_delete_options {} + ; + +opt_delete_option: + QUICK { Select->options|= OPTION_QUICK; } + | LOW_PRIORITY { YYPS->m_lock_type= TL_WRITE_LOW_PRIORITY; } + | IGNORE_SYM { Lex->ignore= 1; } + ; + +truncate: + TRUNCATE_SYM opt_table_sym + { + LEX* lex= Lex; + lex->sql_command= SQLCOM_TRUNCATE; + lex->alter_info.reset(); + lex->select_lex.options= 0; + lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED; + lex->select_lex.init_order(); + YYPS->m_lock_type= TL_WRITE; + YYPS->m_mdl_type= MDL_EXCLUSIVE; + } + table_name + { + LEX* lex= thd->lex; + DBUG_ASSERT(!lex->m_sql_cmd); + lex->m_sql_cmd= new (thd->mem_root) Sql_cmd_truncate_table(); + if (lex->m_sql_cmd == NULL) + MYSQL_YYABORT; + } + opt_truncate_table_storage_clause { } + ; + +opt_truncate_table_storage_clause: + /* Empty */ + | DROP STORAGE_SYM + | REUSE_SYM STORAGE_SYM + ; + +opt_table_sym: + /* empty */ + | TABLE_SYM + ; + +opt_profile_defs: + /* empty */ + | profile_defs; + +profile_defs: + profile_def + | profile_defs ',' profile_def; + +profile_def: + CPU_SYM + { + Lex->profile_options|= PROFILE_CPU; + } + | MEMORY_SYM + { + Lex->profile_options|= PROFILE_MEMORY; + } + | BLOCK_SYM IO_SYM + { + Lex->profile_options|= PROFILE_BLOCK_IO; + } + | CONTEXT_SYM SWITCHES_SYM + { + Lex->profile_options|= PROFILE_CONTEXT; + } + | PAGE_SYM FAULTS_SYM + { + Lex->profile_options|= PROFILE_PAGE_FAULTS; + } + | IPC_SYM + { + Lex->profile_options|= PROFILE_IPC; + } + | SWAPS_SYM + { + Lex->profile_options|= PROFILE_SWAPS; + } + | SOURCE_SYM + { + Lex->profile_options|= PROFILE_SOURCE; + } + | ALL + { + Lex->profile_options|= PROFILE_ALL; + } + ; + +opt_profile_args: + /* empty */ + { + Lex->profile_query_id= 0; + } + | FOR_SYM QUERY_SYM NUM + { + Lex->profile_query_id= atoi($3.str); + } + ; + +/* Show things */ + +show: + SHOW + { + LEX *lex=Lex; + lex->wild=0; + lex->ident= null_clex_str; + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + lex->create_info.init(); + } + show_param + { + Select->parsing_place= NO_MATTER; + } + ; + +show_param: + DATABASES wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_DATABASES; + if (prepare_schema_table(thd, lex, 0, SCH_SCHEMATA)) + MYSQL_YYABORT; + } + | opt_full TABLES opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TABLES; + lex->select_lex.db= $3.str; + if (prepare_schema_table(thd, lex, 0, SCH_TABLE_NAMES)) + MYSQL_YYABORT; + } + | opt_full TRIGGERS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TRIGGERS; + lex->select_lex.db= $3.str; + if (prepare_schema_table(thd, lex, 0, SCH_TRIGGERS)) + MYSQL_YYABORT; + } + | EVENTS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_EVENTS; + lex->select_lex.db= $2.str; + if (prepare_schema_table(thd, lex, 0, SCH_EVENTS)) + MYSQL_YYABORT; + } + | TABLE_SYM STATUS_SYM opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TABLE_STATUS; + lex->select_lex.db= $3.str; + if (prepare_schema_table(thd, lex, 0, SCH_TABLES)) + MYSQL_YYABORT; + } + | OPEN_SYM TABLES opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_OPEN_TABLES; + lex->select_lex.db= $3.str; + if (prepare_schema_table(thd, lex, 0, SCH_OPEN_TABLES)) + MYSQL_YYABORT; + } + | PLUGINS_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_PLUGINS; + if (prepare_schema_table(thd, lex, 0, SCH_PLUGINS)) + MYSQL_YYABORT; + } + | PLUGINS_SYM SONAME_SYM TEXT_STRING_sys + { + Lex->ident= $3; + Lex->sql_command= SQLCOM_SHOW_PLUGINS; + if (prepare_schema_table(thd, Lex, 0, SCH_ALL_PLUGINS)) + MYSQL_YYABORT; + } + | PLUGINS_SYM SONAME_SYM wild_and_where + { + Lex->sql_command= SQLCOM_SHOW_PLUGINS; + if (prepare_schema_table(thd, Lex, 0, SCH_ALL_PLUGINS)) + MYSQL_YYABORT; + } + | ENGINE_SYM known_storage_engines show_engine_param + { Lex->create_info.db_type= $2; } + | ENGINE_SYM ALL show_engine_param + { Lex->create_info.db_type= NULL; } + | opt_full COLUMNS from_or_in table_ident opt_db wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_FIELDS; + if ($5.str) + $4->change_db(&$5); + if (prepare_schema_table(thd, lex, $4, SCH_COLUMNS)) + MYSQL_YYABORT; + } + | master_or_binary LOGS_SYM + { + Lex->sql_command = SQLCOM_SHOW_BINLOGS; + } + | SLAVE HOSTS_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_HOSTS; + } + | BINLOG_SYM EVENTS_SYM binlog_in binlog_from + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_BINLOG_EVENTS; + } + opt_limit_clause + | RELAYLOG_SYM optional_connection_name EVENTS_SYM binlog_in binlog_from + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_RELAYLOG_EVENTS; + } opt_limit_clause + | keys_or_index from_or_in table_ident opt_db opt_where_clause + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_KEYS; + if ($4.str) + $3->change_db(&$4); + if (prepare_schema_table(thd, lex, $3, SCH_STATISTICS)) + MYSQL_YYABORT; + } + | opt_storage ENGINES_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_STORAGE_ENGINES; + if (prepare_schema_table(thd, lex, 0, SCH_ENGINES)) + MYSQL_YYABORT; + } + | AUTHORS_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_AUTHORS; + } + | CONTRIBUTORS_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_CONTRIBUTORS; + } + | PRIVILEGES + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_PRIVILEGES; + } + | COUNT_SYM '(' '*' ')' WARNINGS + { (void) create_select_for_variable("warning_count"); } + | COUNT_SYM '(' '*' ')' ERRORS + { (void) create_select_for_variable("error_count"); } + | WARNINGS opt_limit_clause + { Lex->sql_command = SQLCOM_SHOW_WARNS;} + | ERRORS opt_limit_clause + { Lex->sql_command = SQLCOM_SHOW_ERRORS;} + | PROFILES_SYM + { Lex->sql_command = SQLCOM_SHOW_PROFILES; } + | PROFILE_SYM opt_profile_defs opt_profile_args opt_limit_clause + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_PROFILE; + if (prepare_schema_table(thd, lex, NULL, SCH_PROFILES) != 0) + MYSQL_YYABORT; + } + | opt_var_type STATUS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_STATUS; + lex->option_type= $1; + if (prepare_schema_table(thd, lex, 0, SCH_SESSION_STATUS)) + MYSQL_YYABORT; + } + | opt_full PROCESSLIST_SYM + { Lex->sql_command= SQLCOM_SHOW_PROCESSLIST;} + | opt_var_type VARIABLES wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_VARIABLES; + lex->option_type= $1; + if (prepare_schema_table(thd, lex, 0, SCH_SESSION_VARIABLES)) + MYSQL_YYABORT; + } + | charset wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_CHARSETS; + if (prepare_schema_table(thd, lex, 0, SCH_CHARSETS)) + MYSQL_YYABORT; + } + | COLLATION_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_COLLATIONS; + if (prepare_schema_table(thd, lex, 0, SCH_COLLATIONS)) + MYSQL_YYABORT; + } + | GRANTS + { + Lex->sql_command= SQLCOM_SHOW_GRANTS; + if (!(Lex->grant_user= (LEX_USER*)thd->alloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + Lex->grant_user->user= current_user_and_current_role; + } + | GRANTS FOR_SYM user_or_role clear_privileges + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SHOW_GRANTS; + lex->grant_user=$3; + } + | CREATE DATABASE opt_if_not_exists ident + { + Lex->set_command(SQLCOM_SHOW_CREATE_DB, $3); + Lex->name= $4; + } + | CREATE TABLE_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL,0)) + MYSQL_YYABORT; + lex->create_info.storage_media= HA_SM_DEFAULT; + } + | CREATE VIEW_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) + MYSQL_YYABORT; + lex->table_type= TABLE_TYPE_VIEW; + } + | CREATE SEQUENCE_SYM table_ident + { + LEX *lex= Lex; + lex->sql_command = SQLCOM_SHOW_CREATE; + if (!lex->select_lex.add_table_to_list(thd, $3, NULL, 0)) + MYSQL_YYABORT; + lex->table_type= TABLE_TYPE_SEQUENCE; + } + | MASTER_SYM STATUS_SYM + { + Lex->sql_command = SQLCOM_SHOW_MASTER_STAT; + } + | ALL SLAVES STATUS_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; + Lex->verbose= 1; + } + | SLAVE STATUS_SYM + { + LEX *lex= thd->lex; + lex->mi.connection_name= null_clex_str; + lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; + lex->verbose= 0; + } + | SLAVE connection_name STATUS_SYM + { + Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; + Lex->verbose= 0; + } + | CREATE PROCEDURE_SYM sp_name + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_PROC; + lex->spname= $3; + } + | CREATE FUNCTION_SYM sp_name + { + LEX *lex= Lex; + + lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; + lex->spname= $3; + } + | CREATE TRIGGER_SYM sp_name + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_CREATE_TRIGGER; + lex->spname= $3; + } + | CREATE USER_SYM + { + Lex->sql_command= SQLCOM_SHOW_CREATE_USER; + if (!(Lex->grant_user= (LEX_USER*)thd->alloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + Lex->grant_user->user= current_user; + } + | CREATE USER_SYM user + { + Lex->sql_command= SQLCOM_SHOW_CREATE_USER; + Lex->grant_user= $3; + } + | PROCEDURE_SYM STATUS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_STATUS_PROC; + if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)) + MYSQL_YYABORT; + } + | FUNCTION_SYM STATUS_SYM wild_and_where + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_STATUS_FUNC; + if (prepare_schema_table(thd, lex, 0, SCH_PROCEDURES)) + MYSQL_YYABORT; + } + | PROCEDURE_SYM CODE_SYM sp_name + { + Lex->sql_command= SQLCOM_SHOW_PROC_CODE; + Lex->spname= $3; + } + | FUNCTION_SYM CODE_SYM sp_name + { + Lex->sql_command= SQLCOM_SHOW_FUNC_CODE; + Lex->spname= $3; + } + | CREATE EVENT_SYM sp_name + { + Lex->spname= $3; + Lex->sql_command = SQLCOM_SHOW_CREATE_EVENT; + } + | describe_command FOR_SYM expr + { + Lex->sql_command= SQLCOM_SHOW_EXPLAIN; + if (prepare_schema_table(thd, Lex, 0, SCH_EXPLAIN)) + MYSQL_YYABORT; + add_value_to_list(thd, $3); + } + | IDENT_sys remember_tok_start wild_and_where + { + LEX *lex= Lex; + bool in_plugin; + lex->sql_command= SQLCOM_SHOW_GENERIC; + ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str, &in_plugin); + if (!table || !table->old_format || !in_plugin) + { + thd->parse_error(ER_SYNTAX_ERROR, $2); + MYSQL_YYABORT; + } + if (lex->wild && table->idx_field1 < 0) + { + thd->parse_error(ER_SYNTAX_ERROR, $3); + MYSQL_YYABORT; + } + if (make_schema_select(thd, Lex->current_select, table)) + MYSQL_YYABORT; + } + ; + +show_engine_param: + STATUS_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_STATUS; } + | MUTEX_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_MUTEX; } + | LOGS_SYM + { Lex->sql_command= SQLCOM_SHOW_ENGINE_LOGS; } + ; + +master_or_binary: + MASTER_SYM + | BINARY + ; + +opt_storage: + /* empty */ + | STORAGE_SYM + ; + +opt_db: + /* empty */ { $$= null_clex_str; } + | from_or_in ident { $$= $2; } + ; + +opt_full: + /* empty */ { Lex->verbose=0; } + | FULL { Lex->verbose=1; } + ; + +from_or_in: + FROM + | IN_SYM + ; + +binlog_in: + /* empty */ { Lex->mi.log_file_name = 0; } + | IN_SYM TEXT_STRING_sys { Lex->mi.log_file_name = $2.str; } + ; + +binlog_from: + /* empty */ { Lex->mi.pos = 4; /* skip magic number */ } + | FROM ulonglong_num { Lex->mi.pos = $2; } + ; + +wild_and_where: + /* empty */ { $$= 0; } + | LIKE remember_tok_start TEXT_STRING_sys + { + Lex->wild= new (thd->mem_root) String($3.str, $3.length, + system_charset_info); + if (Lex->wild == NULL) + MYSQL_YYABORT; + $$= $2; + } + | WHERE remember_tok_start expr + { + Select->where= normalize_cond(thd, $3); + if ($3) + $3->top_level_item(); + $$= $2; + } + ; + +/* A Oracle compatible synonym for show */ +describe: + describe_command table_ident + { + LEX *lex= Lex; + mysql_init_select(lex); + lex->current_select->parsing_place= SELECT_LIST; + lex->sql_command= SQLCOM_SHOW_FIELDS; + lex->select_lex.db= 0; + lex->verbose= 0; + if (prepare_schema_table(thd, lex, $2, SCH_COLUMNS)) + MYSQL_YYABORT; + } + opt_describe_column + { + Select->parsing_place= NO_MATTER; + } + | describe_command opt_extended_describe + { Lex->describe|= DESCRIBE_NORMAL; } + explainable_command + { + LEX *lex=Lex; + lex->select_lex.options|= SELECT_DESCRIBE; + } + ; + +explainable_command: + select + | insert + | replace + | update + | delete + ; + +describe_command: + DESC + | DESCRIBE + ; + +analyze_stmt_command: + ANALYZE_SYM opt_format_json explainable_command + { + Lex->analyze_stmt= true; + } + ; + +opt_extended_describe: + EXTENDED_SYM { Lex->describe|= DESCRIBE_EXTENDED; } + | PARTITIONS_SYM { Lex->describe|= DESCRIBE_PARTITIONS; } + | opt_format_json {} + ; + +opt_format_json: + /* empty */ {} + | FORMAT_SYM '=' ident_or_text + { + if (!my_strcasecmp(system_charset_info, $3.str, "JSON")) + Lex->explain_json= true; + else if (!my_strcasecmp(system_charset_info, $3.str, "TRADITIONAL")) + DBUG_ASSERT(Lex->explain_json==false); + else + my_yyabort_error((ER_UNKNOWN_EXPLAIN_FORMAT, MYF(0), $3.str)); + } + ; + +opt_describe_column: + /* empty */ {} + | text_string { Lex->wild= $1; } + | ident + { + Lex->wild= new (thd->mem_root) String((const char*) $1.str, + $1.length, + system_charset_info); + if (Lex->wild == NULL) + MYSQL_YYABORT; + } + ; + + +/* flush things */ + +flush: + FLUSH_SYM opt_no_write_to_binlog + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_FLUSH; + lex->type= 0; + lex->no_write_to_binlog= $2; + } + flush_options + {} + ; + +flush_options: + table_or_tables + { + Lex->type|= REFRESH_TABLES; + /* + Set type of metadata and table locks for + FLUSH TABLES table_list [WITH READ LOCK]. + */ + YYPS->m_lock_type= TL_READ_NO_INSERT; + YYPS->m_mdl_type= MDL_SHARED_HIGH_PRIO; + } + opt_table_list opt_flush_lock + {} + | flush_options_list + ; + +opt_flush_lock: + /* empty */ {} + | flush_lock + { + TABLE_LIST *tables= Lex->query_tables; + for (; tables; tables= tables->next_global) + { + tables->mdl_request.set_type(MDL_SHARED_NO_WRITE); + /* Don't try to flush views. */ + tables->required_type= TABLE_TYPE_NORMAL; + /* Ignore temporary tables. */ + tables->open_type= OT_BASE_ONLY; + } + } + ; + +flush_lock: + WITH READ_SYM LOCK_SYM optional_flush_tables_arguments + { Lex->type|= REFRESH_READ_LOCK | $4; } + | FOR_SYM + { + if (Lex->query_tables == NULL) // Table list can't be empty + { + thd->parse_error(ER_NO_TABLES_USED); + MYSQL_YYABORT; + } + Lex->type|= REFRESH_FOR_EXPORT; + } EXPORT_SYM {} + ; + +flush_options_list: + flush_options_list ',' flush_option + | flush_option + {} + ; + +flush_option: + ERROR_SYM LOGS_SYM + { Lex->type|= REFRESH_ERROR_LOG; } + | ENGINE_SYM LOGS_SYM + { Lex->type|= REFRESH_ENGINE_LOG; } + | GENERAL LOGS_SYM + { Lex->type|= REFRESH_GENERAL_LOG; } + | SLOW LOGS_SYM + { Lex->type|= REFRESH_SLOW_LOG; } + | BINARY LOGS_SYM + { Lex->type|= REFRESH_BINARY_LOG; } + | RELAY LOGS_SYM optional_connection_name + { + LEX *lex= Lex; + if (lex->type & REFRESH_RELAY_LOG) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "FLUSH", "RELAY LOGS")); + lex->type|= REFRESH_RELAY_LOG; + lex->relay_log_connection_name= lex->mi.connection_name; + } + | QUERY_SYM CACHE_SYM + { Lex->type|= REFRESH_QUERY_CACHE_FREE; } + | HOSTS_SYM + { Lex->type|= REFRESH_HOSTS; } + | PRIVILEGES + { Lex->type|= REFRESH_GRANT; } + | LOGS_SYM + { + Lex->type|= REFRESH_LOG; + Lex->relay_log_connection_name= empty_clex_str; + } + | STATUS_SYM + { Lex->type|= REFRESH_STATUS; } + | SLAVE optional_connection_name + { + LEX *lex= Lex; + if (lex->type & REFRESH_SLAVE) + my_yyabort_error((ER_WRONG_USAGE, MYF(0), "FLUSH","SLAVE")); + lex->type|= REFRESH_SLAVE; + lex->reset_slave_info.all= false; + } + | MASTER_SYM + { Lex->type|= REFRESH_MASTER; } + | DES_KEY_FILE + { Lex->type|= REFRESH_DES_KEY_FILE; } + | RESOURCES + { Lex->type|= REFRESH_USER_RESOURCES; } + | IDENT_sys remember_tok_start + { + Lex->type|= REFRESH_GENERIC; + ST_SCHEMA_TABLE *table= find_schema_table(thd, $1.str); + if (!table || !table->reset_table) + { + thd->parse_error(ER_SYNTAX_ERROR, $2); + MYSQL_YYABORT; + } + Lex->view_list.push_back((LEX_STRING*) + thd->memdup(&$1, sizeof(LEX_STRING)), + thd->mem_root); + } + ; + +opt_table_list: + /* empty */ {} + | table_list {} + ; + +optional_flush_tables_arguments: + /* empty */ {$$= 0;} + | AND_SYM DISABLE_SYM CHECKPOINT_SYM {$$= REFRESH_CHECKPOINT; } + +reset: + RESET_SYM + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_RESET; lex->type=0; + } + reset_options + {} + ; + +reset_options: + reset_options ',' reset_option + | reset_option + ; + +reset_option: + SLAVE { Lex->type|= REFRESH_SLAVE; } + optional_connection_name + slave_reset_options { } + | MASTER_SYM + { + Lex->type|= REFRESH_MASTER; + Lex->next_binlog_file_number= 0; + } + master_reset_options + | QUERY_SYM CACHE_SYM { Lex->type|= REFRESH_QUERY_CACHE;} + ; + +slave_reset_options: + /* empty */ { Lex->reset_slave_info.all= false; } + | ALL { Lex->reset_slave_info.all= true; } + ; + +master_reset_options: + /* empty */ {} + | TO_SYM ulong_num + { + Lex->next_binlog_file_number = $2; + } + ; + +purge: + PURGE + { + LEX *lex=Lex; + lex->type=0; + lex->sql_command = SQLCOM_PURGE; + } + purge_options + {} + ; + +purge_options: + master_or_binary LOGS_SYM purge_option + ; + +purge_option: + TO_SYM TEXT_STRING_sys + { + Lex->to_log = $2.str; + } + | BEFORE_SYM expr + { + LEX *lex= Lex; + lex->value_list.empty(); + lex->value_list.push_front($2, thd->mem_root); + lex->sql_command= SQLCOM_PURGE_BEFORE; + } + ; + +/* kill threads */ + +kill: + KILL_SYM + { + LEX *lex=Lex; + lex->value_list.empty(); + lex->users_list.empty(); + lex->sql_command= SQLCOM_KILL; + lex->kill_type= KILL_TYPE_ID; + } + kill_type kill_option kill_expr + { + Lex->kill_signal= (killed_state) ($3 | $4); + } + ; + +kill_type: + /* Empty */ { $$= (int) KILL_HARD_BIT; } + | HARD_SYM { $$= (int) KILL_HARD_BIT; } + | SOFT_SYM { $$= 0; } + +kill_option: + /* empty */ { $$= (int) KILL_CONNECTION; } + | CONNECTION_SYM { $$= (int) KILL_CONNECTION; } + | QUERY_SYM { $$= (int) KILL_QUERY; } + | QUERY_SYM ID_SYM + { + $$= (int) KILL_QUERY; + Lex->kill_type= KILL_TYPE_QUERY; + } + ; + +kill_expr: + expr + { + Lex->value_list.push_front($$, thd->mem_root); + } + | USER_SYM user + { + Lex->users_list.push_back($2, thd->mem_root); + Lex->kill_type= KILL_TYPE_USER; + } + ; + + +shutdown: + SHUTDOWN { Lex->sql_command= SQLCOM_SHUTDOWN; } + ; + +/* change database */ + +use: + USE_SYM ident + { + LEX *lex=Lex; + lex->sql_command=SQLCOM_CHANGE_DB; + lex->select_lex.db= $2.str; + } + ; + +/* import, export of files */ + +load: + LOAD data_or_xml + { + LEX *lex= thd->lex; + + if (lex->sphead) + { + my_error(ER_SP_BADSTATEMENT, MYF(0), + $2 == FILETYPE_CSV ? "LOAD DATA" : "LOAD XML"); + MYSQL_YYABORT; + } + } + load_data_lock opt_local INFILE TEXT_STRING_filesystem + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_LOAD; + lex->local_file= $5; + lex->duplicates= DUP_ERROR; + lex->ignore= 0; + if (!(lex->exchange= new (thd->mem_root) sql_exchange($7.str, 0, $2))) + MYSQL_YYABORT; + } + opt_duplicate INTO TABLE_SYM table_ident opt_use_partition + { + LEX *lex=Lex; + if (!Select->add_table_to_list(thd, $12, NULL, TL_OPTION_UPDATING, + $4, MDL_SHARED_WRITE, NULL, $13)) + MYSQL_YYABORT; + lex->field_list.empty(); + lex->update_list.empty(); + lex->value_list.empty(); + } + opt_load_data_charset + { Lex->exchange->cs= $15; } + opt_xml_rows_identified_by + opt_field_term opt_line_term opt_ignore_lines opt_field_or_var_spec + opt_load_data_set_spec + {} + ; + +data_or_xml: + DATA_SYM { $$= FILETYPE_CSV; } + | XML_SYM { $$= FILETYPE_XML; } + ; + +opt_local: + /* empty */ { $$=0;} + | LOCAL_SYM { $$=1;} + ; + +load_data_lock: + /* empty */ { $$= TL_WRITE_DEFAULT; } + | CONCURRENT + { + /* + Ignore this option in SP to avoid problem with query cache and + triggers with non default priority locks + */ + $$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT); + } + | LOW_PRIORITY { $$= TL_WRITE_LOW_PRIORITY; } + ; + +opt_duplicate: + /* empty */ { Lex->duplicates=DUP_ERROR; } + | REPLACE { Lex->duplicates=DUP_REPLACE; } + | IGNORE_SYM { Lex->ignore= 1; } + ; + +opt_field_term: + /* empty */ + | COLUMNS field_term_list + ; + +field_term_list: + field_term_list field_term + | field_term + ; + +field_term: + TERMINATED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->field_term= $3; + } + | OPTIONALLY ENCLOSED BY text_string + { + LEX *lex= Lex; + DBUG_ASSERT(lex->exchange != 0); + lex->exchange->enclosed= $4; + lex->exchange->opt_enclosed= 1; + } + | ENCLOSED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->enclosed= $3; + } + | ESCAPED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->escaped= $3; + } + ; + +opt_line_term: + /* empty */ + | LINES line_term_list + ; + +line_term_list: + line_term_list line_term + | line_term + ; + +line_term: + TERMINATED BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->line_term= $3; + } + | STARTING BY text_string + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->line_start= $3; + } + ; + +opt_xml_rows_identified_by: + /* empty */ { } + | ROWS_SYM IDENTIFIED_SYM BY text_string + { Lex->exchange->line_term = $4; }; + +opt_ignore_lines: + /* empty */ + | IGNORE_SYM NUM lines_or_rows + { + DBUG_ASSERT(Lex->exchange != 0); + Lex->exchange->skip_lines= atol($2.str); + } + ; + +lines_or_rows: + LINES { } + | ROWS_SYM { } + ; + +opt_field_or_var_spec: + /* empty */ {} + | '(' fields_or_vars ')' {} + | '(' ')' {} + ; + +fields_or_vars: + fields_or_vars ',' field_or_var + { Lex->field_list.push_back($3, thd->mem_root); } + | field_or_var + { Lex->field_list.push_back($1, thd->mem_root); } + ; + +field_or_var: + simple_ident_nospvar {$$= $1;} + | '@' ident_or_text + { + $$= new (thd->mem_root) Item_user_var_as_out_param(thd, &$2); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +opt_load_data_set_spec: + /* empty */ {} + | SET load_data_set_list {} + ; + +load_data_set_list: + load_data_set_list ',' load_data_set_elem + | load_data_set_elem + ; + +load_data_set_elem: + simple_ident_nospvar equal remember_name expr_or_default remember_end + { + LEX *lex= Lex; + if (lex->update_list.push_back($1, thd->mem_root) || + lex->value_list.push_back($4, thd->mem_root)) + MYSQL_YYABORT; + $4->set_name_no_truncate(thd, $3, (uint) ($5 - $3), thd->charset()); + } + ; + +/* Common definitions */ + +text_literal: + TEXT_STRING + { + if (!($$= thd->make_string_literal($1))) + MYSQL_YYABORT; + } + | NCHAR_STRING + { + DBUG_ASSERT(my_charset_is_ascii_based(national_charset_info)); + $$= new (thd->mem_root) Item_string(thd, $1.str, $1.length, + national_charset_info, + DERIVATION_COERCIBLE, + $1.repertoire()); + if ($$ == NULL) + MYSQL_YYABORT; + } + | UNDERSCORE_CHARSET TEXT_STRING + { + $$= new (thd->mem_root) Item_string_with_introducer(thd, $2.str, + $2.length, $1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | text_literal TEXT_STRING_literal + { + Item_string* item= (Item_string*) $1; + item->append($2.str, $2.length); + if (!(item->collation.repertoire & MY_REPERTOIRE_EXTENDED)) + { + /* + If the string has been pure ASCII so far, + check the new part. + */ + CHARSET_INFO *cs= thd->variables.collation_connection; + item->collation.repertoire|= my_string_repertoire(cs, + $2.str, + $2.length); + } + } + ; + +text_string: + TEXT_STRING_literal + { + $$= new (thd->mem_root) String($1.str, + $1.length, + thd->variables.collation_connection); + if ($$ == NULL) + MYSQL_YYABORT; + } + | hex_or_bin_String { $$= $1; } + ; + + +hex_or_bin_String: + HEX_NUM + { + Item *tmp= new (thd->mem_root) Item_hex_hybrid(thd, $1.str, + $1.length); + if (tmp == NULL) + MYSQL_YYABORT; + /* + it is OK only emulate fix_fields, because we need only + value of constant + */ + tmp->quick_fix_field(); + $$= tmp->val_str((String*) 0); + } + | HEX_STRING + { + Item *tmp= new (thd->mem_root) Item_hex_string(thd, $1.str, + $1.length); + if (tmp == NULL) + MYSQL_YYABORT; + tmp->quick_fix_field(); + $$= tmp->val_str((String*) 0); + } + | BIN_NUM + { + Item *tmp= new (thd->mem_root) Item_bin_string(thd, $1.str, + $1.length); + if (tmp == NULL) + MYSQL_YYABORT; + /* + it is OK only emulate fix_fields, because we need only + value of constant + */ + tmp->quick_fix_field(); + $$= tmp->val_str((String*) 0); + } + ; + +param_marker: + PARAM_MARKER + { + if (!($$= Lex->add_placeholder(thd, ¶m_clex_str, + YYLIP->get_tok_start(), + YYLIP->get_tok_start() + 1))) + MYSQL_YYABORT; + } + | colon_with_pos ident + { + if (!($$= Lex->add_placeholder(thd, &null_clex_str, + $1, YYLIP->get_tok_end()))) + MYSQL_YYABORT; + } + | colon_with_pos NUM + { + if (!($$= Lex->add_placeholder(thd, &null_clex_str, + $1, YYLIP->get_ptr()))) + MYSQL_YYABORT; + } + ; + +signed_literal: + '+' NUM_literal { $$ = $2; } + | '-' NUM_literal + { + $2->max_length++; + $$= $2->neg(thd); + } + ; + +literal: + text_literal { $$ = $1; } + | NUM_literal { $$ = $1; } + | temporal_literal { $$= $1; } + | NULL_SYM + { + /* + For the digest computation, in this context only, + NULL is considered a literal, hence reduced to '?' + REDUCE: + TOK_GENERIC_VALUE := NULL_SYM + */ + YYLIP->reduce_digest_token(TOK_GENERIC_VALUE, NULL_SYM); + $$= new (thd->mem_root) Item_null(thd); + if ($$ == NULL) + MYSQL_YYABORT; + YYLIP->next_state= MY_LEX_OPERATOR_OR_IDENT; + } + | FALSE_SYM + { + $$= new (thd->mem_root) Item_bool(thd, (char*) "FALSE",0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | TRUE_SYM + { + $$= new (thd->mem_root) Item_bool(thd, (char*) "TRUE",1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | HEX_NUM + { + $$= new (thd->mem_root) Item_hex_hybrid(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | HEX_STRING + { + $$= new (thd->mem_root) Item_hex_string(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BIN_NUM + { + $$= new (thd->mem_root) Item_bin_string(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | UNDERSCORE_CHARSET hex_or_bin_String + { + Item_string_with_introducer *item_str; + /* + Pass NULL as name. Name will be set in the "select_item" rule and + will include the introducer and the original hex/bin notation. + */ + item_str= new (thd->mem_root) + Item_string_with_introducer(thd, NULL, $2->ptr(), $2->length(), + $1); + if (!item_str || !item_str->check_well_formed_result(true)) + MYSQL_YYABORT; + + $$= item_str; + } + ; + +NUM_literal: + NUM + { + int error; + $$= new (thd->mem_root) + Item_int(thd, $1.str, + (longlong) my_strtoll10($1.str, NULL, &error), + $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | LONG_NUM + { + int error; + $$= new (thd->mem_root) + Item_int(thd, $1.str, + (longlong) my_strtoll10($1.str, NULL, &error), + $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ULONGLONG_NUM + { + $$= new (thd->mem_root) Item_uint(thd, $1.str, $1.length); + if ($$ == NULL) + MYSQL_YYABORT; + } + | DECIMAL_NUM + { + $$= new (thd->mem_root) Item_decimal(thd, $1.str, $1.length, + thd->charset()); + if (($$ == NULL) || (thd->is_error())) + { + MYSQL_YYABORT; + } + } + | FLOAT_NUM + { + $$= new (thd->mem_root) Item_float(thd, $1.str, $1.length); + if (($$ == NULL) || (thd->is_error())) + { + MYSQL_YYABORT; + } + } + ; + + +temporal_literal: + DATE_SYM TEXT_STRING + { + if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL, + MYSQL_TYPE_DATE, true))) + MYSQL_YYABORT; + } + | TIME_SYM TEXT_STRING + { + if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL, + MYSQL_TYPE_TIME, true))) + MYSQL_YYABORT; + } + | TIMESTAMP TEXT_STRING + { + if (!($$= create_temporal_literal(thd, $2.str, $2.length, YYCSCL, + MYSQL_TYPE_DATETIME, true))) + MYSQL_YYABORT; + } + ; + + +opt_with_clause: + /*empty */ { $$= 0; } + | with_clause + { + $$= $1; + } + ; + + +with_clause: + WITH opt_recursive + { + With_clause *with_clause= + new With_clause($2, Lex->curr_with_clause); + if (with_clause == NULL) + MYSQL_YYABORT; + Lex->derived_tables|= DERIVED_WITH; + Lex->curr_with_clause= with_clause; + with_clause->add_to_list(Lex->with_clauses_list_last_next); + } + with_list + { + $$= Lex->curr_with_clause; + Lex->curr_with_clause= Lex->curr_with_clause->pop(); + } + ; + + +opt_recursive: + /*empty*/ { $$= 0; } + | RECURSIVE_SYM { $$= 1; } + ; + + +with_list: + with_list_element + | with_list ',' with_list_element + ; + + +with_list_element: + query_name + opt_with_column_list + { + $2= new List<LEX_CSTRING> (Lex->with_column_list); + if ($2 == NULL) + MYSQL_YYABORT; + Lex->with_column_list.empty(); + } + AS '(' remember_name subselect remember_end ')' + { + With_element *elem= new With_element($1, *$2, $7->master_unit()); + if (elem == NULL || Lex->curr_with_clause->add_with_element(elem)) + MYSQL_YYABORT; + if (elem->set_unparsed_spec(thd, $6+1, $8)) + MYSQL_YYABORT; + } + ; + + +opt_with_column_list: + /* empty */ + { $$= NULL; } + | '(' with_column_list ')' + { $$= NULL; } + ; + + +with_column_list: + ident + { + Lex->with_column_list.push_back((LEX_CSTRING*) + thd->memdup(&$1, sizeof(LEX_CSTRING))); + } + | with_column_list ',' ident + { + Lex->with_column_list.push_back((LEX_CSTRING*) + thd->memdup(&$3, sizeof(LEX_CSTRING))); + } + ; + + +query_name: + ident + { + $$= (LEX_CSTRING *) thd->memdup(&$1, sizeof(LEX_CSTRING)); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + + + +/********************************************************************** +** Creating different items. +**********************************************************************/ + +insert_ident: + simple_ident_nospvar { $$=$1; } + | table_wild { $$=$1; } + ; + +table_wild: + ident '.' '*' + { + SELECT_LEX *sel= Select; + $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), + NullS, $1.str, &star_clex_str); + if ($$ == NULL) + MYSQL_YYABORT; + sel->with_wild++; + } + | ident '.' ident '.' '*' + { + SELECT_LEX *sel= Select; + const char* schema= thd->client_capabilities & CLIENT_NO_SCHEMA ? + NullS : $1.str; + $$= new (thd->mem_root) Item_field(thd, Lex->current_context(), + schema, + $3.str, &star_clex_str); + if ($$ == NULL) + MYSQL_YYABORT; + sel->with_wild++; + } + ; + +order_ident: + expr { $$=$1; } + ; + +simple_ident: + ident + { + Lex_input_stream *lip= YYLIP; + if (!($$= Lex->create_item_ident(thd, &$1, + lip->get_tok_start_prev(), + lip->get_tok_end()))) + MYSQL_YYABORT; + } + | simple_ident_q2 + | ident '.' ident + { + LEX *lex= thd->lex; + if (!($$= lex->create_item_ident(thd, &$1, &$3, + $1.m_pos, YYLIP->get_tok_end()))) + MYSQL_YYABORT; + } + ; + +simple_ident_nospvar: + ident + { + if (!($$= Lex->create_item_ident_nosp(thd, &$1))) + MYSQL_YYABORT; + } + | simple_ident_q { $$= $1; } + ; + +simple_ident_q: + ident '.' ident + { + if (!($$= Lex->create_item_ident_nospvar(thd, &$1, &$3))) + MYSQL_YYABORT; + } + | simple_ident_q2 + ; + +simple_ident_q2: + colon_with_pos ident '.' ident + { + LEX *lex= Lex; + if (lex->is_trigger_new_or_old_reference(&$2)) + { + bool new_row= ($2.str[0]=='N' || $2.str[0]=='n'); + if (!($$= Lex->create_and_link_Item_trigger_field(thd, + &$4, + new_row))) + MYSQL_YYABORT; + } + else + { + thd->parse_error(); + MYSQL_YYABORT; + } + } + | '.' ident '.' ident + { + if (!($$= Lex->create_item_ident(thd, &null_clex_str, &$2, &$4))) + MYSQL_YYABORT; + } + | ident '.' ident '.' ident + { + if (!($$= Lex->create_item_ident(thd, &$1, &$3, &$5))) + MYSQL_YYABORT; + } + ; + +field_ident: + ident { $$=$1;} + | ident '.' ident '.' ident + { + TABLE_LIST *table= Select->table_list.first; + if (my_strcasecmp(table_alias_charset, $1.str, table->db)) + my_yyabort_error((ER_WRONG_DB_NAME, MYF(0), $1.str)); + if (my_strcasecmp(table_alias_charset, $3.str, + table->table_name)) + my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $3.str)); + $$=$5; + } + | ident '.' ident + { + TABLE_LIST *table= Select->table_list.first; + if (my_strcasecmp(table_alias_charset, $1.str, table->alias)) + my_yyabort_error((ER_WRONG_TABLE_NAME, MYF(0), $1.str)); + $$=$3; + } + | '.' ident { $$=$2;} /* For Delphi */ + ; + +table_ident: + ident + { + $$= new (thd->mem_root) Table_ident(&$1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ident '.' ident + { + $$= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '.' ident + { + /* For Delphi */ + $$= new (thd->mem_root) Table_ident(&$2); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +table_ident_opt_wild: + ident opt_wild + { + $$= new (thd->mem_root) Table_ident(&$1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ident '.' ident opt_wild + { + $$= new (thd->mem_root) Table_ident(thd, &$1, &$3, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +table_ident_nodb: + ident + { + LEX_CSTRING db={(char*) any_db,3}; + $$= new (thd->mem_root) Table_ident(thd, &db, &$1, 0); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +IDENT_sys: + IDENT { $$= $1; } + | IDENT_QUOTED + { + if (thd->charset_is_system_charset) + { + CHARSET_INFO *cs= system_charset_info; + uint wlen= Well_formed_prefix(cs, $1.str, $1.length).length(); + if (wlen < $1.length) + { + ErrConvString err($1.str, $1.length, &my_charset_bin); + my_error(ER_INVALID_CHARACTER_STRING, MYF(0), + cs->csname, err.ptr()); + MYSQL_YYABORT; + } + $$= $1; + } + else + { + LEX_STRING to; + if (thd->convert_with_error(system_charset_info, &to, + thd->charset(), $1.str, $1.length)) + MYSQL_YYABORT; + $$.str= to.str; + $$.length= to.length; + } + } + ; + +TEXT_STRING_sys: + TEXT_STRING + { + if (thd->charset_is_system_charset) + $$= $1; + else + { + LEX_STRING to; + if (thd->convert_string(&to, system_charset_info, + $1.str, $1.length, thd->charset())) + MYSQL_YYABORT; + $$.str= to.str; + $$.length= to.length; + } + } + ; + +TEXT_STRING_literal: + TEXT_STRING + { + if (thd->charset_is_collation_connection) + $$= $1; + else + { + LEX_STRING to; + if (thd->convert_string(&to, thd->variables.collation_connection, + $1.str, $1.length, thd->charset())) + MYSQL_YYABORT; + $$.str= to.str; + $$.length= to.length; + } + } + ; + +TEXT_STRING_filesystem: + TEXT_STRING + { + if (thd->charset_is_character_set_filesystem) + $$= $1; + else + { + LEX_STRING to; + if (thd->convert_string(&to, + thd->variables.character_set_filesystem, + $1.str, $1.length, thd->charset())) + MYSQL_YYABORT; + $$.str= to.str; + $$.length= to.length; + } + } + ; + +ident: + IDENT_sys + { + (LEX_CSTRING &)$$= $1; + $$.m_pos= (char *) YYLIP->get_tok_start_prev(); + } + | keyword + { + $$.str= thd->strmake($1.str, $1.length); + if ($$.str == NULL) + MYSQL_YYABORT; + $$.length= $1.length; + $$.m_pos= (char *) YYLIP->get_tok_start_prev(); + } + ; + +ident_with_tok_start: + IDENT_sys + { + (LEX_CSTRING &)$$= $1; + $$.m_pos= (char *) YYLIP->get_tok_start(); + } + | keyword + { + if (!($$.str= thd->strmake($1.str, $1.length))) + MYSQL_YYABORT; + $$.length= $1.length; + $$.m_pos= (char *) YYLIP->get_tok_start(); + } + ; + +ident_directly_assignable: + IDENT_sys { $$=$1; } + | keyword_directly_assignable + { + $$.str= thd->strmake($1.str, $1.length); + if ($$.str == NULL) + MYSQL_YYABORT; + $$.length= $1.length; + } + | keyword_sp + { + $$.str= thd->strmake($1.str, $1.length); + if ($$.str == NULL) + MYSQL_YYABORT; + $$.length= $1.length; + } + ; + + +label_ident: + IDENT_sys { $$=$1; } + | keyword_sp + { + $$.str= thd->strmake($1.str, $1.length); + if ($$.str == NULL) + MYSQL_YYABORT; + $$.length= $1.length; + } + ; + +labels_declaration_oracle: + label_declaration_oracle { $$= $1; } + | labels_declaration_oracle label_declaration_oracle { $$= $2; } + ; + +label_declaration_oracle: + SHIFT_LEFT label_ident SHIFT_RIGHT + { + if (Lex->sp_push_goto_label(thd, &$2)) + MYSQL_YYABORT; + $$= $2; + } + ; + +ident_or_text: + ident { $$=$1;} + | TEXT_STRING_sys { $$=$1;} + | LEX_HOSTNAME { $$=$1;} + ; + +user_maybe_role: + ident_or_text + { + if (!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + $$->user = $1; + $$->host= null_clex_str; // User or Role, see get_current_user() + $$->reset_auth(); + + if (check_string_char_length(&$$->user, ER_USERNAME, + username_char_length, + system_charset_info, 0)) + MYSQL_YYABORT; + } + | ident_or_text '@' ident_or_text + { + if (!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + $$->user = $1; $$->host=$3; + $$->reset_auth(); + + if (check_string_char_length(&$$->user, ER_USERNAME, + username_char_length, + system_charset_info, 0) || + check_host_name(&$$->host)) + MYSQL_YYABORT; + if ($$->host.str[0]) + { + /* + Convert hostname part of username to lowercase. + It's OK to use in-place lowercase as long as + the character set is utf8. + */ + my_casedn_str(system_charset_info, (char*) $$->host.str); + } + else + { + /* + fix historical undocumented convention that empty host is the + same as '%' + */ + $$->host= host_not_specified; + } + } + | CURRENT_USER optional_braces + { + if (!($$=(LEX_USER*)thd->calloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + $$->user= current_user; + $$->plugin= empty_clex_str; + $$->auth= empty_clex_str; + } + ; + +user_or_role: user_maybe_role | current_role; + +user: user_maybe_role + { + if ($1->user.str != current_user.str && $1->host.str == 0) + $1->host= host_not_specified; + $$= $1; + } + ; + +/* Keyword that we allow for identifiers (except SP labels) */ +keyword: + keyword_sp {} + | keyword_directly_assignable {} + | keyword_directly_not_assignable {} + ; + + +/* + Keywords that we allow in Oracle-style direct assignments: + xxx := 10; +*/ +keyword_directly_assignable: + ASCII_SYM {} + | BACKUP_SYM {} + | BINLOG_SYM {} + | BYTE_SYM {} + | CACHE_SYM {} + | CHECKSUM_SYM {} + | CHECKPOINT_SYM {} + | COLUMN_ADD_SYM {} + | COLUMN_CHECK_SYM {} + | COLUMN_CREATE_SYM {} + | COLUMN_DELETE_SYM {} + | COLUMN_GET_SYM {} + | DEALLOCATE_SYM {} + | EXAMINED_SYM {} + | EXCLUDE_SYM {} + | EXECUTE_SYM {} + | FLUSH_SYM {} + | FOLLOWING_SYM {} + | FORMAT_SYM {} + | GET_SYM {} + | HELP_SYM {} + | HOST_SYM {} + | INSTALL_SYM {} + | OPTION {} + | OPTIONS_SYM {} + | OWNER_SYM {} + | PARSER_SYM {} + | PORT_SYM {} + | PRECEDING_SYM {} + | PREPARE_SYM {} + | REMOVE_SYM {} + | RESET_SYM {} + | RESTORE_SYM {} + | SECURITY_SYM {} + | SERVER_SYM {} + | SIGNED_SYM {} + | SOCKET_SYM {} + | SLAVE {} + | SLAVES {} + | SONAME_SYM {} + | START_SYM {} + | STOP_SYM {} + | STORED_SYM {} + | TIES_SYM {} + | UNICODE_SYM {} + | UNINSTALL_SYM {} + | UNBOUNDED_SYM {} + | WRAPPER_SYM {} + | XA_SYM {} + | UPGRADE_SYM {} + ; + +/* + Keywords that are allowed as identifiers (e.g. table, column names), + but: + - not allowed as SP label names + - not allowed as variable names in Oracle-style assignments: + xxx := 10; + + If we allowed these variables in assignments, there would be conflicts + with SP characteristics, or verb clauses, or compound statements, e.g.: + CREATE PROCEDURE p1 LANGUAGE ... + would be either: + CREATE PROCEDURE p1 LANGUAGE SQL BEGIN END; + or + CREATE PROCEDURE p1 LANGUAGE:=10; + + Note, these variables can still be assigned using quoted identifiers: + `do`:= 10; + "do":= 10; (when ANSI_QUOTES) + or using a SET statement: + SET do= 10; + + Note, some of these keywords are reserved keywords in Oracle. + In case if heavy grammar conflicts are found in the future, + we'll possibly need to make them reserved for sql_mode=ORACLE. + + TODO: Allow these variables as SP lables when sql_mode=ORACLE. + TODO: Allow assigning of "SP characteristics" marked variables + inside compound blocks. + TODO: Allow "follows" and "precedes" as variables in compound blocks: + BEGIN + follows := 10; + END; + as they conflict only with non-block FOR EACH ROW statement: + CREATE TRIGGER .. FOR EACH ROW follows:= 10; + CREATE TRIGGER .. FOR EACH ROW FOLLOWS tr1 a:= 10; +*/ +keyword_directly_not_assignable: + CONTAINS_SYM { /* SP characteristic */ } + | LANGUAGE_SYM { /* SP characteristic */ } + | NO_SYM { /* SP characteristic */ } + | CHARSET { /* SET CHARSET utf8; */ } + | FOLLOWS_SYM { /* Conflicts with assignment in FOR EACH */} + | PRECEDES_SYM { /* Conflicts with assignment in FOR EACH */} + | keyword_sp_verb_clause { } + ; + +/* + * Keywords that we allow for labels in SPs. + * Anything that's the beginning of a statement or characteristics + * must be in keyword above, otherwise we get (harmful) shift/reduce + * conflicts. + */ +keyword_sp: + keyword_sp_data_type + | keyword_sp_not_data_type + ; + + +/* + Keywords that start a statement or an SP block section. + Generally allowed as identifiers (e.g. table, column names) + - not allowed as SP label names + - not allowed as variable names in Oracle-style assignments: + xxx:=10 +*/ +keyword_sp_verb_clause: + BEGIN_SYM { /* Compound. Reserved in Oracle */ } + | CLOSE_SYM { /* Verb clause. Reserved in Oracle */ } + | COMMIT_SYM { /* Verb clause. Reserved in Oracle */ } + | EXCEPTION_SYM { /* EXCEPTION section in SP blocks */ } + | DO_SYM { /* Verb clause */ } + | END { /* Compound. Reserved in Oracle */ } + | HANDLER_SYM { /* Verb clause */ } + | OPEN_SYM { /* Verb clause. Reserved in Oracle */ } + | REPAIR { /* Verb clause */ } + | ROLLBACK_SYM { /* Verb clause. Reserved in Oracle */ } + | SAVEPOINT_SYM { /* Verb clause. Reserved in Oracle */ } + | SHUTDOWN { /* Verb clause */ } + | TRUNCATE_SYM { /* Verb clause. Reserved in Oracle */ } + ; + + +/* + These keywords are generally allowed as identifiers, + but not allowed as non-delimited SP variable names in sql_mode=ORACLE. +*/ +keyword_sp_data_type: + BIT_SYM {} + | BOOLEAN_SYM {} /* PLSQL-R */ + | BOOL_SYM {} + | CLOB {} + | DATE_SYM {} /* Oracle-R, PLSQL-R */ + | DATETIME {} + | ENUM {} + | FIXED_SYM {} + | GEOMETRYCOLLECTION {} + | GEOMETRY_SYM {} + | JSON_SYM {} + | LINESTRING {} + | MEDIUM_SYM {} + | MULTILINESTRING {} + | MULTIPOINT {} + | MULTIPOLYGON {} + | NATIONAL_SYM {} + | NCHAR_SYM {} + | NUMBER_SYM {} /* Oracle-R, PLSQL-R */ + | NVARCHAR_SYM {} + | POINT_SYM {} + | POLYGON {} + | RAW {} /* Oracle-R */ + | ROW_SYM {} + | SERIAL_SYM {} + | TEXT_SYM {} + | TIMESTAMP {} + | TIME_SYM {} /* Oracle-R */ + | VARCHAR2 {} /* Oracle-R, PLSQL-R */ + | YEAR_SYM {} + ; + + +keyword_sp_not_data_type: + ACTION {} + | ADDDATE_SYM {} + | ADMIN_SYM {} + | AFTER_SYM {} + | AGAINST {} + | AGGREGATE_SYM {} + | ALGORITHM_SYM {} + | ALWAYS_SYM {} + | ANY_SYM {} + | AT_SYM {} + | ATOMIC_SYM {} + | AUTHORS_SYM {} + | AUTO_INC {} + | AUTOEXTEND_SIZE_SYM {} + | AUTO_SYM {} + | AVG_ROW_LENGTH {} + | AVG_SYM {} + | BLOCK_SYM {} + | BTREE_SYM {} + | CASCADED {} + | CATALOG_NAME_SYM {} + | CHAIN_SYM {} + | CHANGED {} + | CIPHER_SYM {} + | CLIENT_SYM {} + | CLASS_ORIGIN_SYM {} + | COALESCE {} + | CODE_SYM {} + | COLLATION_SYM {} + | COLUMN_NAME_SYM {} + | COLUMNS {} + | COMMITTED_SYM {} + | COMPACT_SYM {} + | COMPLETION_SYM {} + | COMPRESSED_SYM {} + | CONCURRENT {} + | CONNECTION_SYM {} + | CONSISTENT_SYM {} + | CONSTRAINT_CATALOG_SYM {} + | CONSTRAINT_SCHEMA_SYM {} + | CONSTRAINT_NAME_SYM {} + | CONTEXT_SYM {} + | CONTRIBUTORS_SYM {} + | CURRENT_POS_SYM {} + | CPU_SYM {} + | CUBE_SYM {} + /* + Although a reserved keyword in SQL:2003 (and :2008), + not reserved in MySQL per WL#2111 specification. + */ + | CURRENT_SYM {} + | CURSOR_NAME_SYM {} + | CYCLE_SYM {} + | DATA_SYM {} + | DATAFILE_SYM {} + | DATE_FORMAT_SYM {} + | DAY_SYM {} + | DECODE_SYM {} + | DEFINER_SYM {} + | DELAY_KEY_WRITE_SYM {} + | DES_KEY_FILE {} + | DIAGNOSTICS_SYM {} + | DIRECTORY_SYM {} + | DISABLE_SYM {} + | DISCARD {} + | DISK_SYM {} + | DUMPFILE {} + | DUPLICATE_SYM {} + | DYNAMIC_SYM {} + | ENDS_SYM {} + | ENGINE_SYM {} + | ENGINES_SYM {} + | ERROR_SYM {} + | ERRORS {} + | ESCAPE_SYM {} + | EVENT_SYM {} + | EVENTS_SYM {} + | EVERY_SYM {} + | EXCHANGE_SYM {} + | EXPANSION_SYM {} + | EXPORT_SYM {} + | EXTENDED_SYM {} + | EXTENT_SIZE_SYM {} + | FAULTS_SYM {} + | FAST_SYM {} + | FOUND_SYM {} + | ENABLE_SYM {} + | FULL {} + | FILE_SYM {} + | FIRST_SYM {} + | GENERAL {} + | GENERATED_SYM {} + | GET_FORMAT {} + | GRANTS {} + | GLOBAL_SYM {} + | HASH_SYM {} + | HARD_SYM {} + | HOSTS_SYM {} + | HOUR_SYM {} + | ID_SYM {} + | IDENTIFIED_SYM {} + | IGNORE_SERVER_IDS_SYM {} + | INCREMENT_SYM {} + | IMMEDIATE_SYM {} /* SQL-2003-R */ + | INVOKER_SYM {} + | IMPORT {} + | INDEXES {} + | INITIAL_SIZE_SYM {} + | IO_SYM {} + | IPC_SYM {} + | ISOLATION {} + | ISOPEN_SYM {} + | ISSUER_SYM {} + | INSERT_METHOD {} + | KEY_BLOCK_SIZE {} + | LAST_VALUE {} + | LAST_SYM {} + | LASTVAL_SYM {} + | LEAVES {} + | LESS_SYM {} + | LEVEL_SYM {} + | LIST_SYM {} + | LOCAL_SYM {} + | LOCKS_SYM {} + | LOGFILE_SYM {} + | LOGS_SYM {} + | MAX_ROWS {} + | MASTER_SYM {} + | MASTER_HEARTBEAT_PERIOD_SYM {} + | MASTER_GTID_POS_SYM {} + | MASTER_HOST_SYM {} + | MASTER_PORT_SYM {} + | MASTER_LOG_FILE_SYM {} + | MASTER_LOG_POS_SYM {} + | MASTER_USER_SYM {} + | MASTER_USE_GTID_SYM {} + | MASTER_PASSWORD_SYM {} + | MASTER_SERVER_ID_SYM {} + | MASTER_CONNECT_RETRY_SYM {} + | MASTER_DELAY_SYM {} + | MASTER_SSL_SYM {} + | MASTER_SSL_CA_SYM {} + | MASTER_SSL_CAPATH_SYM {} + | MASTER_SSL_CERT_SYM {} + | MASTER_SSL_CIPHER_SYM {} + | MASTER_SSL_CRL_SYM {} + | MASTER_SSL_CRLPATH_SYM {} + | MASTER_SSL_KEY_SYM {} + | MAX_CONNECTIONS_PER_HOUR {} + | MAX_QUERIES_PER_HOUR {} + | MAX_SIZE_SYM {} + | MAX_STATEMENT_TIME_SYM {} + | MAX_UPDATES_PER_HOUR {} + | MAX_USER_CONNECTIONS_SYM {} + | MEMORY_SYM {} + | MERGE_SYM {} + | MESSAGE_TEXT_SYM {} + | MICROSECOND_SYM {} + | MIGRATE_SYM {} + | MINUTE_SYM {} + | MINVALUE_SYM {} + | MIN_ROWS {} + | MODIFY_SYM {} + | MODE_SYM {} + | MONTH_SYM {} + | MUTEX_SYM {} + | MYSQL_SYM {} + | MYSQL_ERRNO_SYM {} + | NAME_SYM {} + | NAMES_SYM {} + | NEXT_SYM {} + | NEXTVAL_SYM {} + | NEW_SYM {} + | NOCACHE_SYM {} + | NOCYCLE_SYM {} + | NOMINVALUE_SYM {} + | NOMAXVALUE_SYM {} + | NO_WAIT_SYM {} + | NODEGROUP_SYM {} + | NONE_SYM {} + | NOTFOUND_SYM {} + | OF_SYM {} /* SQL-1999-R, Oracle-R */ + | OFFSET_SYM {} + | OLD_PASSWORD_SYM {} + | ONE_SYM {} + | ONLINE_SYM {} + | ONLY_SYM {} + | PACK_KEYS_SYM {} + | PAGE_SYM {} + | PARTIAL {} + | PARTITIONING_SYM {} + | PARTITIONS_SYM {} + | PASSWORD_SYM {} + | PERSISTENT_SYM {} + | PHASE_SYM {} + | PLUGIN_SYM {} + | PLUGINS_SYM {} + | PRESERVE_SYM {} + | PREV_SYM {} + | PREVIOUS_SYM {} + | PRIVILEGES {} + | PROCESS {} + | PROCESSLIST_SYM {} + | PROFILE_SYM {} + | PROFILES_SYM {} + | PROXY_SYM {} + | QUARTER_SYM {} + | QUERY_SYM {} + | QUICK {} + | READ_ONLY_SYM {} + | REBUILD_SYM {} + | RECOVER_SYM {} + | REDO_BUFFER_SIZE_SYM {} + | REDOFILE_SYM {} + | REDUNDANT_SYM {} + | RELAY {} + | RELAYLOG_SYM {} + | RELAY_LOG_FILE_SYM {} + | RELAY_LOG_POS_SYM {} + | RELAY_THREAD {} + | RELOAD {} + | REORGANIZE_SYM {} + | REPEATABLE_SYM {} + | REPLICATION {} + | RESOURCES {} + | RESTART_SYM {} + | RESUME_SYM {} + | RETURNED_SQLSTATE_SYM {} + | RETURNS_SYM {} + | REUSE_SYM {} /* Oracle-R */ + | REVERSE_SYM {} + | ROLE_SYM {} + | ROLLUP_SYM {} + | ROUTINE_SYM {} + | ROWCOUNT_SYM {} + | ROW_COUNT_SYM {} + | ROW_FORMAT_SYM {} + | RTREE_SYM {} + | SCHEDULE_SYM {} + | SCHEMA_NAME_SYM {} + | SECOND_SYM {} + | SEQUENCE_SYM {} + | SERIALIZABLE_SYM {} + | SESSION_SYM {} + | SETVAL_SYM {} + | SIMPLE_SYM {} + | SHARE_SYM {} + | SLAVE_POS_SYM {} + | SLOW {} + | SNAPSHOT_SYM {} + | SOFT_SYM {} + | SOUNDS_SYM {} + | SOURCE_SYM {} + | SQL_CACHE_SYM {} + | SQL_BUFFER_RESULT {} + | SQL_NO_CACHE_SYM {} + | SQL_THREAD {} + | STARTS_SYM {} + | STATEMENT_SYM {} + | STATUS_SYM {} + | STORAGE_SYM {} + | STRING_SYM {} + | SUBCLASS_ORIGIN_SYM {} + | SUBDATE_SYM {} + | SUBJECT_SYM {} + | SUBPARTITION_SYM {} + | SUBPARTITIONS_SYM {} + | SUPER_SYM {} + | SUSPEND_SYM {} + | SWAPS_SYM {} + | SWITCHES_SYM {} + | TABLE_NAME_SYM {} + | TABLES {} + | TABLE_CHECKSUM_SYM {} + | TABLESPACE {} + | TEMPORARY {} + | TEMPTABLE_SYM {} + | THAN_SYM {} + | TRANSACTION_SYM {} + | TRANSACTIONAL_SYM {} + | TRIGGERS_SYM {} + | TIMESTAMP_ADD {} + | TIMESTAMP_DIFF {} + | TYPES_SYM {} + | TYPE_SYM {} + | UDF_RETURNS_SYM {} + | FUNCTION_SYM {} + | UNCOMMITTED_SYM {} + | UNDEFINED_SYM {} + | UNDO_BUFFER_SIZE_SYM {} + | UNDOFILE_SYM {} + | UNKNOWN_SYM {} + | UNTIL_SYM {} + | USER_SYM {} + | USE_FRM {} + | VARIABLES {} + | VIEW_SYM {} + | VIRTUAL_SYM {} + | VALUE_SYM {} + | WARNINGS {} + | WAIT_SYM {} + | WEEK_SYM {} + | WEIGHT_STRING_SYM {} + | WORK_SYM {} + | X509_SYM {} + | XML_SYM {} + | VIA_SYM {} + ; + +/* + SQLCOM_SET_OPTION statement. + + Note that to avoid shift/reduce conflicts, we have separate rules for the + first option listed in the statement. +*/ + +set: + SET + { + LEX *lex=Lex; + lex->set_stmt_init(); + lex->var_list.empty(); + sp_create_assignment_lex(thd, yychar == YYEMPTY); + } + start_option_value_list + {} + | SET STATEMENT_SYM + { + Lex->set_stmt_init(); + } + set_stmt_option_value_following_option_type_list + { + LEX *lex= Lex; + if (lex->table_or_sp_used()) + my_yyabort_error((ER_SUBQUERIES_NOT_SUPPORTED, MYF(0), "SET STATEMENT")); + lex->stmt_var_list= lex->var_list; + lex->var_list.empty(); + } + FOR_SYM verb_clause + {} + ; + +set_assign: + internal_variable_name_directly_assignable SET_VAR + { + LEX *lex=Lex; + lex->set_stmt_init(); + lex->var_list.empty(); + sp_create_assignment_lex(thd, yychar == YYEMPTY); + } + set_expr_or_default + { + if (Lex->set_variable(&$1, $4) || + sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + | ident_directly_assignable '.' ident SET_VAR + { + LEX *lex=Lex; + lex->set_stmt_init(); + lex->var_list.empty(); + sp_create_assignment_lex(thd, yychar == YYEMPTY); + } + set_expr_or_default + { + LEX *lex= Lex; + DBUG_ASSERT(lex->var_list.is_empty()); + if (lex->set_variable(&$1, &$3, $6) || + lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + ; + +set_stmt_option_value_following_option_type_list: + /* + Only system variables can be used here. If this condition is changed + please check careful code under lex->option_type == OPT_STATEMENT + condition on wrong type casts. + */ + option_value_following_option_type + | set_stmt_option_value_following_option_type_list ',' option_value_following_option_type + ; + +/* Start of option value list */ +start_option_value_list: + option_value_no_option_type + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + option_value_list_continued + | TRANSACTION_SYM + { + Lex->option_type= OPT_DEFAULT; + } + transaction_characteristics + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + | option_type + { + Lex->option_type= $1; + } + start_option_value_list_following_option_type + ; + + +/* Start of option value list, option_type was given */ +start_option_value_list_following_option_type: + option_value_following_option_type + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + option_value_list_continued + | TRANSACTION_SYM transaction_characteristics + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + ; + +/* Remainder of the option value list after first option value. */ +option_value_list_continued: + /* empty */ + | ',' option_value_list + ; + +/* Repeating list of option values after first option value. */ +option_value_list: + { + sp_create_assignment_lex(thd, yychar == YYEMPTY); + } + option_value + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + | option_value_list ',' + { + sp_create_assignment_lex(thd, yychar == YYEMPTY); + } + option_value + { + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } + ; + +/* Wrapper around option values following the first option value in the stmt. */ +option_value: + option_type + { + Lex->option_type= $1; + } + option_value_following_option_type + | option_value_no_option_type + ; + +option_type: + GLOBAL_SYM { $$=OPT_GLOBAL; } + | LOCAL_SYM { $$=OPT_SESSION; } + | SESSION_SYM { $$=OPT_SESSION; } + ; + +opt_var_type: + /* empty */ { $$=OPT_SESSION; } + | GLOBAL_SYM { $$=OPT_GLOBAL; } + | LOCAL_SYM { $$=OPT_SESSION; } + | SESSION_SYM { $$=OPT_SESSION; } + ; + +opt_var_ident_type: + /* empty */ { $$=OPT_DEFAULT; } + | GLOBAL_SYM '.' { $$=OPT_GLOBAL; } + | LOCAL_SYM '.' { $$=OPT_SESSION; } + | SESSION_SYM '.' { $$=OPT_SESSION; } + ; + +/* Option values with preceding option_type. */ +option_value_following_option_type: + internal_variable_name equal set_expr_or_default + { + LEX *lex= Lex; + + if ($1.var && $1.var != trg_new_row_fake_var) + { + /* It is a system variable. */ + if (lex->set_system_variable(&$1, lex->option_type, $3)) + MYSQL_YYABORT; + } + else + { + /* + Not in trigger assigning value to new row, + and option_type preceding local variable is illegal. + */ + thd->parse_error(); + MYSQL_YYABORT; + } + } + ; + +/* Option values without preceding option_type. */ +option_value_no_option_type: + ident equal set_expr_or_default + { + struct sys_var_with_base var; + if (Lex->init_internal_variable(&var, &$1) || + Lex->set_variable(&var, $3)) + MYSQL_YYABORT; + } + | ident '.' ident equal set_expr_or_default + { + DBUG_ASSERT(Lex->var_list.is_empty()); + if (Lex->set_variable(&$1, &$3, $5)) + MYSQL_YYABORT; + } + | DEFAULT '.' ident equal set_expr_or_default + { + struct sys_var_with_base var; + if (Lex->init_default_internal_variable(&var, $3) || + Lex->set_variable(&var, $5)) + MYSQL_YYABORT; + } + | '@' ident_or_text equal expr + { + if (Lex->set_user_variable(thd, &$2, $4)) + MYSQL_YYABORT; + } + | '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default + { + struct sys_var_with_base tmp= $4; + /* Lookup if necessary: must be a system variable. */ + if (tmp.var == NULL) + { + if (find_sys_var_null_base(thd, &tmp)) + MYSQL_YYABORT; + } + if (Lex->set_system_variable(&tmp, $3, $6)) + MYSQL_YYABORT; + } + | charset old_or_new_charset_name_or_default + { + LEX *lex= thd->lex; + CHARSET_INFO *cs2; + cs2= $2 ? $2: global_system_variables.character_set_client; + set_var_collation_client *var; + var= (new (thd->mem_root) + set_var_collation_client(cs2, + thd->variables.collation_database, + cs2)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + } + | NAMES_SYM equal expr + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + LEX_CSTRING names= { STRING_WITH_LEN("names") }; + if (spc && spc->find_variable(&names, false)) + my_error(ER_SP_BAD_VAR_SHADOW, MYF(0), names.str); + else + thd->parse_error(); + MYSQL_YYABORT; + } + | NAMES_SYM charset_name_or_default opt_collate + { + LEX *lex= Lex; + CHARSET_INFO *cs2; + CHARSET_INFO *cs3; + cs2= $2 ? $2 : global_system_variables.character_set_client; + cs3= $3 ? $3 : cs2; + if (!my_charset_same(cs2, cs3)) + { + my_error(ER_COLLATION_CHARSET_MISMATCH, MYF(0), + cs3->name, cs2->csname); + MYSQL_YYABORT; + } + set_var_collation_client *var; + var= new (thd->mem_root) set_var_collation_client(cs3, cs3, cs3); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + } + | DEFAULT ROLE_SYM grant_role + { + LEX *lex = Lex; + LEX_USER *user; + if (!(user=(LEX_USER *) thd->calloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + user->user= current_user; + set_var_default_role *var= (new (thd->mem_root) + set_var_default_role(user, + $3->user)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + thd->lex->autocommit= TRUE; + if (lex->sphead) + lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + } + | DEFAULT ROLE_SYM grant_role FOR_SYM user + { + LEX *lex = Lex; + set_var_default_role *var= (new (thd->mem_root) + set_var_default_role($5, $3->user)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + thd->lex->autocommit= TRUE; + if (lex->sphead) + lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + } + | ROLE_SYM ident_or_text + { + LEX *lex = Lex; + set_var_role *var= new (thd->mem_root) set_var_role($2); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + } + | PASSWORD_SYM opt_for_user text_or_password + { + LEX *lex = Lex; + set_var_password *var= (new (thd->mem_root) + set_var_password(lex->definer)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + lex->autocommit= TRUE; + if (lex->sphead) + lex->sphead->m_flags|= sp_head::HAS_SET_AUTOCOMMIT_STMT; + } + ; + + +internal_variable_name: + ident + { + if (Lex->init_internal_variable(&$$, &$1)) + MYSQL_YYABORT; + } + | ident '.' ident + { + if (Lex->init_internal_variable(&$$, &$1, &$3)) + MYSQL_YYABORT; + } + | DEFAULT '.' ident + { + if (Lex->init_default_internal_variable(&$$, $3)) + MYSQL_YYABORT; + } + ; + + +internal_variable_name_directly_assignable: + ident_directly_assignable + { + if (Lex->init_internal_variable(&$$, &$1)) + MYSQL_YYABORT; + } + | DEFAULT '.' ident + { + if (Lex->init_default_internal_variable(&$$, $3)) + MYSQL_YYABORT; + } + | colon_with_pos ident_directly_assignable '.' ident + { + if (!Lex->is_trigger_new_or_old_reference(&$2)) + { + thd->parse_error(); + MYSQL_YYABORT; + } + if (Lex->init_internal_variable(&$$, &$2, &$4)) + MYSQL_YYABORT; + } + ; + +transaction_characteristics: + transaction_access_mode + | isolation_level + | transaction_access_mode ',' isolation_level + | isolation_level ',' transaction_access_mode + ; + +transaction_access_mode: + transaction_access_mode_types + { + LEX *lex=Lex; + Item *item= new (thd->mem_root) Item_int(thd, (int32) $1); + if (item == NULL) + MYSQL_YYABORT; + set_var *var= (new (thd->mem_root) + set_var(thd, lex->option_type, + find_sys_var(thd, "tx_read_only"), + &null_clex_str, + item)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + } + ; + +isolation_level: + ISOLATION LEVEL_SYM isolation_types + { + LEX *lex=Lex; + Item *item= new (thd->mem_root) Item_int(thd, (int32) $3); + if (item == NULL) + MYSQL_YYABORT; + set_var *var= (new (thd->mem_root) + set_var(thd, lex->option_type, + find_sys_var(thd, "tx_isolation"), + &null_clex_str, + item)); + if (var == NULL) + MYSQL_YYABORT; + lex->var_list.push_back(var, thd->mem_root); + } + ; + +transaction_access_mode_types: + READ_SYM ONLY_SYM { $$= true; } + | READ_SYM WRITE_SYM { $$= false; } + ; + +isolation_types: + READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; } + | READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; } + | REPEATABLE_SYM READ_SYM { $$= ISO_REPEATABLE_READ; } + | SERIALIZABLE_SYM { $$= ISO_SERIALIZABLE; } + ; + +opt_for_user: + equal + { + LEX *lex= thd->lex; + sp_pcontext *spc= lex->spcont; + LEX_CSTRING pw= { STRING_WITH_LEN("password") }; + + if (spc && spc->find_variable(&pw, false)) + my_yyabort_error((ER_SP_BAD_VAR_SHADOW, MYF(0), pw.str)); + if (!(lex->definer= (LEX_USER*) thd->calloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + lex->definer->user= current_user; + lex->definer->plugin= empty_clex_str; + lex->definer->auth= empty_clex_str; + } + | FOR_SYM user equal { Lex->definer= $2; } + ; + +text_or_password: + TEXT_STRING { Lex->definer->pwhash= $1;} + | PASSWORD_SYM '(' TEXT_STRING ')' { Lex->definer->pwtext= $3; } + | OLD_PASSWORD_SYM '(' TEXT_STRING ')' + { + Lex->definer->pwtext= $3; + Lex->definer->pwhash.str= Item_func_password::alloc(thd, + $3.str, $3.length, Item_func_password::OLD); + Lex->definer->pwhash.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + } + ; + +set_expr_or_default: + expr { $$=$1; } + | DEFAULT { $$=0; } + | ON + { + $$=new (thd->mem_root) Item_string_sys(thd, "ON", 2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | ALL + { + $$=new (thd->mem_root) Item_string_sys(thd, "ALL", 3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BINARY + { + $$=new (thd->mem_root) Item_string_sys(thd, "binary", 6); + if ($$ == NULL) + MYSQL_YYABORT; + } + ; + +/* Lock function */ + +lock: + LOCK_SYM table_or_tables + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "LOCK")); + lex->sql_command= SQLCOM_LOCK_TABLES; + } + table_lock_list + {} + ; + +table_or_tables: + TABLE_SYM { } + | TABLES { } + ; + +table_lock_list: + table_lock + | table_lock_list ',' table_lock + ; + +table_lock: + table_ident opt_table_alias lock_option + { + thr_lock_type lock_type= (thr_lock_type) $3; + bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE); + if (!Select->add_table_to_list(thd, $1, $2, 0, lock_type, + (lock_for_write ? + lock_type == TL_WRITE_CONCURRENT_INSERT ? + MDL_SHARED_WRITE : + MDL_SHARED_NO_READ_WRITE : + MDL_SHARED_READ))) + MYSQL_YYABORT; + } + ; + +lock_option: + READ_SYM { $$= TL_READ_NO_INSERT; } + | WRITE_SYM { $$= TL_WRITE_DEFAULT; } + | WRITE_SYM CONCURRENT + { + $$= (Lex->sphead ? TL_WRITE_DEFAULT : TL_WRITE_CONCURRENT_INSERT); + } + + | LOW_PRIORITY WRITE_SYM { $$= TL_WRITE_LOW_PRIORITY; } + | READ_SYM LOCAL_SYM { $$= TL_READ; } + ; + +unlock: + UNLOCK_SYM + { + LEX *lex= Lex; + + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "UNLOCK")); + lex->sql_command= SQLCOM_UNLOCK_TABLES; + } + table_or_tables + {} + ; + +/* +** Handler: direct access to ISAM functions +*/ + +handler: + HANDLER_SYM table_ident OPEN_SYM opt_table_alias + { + LEX *lex= Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); + lex->sql_command = SQLCOM_HA_OPEN; + if (!lex->current_select->add_table_to_list(thd, $2, $4, 0)) + MYSQL_YYABORT; + } + | HANDLER_SYM table_ident_nodb CLOSE_SYM + { + LEX *lex= Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); + lex->sql_command = SQLCOM_HA_CLOSE; + if (!lex->current_select->add_table_to_list(thd, $2, 0, 0)) + MYSQL_YYABORT; + } + | HANDLER_SYM table_ident_nodb READ_SYM + { + LEX *lex=Lex; + if (lex->sphead) + my_yyabort_error((ER_SP_BADSTATEMENT, MYF(0), "HANDLER")); + lex->expr_allows_subselect= FALSE; + lex->sql_command = SQLCOM_HA_READ; + lex->ha_rkey_mode= HA_READ_KEY_EXACT; /* Avoid purify warnings */ + Item *one= new (thd->mem_root) Item_int(thd, (int32) 1); + if (one == NULL) + MYSQL_YYABORT; + lex->current_select->select_limit= one; + lex->current_select->offset_limit= 0; + lex->limit_rows_examined= 0; + if (!lex->current_select->add_table_to_list(thd, $2, 0, 0)) + MYSQL_YYABORT; + } + handler_read_or_scan opt_where_clause opt_limit_clause + { + Lex->expr_allows_subselect= TRUE; + /* Stored functions are not supported for HANDLER READ. */ + if (Lex->uses_stored_routines()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "stored functions in HANDLER ... READ"); + MYSQL_YYABORT; + } + } + ; + +handler_read_or_scan: + handler_scan_function { Lex->ident= null_clex_str; } + | ident handler_rkey_function { Lex->ident= $1; } + ; + +handler_scan_function: + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; } + ; + +handler_rkey_function: + FIRST_SYM { Lex->ha_read_mode = RFIRST; } + | NEXT_SYM { Lex->ha_read_mode = RNEXT; } + | PREV_SYM { Lex->ha_read_mode = RPREV; } + | LAST_SYM { Lex->ha_read_mode = RLAST; } + | handler_rkey_mode + { + LEX *lex=Lex; + lex->ha_read_mode = RKEY; + lex->ha_rkey_mode=$1; + if (!(lex->insert_list= new (thd->mem_root) List_item)) + MYSQL_YYABORT; + } + '(' values ')' + {} + ; + +handler_rkey_mode: + '=' { $$=HA_READ_KEY_EXACT; } + | GE { $$=HA_READ_KEY_OR_NEXT; } + | LE { $$=HA_READ_KEY_OR_PREV; } + | '>' { $$=HA_READ_AFTER_KEY; } + | '<' { $$=HA_READ_BEFORE_KEY; } + ; + +/* GRANT / REVOKE */ + +revoke: + REVOKE clear_privileges revoke_command + {} + ; + +revoke_command: + grant_privileges ON opt_table grant_ident FROM user_and_role_list + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_REVOKE; + lex->type= 0; + } + | grant_privileges ON FUNCTION_SYM grant_ident FROM user_and_role_list + { + if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_FUNCTION)) + MYSQL_YYABORT; + } + | grant_privileges ON PROCEDURE_SYM grant_ident FROM user_and_role_list + { + if (Lex->add_grant_command(thd, SQLCOM_REVOKE, TYPE_ENUM_PROCEDURE)) + MYSQL_YYABORT; + } + | ALL opt_privileges ',' GRANT OPTION FROM user_and_role_list + { + Lex->sql_command = SQLCOM_REVOKE_ALL; + } + | PROXY_SYM ON user FROM user_list + { + LEX *lex= Lex; + lex->users_list.push_front ($3); + lex->sql_command= SQLCOM_REVOKE; + lex->type= TYPE_ENUM_PROXY; + } + | admin_option_for_role FROM user_and_role_list + { + Lex->sql_command= SQLCOM_REVOKE_ROLE; + if (Lex->users_list.push_front($1, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +admin_option_for_role: + ADMIN_SYM OPTION FOR_SYM grant_role + { Lex->with_admin_option= true; $$= $4; } + | grant_role + { Lex->with_admin_option= false; $$= $1; } + ; + +grant: + GRANT clear_privileges grant_command + {} + ; + +grant_command: + grant_privileges ON opt_table grant_ident TO_SYM grant_list + opt_require_clause opt_grant_options + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_GRANT; + lex->type= 0; + } + | grant_privileges ON FUNCTION_SYM grant_ident TO_SYM grant_list + opt_require_clause opt_grant_options + { + if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_FUNCTION)) + MYSQL_YYABORT; + } + | grant_privileges ON PROCEDURE_SYM grant_ident TO_SYM grant_list + opt_require_clause opt_grant_options + { + if (Lex->add_grant_command(thd, SQLCOM_GRANT, TYPE_ENUM_PROCEDURE)) + MYSQL_YYABORT; + } + | PROXY_SYM ON user TO_SYM grant_list opt_grant_option + { + LEX *lex= Lex; + lex->users_list.push_front ($3); + lex->sql_command= SQLCOM_GRANT; + lex->type= TYPE_ENUM_PROXY; + } + | grant_role TO_SYM grant_list opt_with_admin_option + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_GRANT_ROLE; + /* The first role is the one that is granted */ + if (Lex->users_list.push_front($1, thd->mem_root)) + MYSQL_YYABORT; + } + + ; + +opt_with_admin: + /* nothing */ { Lex->definer = 0; } + | WITH ADMIN_SYM user_or_role { Lex->definer = $3; } + +opt_with_admin_option: + /* nothing */ { Lex->with_admin_option= false; } + | WITH ADMIN_SYM OPTION { Lex->with_admin_option= true; } + +role_list: + grant_role + { + if (Lex->users_list.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | role_list ',' grant_role + { + if (Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +current_role: + CURRENT_ROLE optional_braces + { + if (!($$=(LEX_USER*) thd->calloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + $$->user= current_role; + $$->reset_auth(); + } + ; + +grant_role: + ident_or_text + { + CHARSET_INFO *cs= system_charset_info; + /* trim end spaces (as they'll be lost in mysql.user anyway) */ + $1.length= cs->cset->lengthsp(cs, $1.str, $1.length); + ((char*) $1.str)[$1.length] = '\0'; + if ($1.length == 0) + my_yyabort_error((ER_INVALID_ROLE, MYF(0), "")); + if (!($$=(LEX_USER*) thd->alloc(sizeof(LEX_USER)))) + MYSQL_YYABORT; + $$->user= $1; + $$->host= empty_clex_str; + $$->reset_auth(); + + if (check_string_char_length(&$$->user, ER_USERNAME, + username_char_length, + cs, 0)) + MYSQL_YYABORT; + } + | current_role + ; + +opt_table: + /* Empty */ + | TABLE_SYM + ; + +grant_privileges: + object_privilege_list {} + | ALL opt_privileges + { + Lex->all_privileges= 1; + Lex->grant= GLOBAL_ACLS; + } + ; + +opt_privileges: + /* empty */ + | PRIVILEGES + ; + +object_privilege_list: + object_privilege + | object_privilege_list ',' object_privilege + ; + +object_privilege: + SELECT_SYM + { Lex->which_columns = SELECT_ACL;} + opt_column_list {} + | INSERT + { Lex->which_columns = INSERT_ACL;} + opt_column_list {} + | UPDATE_SYM + { Lex->which_columns = UPDATE_ACL; } + opt_column_list {} + | REFERENCES + { Lex->which_columns = REFERENCES_ACL;} + opt_column_list {} + | DELETE_SYM { Lex->grant |= DELETE_ACL;} + | USAGE {} + | INDEX_SYM { Lex->grant |= INDEX_ACL;} + | ALTER { Lex->grant |= ALTER_ACL;} + | CREATE { Lex->grant |= CREATE_ACL;} + | DROP { Lex->grant |= DROP_ACL;} + | EXECUTE_SYM { Lex->grant |= EXECUTE_ACL;} + | RELOAD { Lex->grant |= RELOAD_ACL;} + | SHUTDOWN { Lex->grant |= SHUTDOWN_ACL;} + | PROCESS { Lex->grant |= PROCESS_ACL;} + | FILE_SYM { Lex->grant |= FILE_ACL;} + | GRANT OPTION { Lex->grant |= GRANT_ACL;} + | SHOW DATABASES { Lex->grant |= SHOW_DB_ACL;} + | SUPER_SYM { Lex->grant |= SUPER_ACL;} + | CREATE TEMPORARY TABLES { Lex->grant |= CREATE_TMP_ACL;} + | LOCK_SYM TABLES { Lex->grant |= LOCK_TABLES_ACL; } + | REPLICATION SLAVE { Lex->grant |= REPL_SLAVE_ACL; } + | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; } + | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; } + | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; } + | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; } + | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; } + | CREATE USER_SYM { Lex->grant |= CREATE_USER_ACL; } + | EVENT_SYM { Lex->grant |= EVENT_ACL;} + | TRIGGER_SYM { Lex->grant |= TRIGGER_ACL; } + | CREATE TABLESPACE { Lex->grant |= CREATE_TABLESPACE_ACL; } + ; + +opt_and: + /* empty */ {} + | AND_SYM {} + ; + +require_list: + require_list_element opt_and require_list + | require_list_element + ; + +require_list_element: + SUBJECT_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_subject) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "SUBJECT")); + lex->x509_subject=$2.str; + } + | ISSUER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->x509_issuer) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "ISSUER")); + lex->x509_issuer=$2.str; + } + | CIPHER_SYM TEXT_STRING + { + LEX *lex=Lex; + if (lex->ssl_cipher) + my_yyabort_error((ER_DUP_ARGUMENT, MYF(0), "CIPHER")); + lex->ssl_cipher=$2.str; + } + ; + +grant_ident: + '*' + { + LEX *lex= Lex; + size_t dummy; + if (lex->copy_db_to(&lex->current_select->db, &dummy)) + MYSQL_YYABORT; + if (lex->grant == GLOBAL_ACLS) + lex->grant = DB_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0))); + } + | ident '.' '*' + { + LEX *lex= Lex; + lex->current_select->db = $1.str; + if (lex->grant == GLOBAL_ACLS) + lex->grant = DB_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0))); + } + | '*' '.' '*' + { + LEX *lex= Lex; + lex->current_select->db = NULL; + if (lex->grant == GLOBAL_ACLS) + lex->grant= GLOBAL_ACLS & ~GRANT_ACL; + else if (lex->columns.elements) + my_yyabort_error((ER_ILLEGAL_GRANT_FOR_TABLE, MYF(0))); + } + | table_ident + { + LEX *lex=Lex; + if (!lex->current_select->add_table_to_list(thd, $1,NULL, + TL_OPTION_UPDATING)) + MYSQL_YYABORT; + if (lex->grant == GLOBAL_ACLS) + lex->grant = TABLE_ACLS & ~GRANT_ACL; + } + ; + +user_list: + user + { + if (Lex->users_list.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | user_list ',' user + { + if (Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +grant_list: + grant_user + { + if (Lex->users_list.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | grant_list ',' grant_user + { + if (Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +user_and_role_list: + user_or_role + { + if (Lex->users_list.push_back($1, thd->mem_root)) + MYSQL_YYABORT; + } + | user_and_role_list ',' user_or_role + { + if (Lex->users_list.push_back($3, thd->mem_root)) + MYSQL_YYABORT; + } + ; + +via_or_with: VIA_SYM | WITH ; +using_or_as: USING | AS ; + +grant_user: + user IDENTIFIED_SYM BY TEXT_STRING + { + $$= $1; + $1->pwtext= $4; + if (Lex->sql_command == SQLCOM_REVOKE) + MYSQL_YYABORT; + } + | user IDENTIFIED_SYM BY PASSWORD_SYM TEXT_STRING + { + $$= $1; + $1->pwhash= $5; + } + | user IDENTIFIED_SYM via_or_with ident_or_text + { + $$= $1; + $1->plugin= $4; + $1->auth= empty_clex_str; + } + | user IDENTIFIED_SYM via_or_with ident_or_text using_or_as TEXT_STRING_sys + { + $$= $1; + $1->plugin= $4; + $1->auth= $6; + } + | user_or_role + { $$= $1; } + ; + +opt_column_list: + /* empty */ + { + LEX *lex=Lex; + lex->grant |= lex->which_columns; + } + | '(' column_list ')' + ; + +column_list: + column_list ',' column_list_id + | column_list_id + ; + +column_list_id: + ident + { + String *new_str= new (thd->mem_root) String((const char*) $1.str,$1.length,system_charset_info); + if (new_str == NULL) + MYSQL_YYABORT; + List_iterator <LEX_COLUMN> iter(Lex->columns); + class LEX_COLUMN *point; + LEX *lex=Lex; + while ((point=iter++)) + { + if (!my_strcasecmp(system_charset_info, + point->column.c_ptr(), new_str->c_ptr())) + break; + } + lex->grant_tot_col|= lex->which_columns; + if (point) + point->rights |= lex->which_columns; + else + { + LEX_COLUMN *col= (new (thd->mem_root) + LEX_COLUMN(*new_str,lex->which_columns)); + if (col == NULL) + MYSQL_YYABORT; + lex->columns.push_back(col, thd->mem_root); + } + } + ; + +opt_require_clause: + /* empty */ + | REQUIRE_SYM require_list + { + Lex->ssl_type=SSL_TYPE_SPECIFIED; + } + | REQUIRE_SYM SSL_SYM + { + Lex->ssl_type=SSL_TYPE_ANY; + } + | REQUIRE_SYM X509_SYM + { + Lex->ssl_type=SSL_TYPE_X509; + } + | REQUIRE_SYM NONE_SYM + { + Lex->ssl_type=SSL_TYPE_NONE; + } + ; + +resource_option: + MAX_QUERIES_PER_HOUR ulong_num + { + LEX *lex=Lex; + lex->mqh.questions=$2; + lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR; + } + | MAX_UPDATES_PER_HOUR ulong_num + { + LEX *lex=Lex; + lex->mqh.updates=$2; + lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR; + } + | MAX_CONNECTIONS_PER_HOUR ulong_num + { + LEX *lex=Lex; + lex->mqh.conn_per_hour= $2; + lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR; + } + | MAX_USER_CONNECTIONS_SYM int_num + { + LEX *lex=Lex; + lex->mqh.user_conn= $2; + lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS; + } + | MAX_STATEMENT_TIME_SYM NUM_literal + { + LEX *lex=Lex; + lex->mqh.max_statement_time= $2->val_real(); + lex->mqh.specified_limits|= USER_RESOURCES::MAX_STATEMENT_TIME; + } + ; + +resource_option_list: + resource_option_list resource_option {} + | resource_option {} + ; + +opt_resource_options: + /* empty */ {} + | WITH resource_option_list + ; + + +opt_grant_options: + /* empty */ {} + | WITH grant_option_list {} + ; + +opt_grant_option: + /* empty */ {} + | WITH GRANT OPTION { Lex->grant |= GRANT_ACL;} + ; + +grant_option_list: + grant_option_list grant_option {} + | grant_option {} + ; + +grant_option: + GRANT OPTION { Lex->grant |= GRANT_ACL;} + | resource_option {} + ; + +compound_statement: + sp_proc_stmt_compound_ok + { + Lex->sql_command= SQLCOM_COMPOUND; + Lex->sphead->set_stmt_end(thd); + Lex->sphead->restore_thd_mem_root(thd); + } + ; + +opt_not: + /* nothing */ { $$= 0; } + | not { $$= 1; } + ; + +opt_work: + /* empty */ {} + | WORK_SYM {} + ; + +opt_chain: + /* empty */ + { $$= TVL_UNKNOWN; } + | AND_SYM NO_SYM CHAIN_SYM { $$= TVL_NO; } + | AND_SYM CHAIN_SYM { $$= TVL_YES; } + ; + +opt_release: + /* empty */ + { $$= TVL_UNKNOWN; } + | RELEASE_SYM { $$= TVL_YES; } + | NO_SYM RELEASE_SYM { $$= TVL_NO; } +; + +commit: + COMMIT_SYM opt_work opt_chain opt_release + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_COMMIT; + /* Don't allow AND CHAIN RELEASE. */ + MYSQL_YYABORT_UNLESS($3 != TVL_YES || $4 != TVL_YES); + lex->tx_chain= $3; + lex->tx_release= $4; + } + ; + +rollback: + ROLLBACK_SYM opt_work opt_chain opt_release + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK; + /* Don't allow AND CHAIN RELEASE. */ + MYSQL_YYABORT_UNLESS($3 != TVL_YES || $4 != TVL_YES); + lex->tx_chain= $3; + lex->tx_release= $4; + } + | ROLLBACK_SYM opt_work TO_SYM SAVEPOINT_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; + lex->ident= $5; + } + | ROLLBACK_SYM opt_work TO_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_ROLLBACK_TO_SAVEPOINT; + lex->ident= $4; + } + ; + +savepoint: + SAVEPOINT_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_SAVEPOINT; + lex->ident= $2; + } + ; + +release: + RELEASE_SYM SAVEPOINT_SYM ident + { + LEX *lex=Lex; + lex->sql_command= SQLCOM_RELEASE_SAVEPOINT; + lex->ident= $3; + } + ; + +/* + UNIONS : glue selects together +*/ + +unit_type_decl: + UNION_SYM + { $$= UNION_TYPE; } + | INTERSECT_SYM + { $$= INTERSECT_TYPE; } + | EXCEPT_SYM + { $$= EXCEPT_TYPE; } + + +union_clause: + /* empty */ {} + | union_list + ; + +union_list: + unit_type_decl union_option + { + if (Lex->add_select_to_union_list((bool)$2, $1, TRUE)) + MYSQL_YYABORT; + } + union_list_part2 + { + /* + Remove from the name resolution context stack the context of the + last select in the union. + */ + Lex->pop_context(); + } + ; + +union_list_view: + unit_type_decl union_option + { + if (Lex->add_select_to_union_list((bool)$2, $1, TRUE)) + MYSQL_YYABORT; + } + query_expression_body_view + { + Lex->pop_context(); + } + ; + +union_order_or_limit: + { + LEX *lex= thd->lex; + DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE); + SELECT_LEX *sel= lex->current_select; + SELECT_LEX_UNIT *unit= sel->master_unit(); + SELECT_LEX *fake= unit->fake_select_lex; + if (fake) + { + fake->no_table_names_allowed= 1; + lex->current_select= fake; + } + thd->where= "global ORDER clause"; + } + order_or_limit + { + thd->lex->current_select->no_table_names_allowed= 0; + thd->where= ""; + } + ; + +order_or_limit: + order_clause opt_limit_clause + | limit_clause + ; + +/* + Start a UNION, for non-top level query expressions. +*/ +union_head_non_top: + unit_type_decl union_option + { + if (Lex->add_select_to_union_list((bool)$2, $1, FALSE)) + MYSQL_YYABORT; + } + ; + +union_option: + /* empty */ { $$=1; } + | DISTINCT { $$=1; } + | ALL { $$=0; } + ; + +/* + Corresponds to the SQL Standard + <query specification> ::= + SELECT [ <set quantifier> ] <select list> <table expression> + + Notes: + - We allow more options in addition to <set quantifier> + - <table expression> is optional in MariaDB +*/ +query_specification: + SELECT_SYM select_init2_derived opt_table_expression + { + $$= Lex->current_select->master_unit()->first_select(); + } + ; + +query_term_union_not_ready: + query_specification order_or_limit opt_select_lock_type { $$= $1; } + | '(' select_paren_derived ')' union_order_or_limit { $$= $2; } + ; + +query_term_union_ready: + query_specification opt_select_lock_type { $$= $1; } + | '(' select_paren_derived ')' { $$= $2; } + ; + +query_expression_body: + query_term_union_not_ready { $$= $1; } + | query_term_union_ready { $$= $1; } + | query_term_union_ready union_list_derived { $$= $1; } + ; + +/* Corresponds to <query expression> in the SQL:2003 standard. */ +subselect: + subselect_start opt_with_clause query_expression_body subselect_end + { + $3->set_with_clause($2); + $$= $3; + } + ; + +subselect_start: + { + LEX *lex=Lex; + if (!lex->expr_allows_subselect || + lex->sql_command == (int)SQLCOM_PURGE) + { + thd->parse_error(); + MYSQL_YYABORT; + } + /* + we are making a "derived table" for the parenthesis + as we need to have a lex level to fit the union + after the parenthesis, e.g. + (SELECT .. ) UNION ... becomes + SELECT * FROM ((SELECT ...) UNION ...) + */ + if (mysql_new_select(Lex, 1, NULL)) + MYSQL_YYABORT; + } + ; + +subselect_end: + { + LEX *lex=Lex; + + lex->check_automatic_up(UNSPECIFIED_TYPE); + lex->pop_context(); + SELECT_LEX *child= lex->current_select; + lex->current_select = lex->current_select->return_after_parsing(); + lex->nest_level--; + lex->current_select->n_child_sum_items += child->n_sum_items; + /* + A subselect can add fields to an outer select. Reserve space for + them. + */ + lex->current_select->select_n_where_fields+= + child->select_n_where_fields; + + /* + Aggregate functions in having clause may add fields to an outer + select. Count them also. + */ + lex->current_select->select_n_having_items+= + child->select_n_having_items; + } + ; + +opt_query_expression_options: + /* empty */ + | query_expression_option_list + ; + +query_expression_option_list: + query_expression_option_list query_expression_option + | query_expression_option + ; + +query_expression_option: + STRAIGHT_JOIN { Select->options|= SELECT_STRAIGHT_JOIN; } + | HIGH_PRIORITY + { + if (check_simple_select()) + MYSQL_YYABORT; + YYPS->m_lock_type= TL_READ_HIGH_PRIORITY; + YYPS->m_mdl_type= MDL_SHARED_READ; + Select->options|= SELECT_HIGH_PRIORITY; + } + | DISTINCT { Select->options|= SELECT_DISTINCT; } + | UNIQUE_SYM { Select->options|= SELECT_DISTINCT; } + | SQL_SMALL_RESULT { Select->options|= SELECT_SMALL_RESULT; } + | SQL_BIG_RESULT { Select->options|= SELECT_BIG_RESULT; } + | SQL_BUFFER_RESULT + { + if (check_simple_select()) + MYSQL_YYABORT; + Select->options|= OPTION_BUFFER_RESULT; + } + | SQL_CALC_FOUND_ROWS + { + if (check_simple_select()) + MYSQL_YYABORT; + Select->options|= OPTION_FOUND_ROWS; + } + | ALL { Select->options|= SELECT_ALL; } + ; + +/************************************************************************** + + DEFINER clause support. + +**************************************************************************/ + +definer_opt: + no_definer + | definer + ; + +no_definer: + /* empty */ + { + /* + We have to distinguish missing DEFINER-clause from case when + CURRENT_USER specified as definer explicitly in order to properly + handle CREATE TRIGGER statements which come to replication thread + from older master servers (i.e. to create non-suid trigger in this + case). + */ + thd->lex->definer= 0; + } + ; + +definer: + DEFINER_SYM '=' user_or_role + { + Lex->definer= $3; + Lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + Lex->ssl_cipher= Lex->x509_subject= Lex->x509_issuer= 0; + bzero(&(Lex->mqh), sizeof(Lex->mqh)); + } + ; + +/************************************************************************** + + CREATE VIEW statement parts. + +**************************************************************************/ + +view_algorithm: + ALGORITHM_SYM '=' UNDEFINED_SYM { $$= DTYPE_ALGORITHM_UNDEFINED; } + | ALGORITHM_SYM '=' MERGE_SYM { $$= VIEW_ALGORITHM_MERGE; } + | ALGORITHM_SYM '=' TEMPTABLE_SYM { $$= VIEW_ALGORITHM_TMPTABLE; } + ; + +opt_view_suid: + /* empty */ { $$= VIEW_SUID_DEFAULT; } + | view_suid { $$= $1; } + ; + +view_suid: + SQL_SYM SECURITY_SYM DEFINER_SYM { $$= VIEW_SUID_DEFINER; } + | SQL_SYM SECURITY_SYM INVOKER_SYM { $$= VIEW_SUID_INVOKER; } + ; + +view_list_opt: + /* empty */ + {} + | '(' view_list ')' + ; + +view_list: + ident + { + Lex->view_list.push_back((LEX_STRING*) + thd->memdup(&$1, sizeof(LEX_STRING)), + thd->mem_root); + } + | view_list ',' ident + { + Lex->view_list.push_back((LEX_STRING*) + thd->memdup(&$3, sizeof(LEX_STRING)), + thd->mem_root); + } + ; + +view_select: + { + LEX *lex= Lex; + lex->parsing_options.allows_variable= FALSE; + lex->create_view->select.str= (char *) YYLIP->get_cpp_ptr(); + } + opt_with_clause query_expression_body_view view_check_option + { + LEX *lex= Lex; + size_t len= YYLIP->get_cpp_ptr() - lex->create_view->select.str; + uint not_used; + void *create_view_select= thd->memdup(lex->create_view->select.str, len); + lex->create_view->select.length= len; + lex->create_view->select.str= (char *) create_view_select; + trim_whitespace(thd->charset(), + &lex->create_view->select, + ¬_used); + lex->create_view->check= $4; + lex->parsing_options.allows_variable= TRUE; + lex->current_select->set_with_clause($2); + } + ; + +/* + SQL Standard <query expression body> for VIEWs. + Does not include INTO and PROCEDURE clauses. +*/ +query_expression_body_view: + SELECT_SYM select_options_and_item_list select_init3_view + | '(' select_paren_view ')' + | '(' select_paren_view ')' union_order_or_limit + | '(' select_paren_view ')' union_list_view + ; + +view_check_option: + /* empty */ { $$= VIEW_CHECK_NONE; } + | WITH CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; } + | WITH CASCADED CHECK_SYM OPTION { $$= VIEW_CHECK_CASCADED; } + | WITH LOCAL_SYM CHECK_SYM OPTION { $$= VIEW_CHECK_LOCAL; } + ; + +/************************************************************************** + + CREATE TRIGGER statement parts. + +**************************************************************************/ + +trigger_action_order: + FOLLOWS_SYM + { $$= TRG_ORDER_FOLLOWS; } + | PRECEDES_SYM + { $$= TRG_ORDER_PRECEDES; } + ; + +trigger_follows_precedes_clause: + /* empty */ + { + $$.ordering_clause= TRG_ORDER_NONE; + $$.anchor_trigger_name.str= NULL; + $$.anchor_trigger_name.length= 0; + } + | + trigger_action_order ident_or_text + { + $$.ordering_clause= $1; + $$.anchor_trigger_name= $2; + } + ; + +trigger_tail: + remember_name + opt_if_not_exists + { + if (Lex->add_create_options_with_check($2)) + MYSQL_YYABORT; + } + sp_name + trg_action_time + trg_event + ON + remember_name /* $8 */ + { /* $9 */ + Lex->raw_trg_on_table_name_begin= YYLIP->get_tok_start(); + } + table_ident /* $10 */ + FOR_SYM + remember_name /* $12 */ + { /* $13 */ + Lex->raw_trg_on_table_name_end= YYLIP->get_tok_start(); + } + EACH_SYM + ROW_SYM + { + Lex->trg_chistics.ordering_clause_begin= YYLIP->get_cpp_ptr(); + } + trigger_follows_precedes_clause /* $17 */ + { /* $18 */ + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + + if (lex->sphead) + my_yyabort_error((ER_SP_NO_RECURSIVE_CREATE, MYF(0), "TRIGGER")); + + lex->stmt_definition_begin= $1; + lex->ident.str= $8; + lex->ident.length= $12 - $8; + lex->spname= $4; + (*static_cast<st_trg_execution_order*>(&lex->trg_chistics))= ($17); + lex->trg_chistics.ordering_clause_end= lip->get_cpp_ptr(); + + if (!lex->make_sp_head(thd, $4, &sp_handler_trigger)) + MYSQL_YYABORT; + + lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); + } + sp_proc_stmt /* $19 */ + { /* $20 */ + LEX *lex= Lex; + sp_head *sp= lex->sphead; + if (sp->check_unresolved_goto()) + MYSQL_YYABORT; + + lex->sql_command= SQLCOM_CREATE_TRIGGER; + sp->set_stmt_end(thd); + sp->restore_thd_mem_root(thd); + + if (sp->is_not_allowed_in_function("trigger")) + MYSQL_YYABORT; + + /* + We have to do it after parsing trigger body, because some of + sp_proc_stmt alternatives are not saving/restoring LEX, so + lex->query_tables can be wiped out. + */ + if (!lex->select_lex.add_table_to_list(thd, $10, + (LEX_CSTRING*) 0, + TL_OPTION_UPDATING, + TL_READ_NO_INSERT, + MDL_SHARED_NO_WRITE)) + MYSQL_YYABORT; + } + ; + +/************************************************************************** + + CREATE FUNCTION | PROCEDURE statements parts. + +**************************************************************************/ + +udf_tail: + opt_if_not_exists ident + RETURNS_SYM udf_type SONAME_SYM TEXT_STRING_sys + { + LEX *lex= thd->lex; + if (lex->add_create_options_with_check($1)) + MYSQL_YYABORT; + if (is_native_function(thd, & $2)) + my_yyabort_error((ER_NATIVE_FCT_NAME_COLLISION, MYF(0), $2.str)); + lex->sql_command= SQLCOM_CREATE_FUNCTION; + lex->udf.name= $2; + lex->udf.returns= (Item_result) $4; + lex->udf.dl= $6.str; + } + ; + + +sf_return_type: + RETURN_SYM + { + LEX *lex= Lex; + lex->init_last_field(&lex->sphead->m_return_field_def, + &empty_clex_str, + thd->variables.collation_database); + } + sp_param_type_with_opt_collate + { + if (Lex->sphead->fill_field_definition(thd, Lex->last_field)) + MYSQL_YYABORT; + } + ; + +sf_tail: + opt_if_not_exists + sp_name + { + Lex->sql_command= SQLCOM_CREATE_SPFUNCTION; + if (!Lex->make_sp_head_no_recursive(thd, $1, $2, + &sp_handler_function)) + MYSQL_YYABORT; + } + opt_sp_parenthesized_fdparam_list + sf_return_type + sp_c_chistics + { + LEX *lex= thd->lex; + Lex_input_stream *lip= YYLIP; + + lex->sphead->set_chistics(lex->sp_chistics); + lex->sphead->set_body_start(thd, lip->get_cpp_tok_start()); + } + sp_tail_is + sp_body + { + if (Lex->sp_body_finalize_function(thd)) + MYSQL_YYABORT; + } + ; + +sp_tail: + opt_if_not_exists sp_name + { + Lex->sql_command= SQLCOM_CREATE_PROCEDURE; + if (!Lex->make_sp_head_no_recursive(thd, $1, $2, + &sp_handler_procedure)) + MYSQL_YYABORT; + } + opt_sp_parenthesized_pdparam_list + sp_c_chistics + { + Lex->sphead->set_chistics(Lex->sp_chistics); + Lex->sphead->set_body_start(thd, YYLIP->get_cpp_tok_start()); + } + sp_tail_is + sp_body + { + if (Lex->sp_body_finalize_procedure(thd)) + MYSQL_YYABORT; + } + ; + +sf_tail_standalone: + sf_tail opt_sp_name + { + if ($2 && !$2->eq(Lex->sphead)) + my_yyabort_error((ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0), + ErrConvDQName($2).ptr(), + ErrConvDQName(Lex->sphead).ptr())); + } + ; + +sp_tail_standalone: + sp_tail opt_sp_name + { + if ($2 && !$2->eq(Lex->sphead)) + my_yyabort_error((ER_END_IDENTIFIER_DOES_NOT_MATCH, MYF(0), + ErrConvDQName($2).ptr(), + ErrConvDQName(Lex->sphead).ptr())); + } + ; + +sp_tail_is: + IS + | AS + ; + +/*************************************************************************/ + +xa: + XA_SYM begin_or_start xid opt_join_or_resume + { + Lex->sql_command = SQLCOM_XA_START; + } + | XA_SYM END xid opt_suspend + { + Lex->sql_command = SQLCOM_XA_END; + } + | XA_SYM PREPARE_SYM xid + { + Lex->sql_command = SQLCOM_XA_PREPARE; + } + | XA_SYM COMMIT_SYM xid opt_one_phase + { + Lex->sql_command = SQLCOM_XA_COMMIT; + } + | XA_SYM ROLLBACK_SYM xid + { + Lex->sql_command = SQLCOM_XA_ROLLBACK; + } + | XA_SYM RECOVER_SYM + { + Lex->sql_command = SQLCOM_XA_RECOVER; + } + ; + +xid: + text_string + { + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE); + if (!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))) + MYSQL_YYABORT; + Lex->xid->set(1L, $1->ptr(), $1->length(), 0, 0); + } + | text_string ',' text_string + { + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + if (!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))) + MYSQL_YYABORT; + Lex->xid->set(1L, $1->ptr(), $1->length(), $3->ptr(), $3->length()); + } + | text_string ',' text_string ',' ulong_num + { + MYSQL_YYABORT_UNLESS($1->length() <= MAXGTRIDSIZE && $3->length() <= MAXBQUALSIZE); + if (!(Lex->xid=(XID *)thd->alloc(sizeof(XID)))) + MYSQL_YYABORT; + Lex->xid->set($5, $1->ptr(), $1->length(), $3->ptr(), $3->length()); + } + ; + +begin_or_start: + BEGIN_SYM {} + | START_SYM {} + ; + +opt_join_or_resume: + /* nothing */ { Lex->xa_opt=XA_NONE; } + | JOIN_SYM { Lex->xa_opt=XA_JOIN; } + | RESUME_SYM { Lex->xa_opt=XA_RESUME; } + ; + +opt_one_phase: + /* nothing */ { Lex->xa_opt=XA_NONE; } + | ONE_SYM PHASE_SYM { Lex->xa_opt=XA_ONE_PHASE; } + ; + +opt_suspend: + /* nothing */ + { Lex->xa_opt=XA_NONE; } + | SUSPEND_SYM + { Lex->xa_opt=XA_SUSPEND; } + opt_migrate + ; + +opt_migrate: + /* nothing */ {} + | FOR_SYM MIGRATE_SYM { Lex->xa_opt=XA_FOR_MIGRATE; } + ; + +install: + INSTALL_SYM PLUGIN_SYM ident SONAME_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSTALL_PLUGIN; + lex->comment= $3; + lex->ident= $5; + } + | INSTALL_SYM SONAME_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_INSTALL_PLUGIN; + lex->comment= null_clex_str; + lex->ident= $3; + } + ; + +uninstall: + UNINSTALL_SYM PLUGIN_SYM ident + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; + lex->comment= $3; + } + | UNINSTALL_SYM SONAME_SYM TEXT_STRING_sys + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_UNINSTALL_PLUGIN; + lex->comment= null_clex_str; + lex->ident= $3; + } + ; + +/* Avoid compiler warning from sql_yacc.cc where yyerrlab1 is not used */ +keep_gcc_happy: + IMPOSSIBLE_ACTION + { + YYERROR; + } + +/** + @} (end of group Parser) +*/ diff --git a/sql/strfunc.cc b/sql/strfunc.cc index 4928e939e27..b09eadb098e 100644 --- a/sql/strfunc.cc +++ b/sql/strfunc.cc @@ -331,10 +331,10 @@ outp: >=0 Ordinal position */ -int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle, +int find_string_in_array(LEX_CSTRING * const haystack, LEX_CSTRING * const needle, CHARSET_INFO * const cs) { - const LEX_STRING *pos; + const LEX_CSTRING *pos; for (pos= haystack; pos->str; pos++) if (!cs->coll->strnncollsp(cs, (uchar *) pos->str, pos->length, (uchar *) needle->str, needle->length)) @@ -345,12 +345,12 @@ int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle, } -char *set_to_string(THD *thd, LEX_STRING *result, ulonglong set, - const char *lib[]) +const char *set_to_string(THD *thd, LEX_CSTRING *result, ulonglong set, + const char *lib[]) { char buff[STRING_BUFFER_USUAL_SIZE*8]; String tmp(buff, sizeof(buff), &my_charset_latin1); - LEX_STRING unused; + LEX_CSTRING unused; if (!result) result= &unused; @@ -376,12 +376,12 @@ char *set_to_string(THD *thd, LEX_STRING *result, ulonglong set, return result->str; } -char *flagset_to_string(THD *thd, LEX_STRING *result, ulonglong set, - const char *lib[]) +const char *flagset_to_string(THD *thd, LEX_CSTRING *result, ulonglong set, + const char *lib[]) { char buff[STRING_BUFFER_USUAL_SIZE*8]; String tmp(buff, sizeof(buff), &my_charset_latin1); - LEX_STRING unused; + LEX_CSTRING unused; if (!result) result= &unused; diff --git a/sql/strfunc.h b/sql/strfunc.h index 7b031710c76..7f3021e1a6d 100644 --- a/sql/strfunc.h +++ b/sql/strfunc.h @@ -33,12 +33,13 @@ uint find_type2(const TYPELIB *lib, const char *find, uint length, void unhex_type2(TYPELIB *lib); uint check_word(TYPELIB *lib, const char *val, const char *end, const char **end_of_word); -int find_string_in_array(LEX_STRING * const haystack, LEX_STRING * const needle, +int find_string_in_array(LEX_CSTRING * const haystack, + LEX_CSTRING * const needle, CHARSET_INFO * const cs); -char *flagset_to_string(THD *thd, LEX_STRING *result, ulonglong set, +const char *flagset_to_string(THD *thd, LEX_CSTRING *result, ulonglong set, const char *lib[]); -char *set_to_string(THD *thd, LEX_STRING *result, ulonglong set, - const char *lib[]); +const char *set_to_string(THD *thd, LEX_CSTRING *result, ulonglong set, + const char *lib[]); /* These functions were protected by INNODB_COMPATIBILITY_HOOKS diff --git a/sql/structs.h b/sql/structs.h index 98eb0f2585d..97702e5727b 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -29,6 +29,7 @@ #include <mysql_com.h> /* USERNAME_LENGTH */ struct TABLE; +class Type_handler; class Field; class Index_statistics; @@ -124,10 +125,10 @@ typedef struct st_key { union { plugin_ref parser; /* Fulltext [pre]parser */ - LEX_STRING *parser_name; /* Fulltext [pre]parser name */ + LEX_CSTRING *parser_name; /* Fulltext [pre]parser name */ }; KEY_PART_INFO *key_part; - char *name; /* Name of key */ + const char *name; /* Name of key */ /* Unique name for cache; db + \0 + table_name + \0 + key_name + \0 */ uchar *cache_name; /* @@ -152,7 +153,7 @@ typedef struct st_key { int bdb_return_if_eq; } handler; TABLE *table; - LEX_STRING comment; + LEX_CSTRING comment; /** reference to the list of options or NULL */ engine_option_value *option_list; ha_index_option_struct *option_struct; /* structure with parsed options */ @@ -203,24 +204,39 @@ extern const char *show_comp_option_name[]; typedef int *(*update_var)(THD *, struct st_mysql_show_var *); -typedef struct st_lex_user { - LEX_STRING user, host, plugin, auth; - LEX_STRING pwtext, pwhash; + +struct AUTHID +{ + LEX_CSTRING user, host; + void init() { memset(this, 0, sizeof(*this)); } + void copy(MEM_ROOT *root, const LEX_CSTRING *usr, const LEX_CSTRING *host); bool is_role() const { return user.str[0] && !host.str[0]; } - void set_lex_string(LEX_STRING *l, char *buf) + void set_lex_string(LEX_CSTRING *l, char *buf) { if (is_role()) *l= user; else - l->length= strxmov(l->str= buf, user.str, "@", host.str, NullS) - buf; + { + l->str= buf; + l->length= strxmov(buf, user.str, "@", host.str, NullS) - buf; + } } + void parse(const char *str, size_t length); + bool read_from_mysql_proc_row(THD *thd, TABLE *table); +}; + + +struct LEX_USER: public AUTHID +{ + LEX_CSTRING plugin, auth; + LEX_CSTRING pwtext, pwhash; void reset_auth() { pwtext.length= pwhash.length= plugin.length= auth.length= 0; pwtext.str= pwhash.str= 0; - plugin.str= auth.str= const_cast<char*>(""); + plugin.str= auth.str= ""; } -} LEX_USER; +}; /* This structure specifies the maximum amount of resources which @@ -578,27 +594,27 @@ public: struct Lex_field_type_st: public Lex_length_and_dec_st { private: - enum_field_types m_type; - void set(enum_field_types type, const char *length, const char *dec) + const Type_handler *m_handler; + void set(const Type_handler *handler, const char *length, const char *dec) { - m_type= type; + m_handler= handler; Lex_length_and_dec_st::set(length, dec); } public: - void set(enum_field_types type, Lex_length_and_dec_st length_and_dec) + void set(const Type_handler *handler, Lex_length_and_dec_st length_and_dec) { - m_type= type; + m_handler= handler; Lex_length_and_dec_st::operator=(length_and_dec); } - void set(enum_field_types type, const char *length) + void set(const Type_handler *handler, const char *length) { - set(type, length, 0); + set(handler, length, 0); } - void set(enum_field_types type) + void set(const Type_handler *handler) { - set(type, 0, 0); + set(handler, 0, 0); } - enum_field_types field_type() const { return m_type; } + const Type_handler *type_handler() const { return m_handler; } }; @@ -629,4 +645,89 @@ public: }; +struct Lex_spblock_handlers_st +{ +public: + int hndlrs; + void init(int count) { hndlrs= count; } +}; + + +struct Lex_spblock_st: public Lex_spblock_handlers_st +{ +public: + int vars; + int conds; + int curs; + void init() + { + vars= conds= hndlrs= curs= 0; + } + void init_using_vars(uint nvars) + { + vars= nvars; + conds= hndlrs= curs= 0; + } + void join(const Lex_spblock_st &b1, const Lex_spblock_st &b2) + { + vars= b1.vars + b2.vars; + conds= b1.conds + b2.conds; + hndlrs= b1.hndlrs + b2.hndlrs; + curs= b1.curs + b2.curs; + } +}; + + +class Lex_spblock: public Lex_spblock_st +{ +public: + Lex_spblock() { init(); } + Lex_spblock(const Lex_spblock_handlers_st &other) + { + vars= conds= curs= 0; + hndlrs= other.hndlrs; + } +}; + + +struct Lex_for_loop_bounds_st +{ +public: + class sp_assignment_lex *m_index; + class sp_assignment_lex *m_upper_bound; + int8 m_direction; + bool m_implicit_cursor; + bool is_for_loop_cursor() const { return m_upper_bound == NULL; } +}; + + +struct Lex_for_loop_st +{ +public: + class sp_variable *m_index; + class sp_variable *m_upper_bound; + int m_cursor_offset; + int8 m_direction; + bool m_implicit_cursor; + void init() + { + m_index= 0; + m_upper_bound= 0; + m_direction= 0; + m_implicit_cursor= false; + } + void init(const Lex_for_loop_st &other) + { + *this= other; + } + bool is_for_loop_cursor() const { return m_upper_bound == NULL; } +}; + + +struct Lex_string_with_pos_st: public LEX_CSTRING +{ + const char *m_pos; +}; + + #endif /* STRUCTS_INCLUDED */ diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 9f5dfa2d3a9..eb154d215d3 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -1035,7 +1035,7 @@ static Sys_var_lexstring Sys_init_connect( #ifdef HAVE_REPLICATION static bool check_master_connection(sys_var *self, THD *thd, set_var *var) { - LEX_STRING tmp; + LEX_CSTRING tmp; tmp.str= var->save_result.string_value.str; tmp.length= var->save_result.string_value.length; if (!tmp.str || check_master_connection_name(&tmp)) @@ -1567,7 +1567,7 @@ static Sys_var_gtid_binlog_pos Sys_gtid_binlog_pos( uchar * -Sys_var_gtid_binlog_pos::global_value_ptr(THD *thd, const LEX_STRING *base) +Sys_var_gtid_binlog_pos::global_value_ptr(THD *thd, const LEX_CSTRING *base) { char buf[128]; String str(buf, sizeof(buf), system_charset_info); @@ -1595,7 +1595,7 @@ static Sys_var_gtid_current_pos Sys_gtid_current_pos( uchar * -Sys_var_gtid_current_pos::global_value_ptr(THD *thd, const LEX_STRING *base) +Sys_var_gtid_current_pos::global_value_ptr(THD *thd, const LEX_CSTRING *base) { String str; char *p; @@ -1676,7 +1676,7 @@ Sys_var_gtid_slave_pos::global_update(THD *thd, set_var *var) uchar * -Sys_var_gtid_slave_pos::global_value_ptr(THD *thd, const LEX_STRING *base) +Sys_var_gtid_slave_pos::global_value_ptr(THD *thd, const LEX_CSTRING *base) { String str; char *p; @@ -1797,7 +1797,7 @@ Sys_var_gtid_binlog_state::global_update(THD *thd, set_var *var) uchar * -Sys_var_gtid_binlog_state::global_value_ptr(THD *thd, const LEX_STRING *base) +Sys_var_gtid_binlog_state::global_value_ptr(THD *thd, const LEX_CSTRING *base) { char buf[512]; String str(buf, sizeof(buf), system_charset_info); @@ -1830,7 +1830,7 @@ static Sys_var_last_gtid Sys_last_gtid( uchar * -Sys_var_last_gtid::session_value_ptr(THD *thd, const LEX_STRING *base) +Sys_var_last_gtid::session_value_ptr(THD *thd, const LEX_CSTRING *base) { char buf[10+1+10+1+20+1]; String str(buf, sizeof(buf), system_charset_info); @@ -1939,7 +1939,7 @@ Sys_var_slave_parallel_mode::global_update(THD *thd, set_var *var) { enum_slave_parallel_mode new_value= (enum_slave_parallel_mode)var->save_result.ulonglong_value; - LEX_STRING *base_name= &var->base; + LEX_CSTRING *base_name= &var->base; Master_info *mi; bool res= false; @@ -1959,7 +1959,7 @@ Sys_var_slave_parallel_mode::global_update(THD *thd, set_var *var) if (mi->rli.slave_running) { my_error(ER_SLAVE_MUST_STOP, MYF(0), - mi->connection_name.length, mi->connection_name.str); + (int) mi->connection_name.length, mi->connection_name.str); res= true; } else @@ -1982,7 +1982,7 @@ Sys_var_slave_parallel_mode::global_update(THD *thd, set_var *var) uchar * Sys_var_slave_parallel_mode::global_value_ptr(THD *thd, - const LEX_STRING *base_name) + const LEX_CSTRING *base_name) { Master_info *mi; enum_slave_parallel_mode val= @@ -2394,6 +2394,7 @@ export const char *optimizer_switch_names[]= "exists_to_in", "orderby_uses_equalities", "condition_pushdown_for_derived", + "split_grouping_derived", "default", NullS }; @@ -3088,7 +3089,7 @@ static const char *sql_mode_names[]= }; export bool sql_mode_string_representation(THD *thd, sql_mode_t sql_mode, - LEX_STRING *ls) + LEX_CSTRING *ls) { set_to_string(thd, ls, sql_mode, sql_mode_names); return ls->str == 0; @@ -3502,6 +3503,14 @@ static Sys_var_charptr Sys_version_compile_os( CMD_LINE_HELP_ONLY, IN_SYSTEM_CHARSET, DEFAULT(SYSTEM_TYPE)); +#include <source_revision.h> +static char *server_version_source_revision; +static Sys_var_charptr Sys_version_source_revision( + "version_source_revision", "Source control revision id for MariaDB source code", + READ_ONLY GLOBAL_VAR(server_version_source_revision), + CMD_LINE_HELP_ONLY, + IN_SYSTEM_CHARSET, DEFAULT(SOURCE_REVISION)); + static char *guess_malloc_library() { if (strcmp(MALLOC_LIBRARY, "system") == 0) @@ -4447,7 +4456,7 @@ static Sys_var_mybool Sys_relay_log_recovery( bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var) { bool result= true; // Assume error - LEX_STRING *base_name= &var->base; + LEX_CSTRING *base_name= &var->base; if (!base_name->length) base_name= &thd->variables.default_master_connection; @@ -4461,7 +4470,7 @@ bool Sys_var_rpl_filter::global_update(THD *thd, set_var *var) if (mi->rli.slave_running) { my_error(ER_SLAVE_MUST_STOP, MYF(0), - mi->connection_name.length, + (int) mi->connection_name.length, mi->connection_name.str); result= true; } @@ -4508,7 +4517,7 @@ bool Sys_var_rpl_filter::set_filter_value(const char *value, Master_info *mi) } uchar *Sys_var_rpl_filter::global_value_ptr(THD *thd, - const LEX_STRING *base_name) + const LEX_CSTRING *base_name) { char buf[256]; String tmp(buf, sizeof(buf), &my_charset_bin); @@ -4664,7 +4673,7 @@ static bool update_slave_skip_counter(sys_var *self, THD *thd, Master_info *mi) { if (mi->rli.slave_running) { - my_error(ER_SLAVE_MUST_STOP, MYF(0), mi->connection_name.length, + my_error(ER_SLAVE_MUST_STOP, MYF(0), (int) mi->connection_name.length, mi->connection_name.str); return true; } diff --git a/sql/sys_vars.ic b/sql/sys_vars.ic index bc913b1adbd..ba58315b449 100644 --- a/sql/sys_vars.ic +++ b/sql/sys_vars.ic @@ -364,9 +364,9 @@ public: { var->save_result.ulonglong_value= option.def_value; } uchar *valptr(THD *thd, ulong val) { return (uchar*)typelib.type_names[val]; } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, session_var(thd, ulong)); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, global_var(ulong)); } uchar *default_value_ptr(THD *thd) { return valptr(thd, (ulong)option.def_value); } @@ -612,14 +612,16 @@ public: /* parse and feel list with default values */ if (thd) { +#ifndef DBUG_OFF bool res= +#endif sysvartrack_validate_value(thd, var->save_result.string_value.str, var->save_result.string_value.length); DBUG_ASSERT(res == 0); } } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(thd != NULL); size_t len= sysvartrack_value_len(thd); @@ -669,7 +671,7 @@ public: void global_save_default(THD *thd, set_var *var) { DBUG_ASSERT(FALSE); } protected: - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { return thd->security_ctx->proxy_user[0] ? (uchar *) &(thd->security_ctx->proxy_user[0]) : NULL; @@ -685,7 +687,7 @@ public: {} protected: - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { return (uchar*)thd->security_ctx->external_user; } @@ -725,7 +727,7 @@ public: bool global_update(THD *thd, set_var *var); protected: - uchar *global_value_ptr(THD *thd, const LEX_STRING *base); + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base); bool set_filter_value(const char *value, Master_info *mi); }; @@ -736,7 +738,7 @@ protected: Class specific constructor arguments: enum charset_enum is_os_charset_arg - Backing store: LEX_STRING + Backing store: LEX_CSTRING @note Behaves exactly as Sys_var_charptr, only the backing store is different. @@ -757,22 +759,22 @@ public: getopt, is_os_charset_arg, def_val, lock, binlog_status_arg, on_check_func, on_update_func, substitute) { - global_var(LEX_STRING).length= strlen(def_val); - SYSVAR_ASSERT(size == sizeof(LEX_STRING)); + global_var(LEX_CSTRING).length= strlen(def_val); + SYSVAR_ASSERT(size == sizeof(LEX_CSTRING)); *const_cast<SHOW_TYPE*>(&show_val_type)= SHOW_LEX_STRING; } bool global_update(THD *thd, set_var *var) { if (Sys_var_charptr::global_update(thd, var)) return true; - global_var(LEX_STRING).length= var->save_result.string_value.length; + global_var(LEX_CSTRING).length= var->save_result.string_value.length; return false; } }; /* - A LEX_STRING stored only in thd->variables + A LEX_CSTRING stored only in thd->variables Only to be used for small buffers */ @@ -822,10 +824,10 @@ public: } bool session_update(THD *thd, set_var *var) { - LEX_STRING *tmp= &session_var(thd, LEX_STRING); + LEX_CSTRING *tmp= &session_var(thd, LEX_CSTRING); tmp->length= var->save_result.string_value.length; /* Store as \0 terminated string (just to be safe) */ - strmake(tmp->str, var->save_result.string_value.str, tmp->length); + strmake((char*) tmp->str, var->save_result.string_value.str, tmp->length); return false; } bool global_update(THD *thd, set_var *var) @@ -843,7 +845,7 @@ public: { DBUG_ASSERT(FALSE); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(FALSE); return NULL; @@ -913,13 +915,13 @@ public: char *ptr= (char*)(intptr)option.def_value; var->save_result.string_value.str= ptr; } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { char buf[256]; DBUG_EXPLAIN(buf, sizeof(buf)); return (uchar*) thd->strdup(buf); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { char buf[256]; DBUG_EXPLAIN_INITIAL(buf, sizeof(buf)); @@ -974,7 +976,7 @@ public: bool global_update(THD *thd, set_var *var) { ulonglong new_value= var->save_result.ulonglong_value; - LEX_STRING *base_name= &var->base; + LEX_CSTRING *base_name= &var->base; KEY_CACHE *key_cache; /* If no basename, assume it's for the key cache named 'default' */ @@ -1001,7 +1003,7 @@ public: return keycache_update(thd, key_cache, offset, new_value); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { KEY_CACHE *key_cache= get_key_cache(base); if (!key_cache) @@ -1186,7 +1188,7 @@ public: lock, binlog_status_arg, on_check_func, on_update_func, substitute) { } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { if (thd->user_connect && thd->user_connect->user_resources.user_conn) return (uchar*) &(thd->user_connect->user_resources.user_conn); @@ -1228,7 +1230,7 @@ public: global_var(ulonglong)= def_val; SYSVAR_ASSERT(typelib.count > 1); SYSVAR_ASSERT(typelib.count <= 65); - SYSVAR_ASSERT(def_val < my_set_bits(typelib.count)); + SYSVAR_ASSERT(def_val <= my_set_bits(typelib.count-1)); SYSVAR_ASSERT(strcmp(values[typelib.count-1], "default") == 0); SYSVAR_ASSERT(size == sizeof(ulonglong)); } @@ -1300,9 +1302,9 @@ public: { var->save_result.ulonglong_value= option.def_value; } uchar *valptr(THD *thd, ulonglong val) { return (uchar*)flagset_to_string(thd, 0, val, typelib.type_names); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, session_var(thd, ulonglong)); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, global_var(ulonglong)); } uchar *default_value_ptr(THD *thd) { return valptr(thd, option.def_value); } @@ -1399,9 +1401,9 @@ public: { var->save_result.ulonglong_value= option.def_value; } uchar *valptr(THD *thd, ulonglong val) { return (uchar*)set_to_string(thd, 0, val, typelib.type_names); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, session_var(thd, ulonglong)); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, global_var(ulonglong)); } uchar *default_value_ptr(THD *thd) { return valptr(thd, option.def_value); } @@ -1450,7 +1452,7 @@ public: var->save_result.plugin= NULL; else { - const LEX_STRING pname= { const_cast<char*>(res->ptr()), res->length() }; + const LEX_CSTRING pname= { const_cast<char*>(res->ptr()), res->length() }; plugin_ref plugin; // special code for storage engines (e.g. to handle historical aliases) @@ -1504,7 +1506,7 @@ public: if (!default_value) return 0; - LEX_STRING pname= { default_value, strlen(pname.str) }; + LEX_CSTRING pname= { default_value, strlen(default_value) }; plugin_ref plugin; if (plugin_type == MYSQL_STORAGE_ENGINE_PLUGIN) @@ -1525,9 +1527,9 @@ public: return (uchar*)(plugin ? thd->strmake(plugin_name(plugin)->str, plugin_name(plugin)->length) : 0); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, session_var(thd, plugin_ref)); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, global_var(plugin_ref)); } uchar *default_value_ptr(THD *thd) { return valptr(thd, get_default(thd)); } @@ -1593,11 +1595,11 @@ public: { DBUG_ASSERT(FALSE); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { return debug_sync_value_ptr(thd); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(FALSE); return 0; @@ -1680,9 +1682,9 @@ public: thd->sys_var_tmp.my_bool_value= reverse_semantics ^ ((val & bitmask) != 0); return (uchar*) &thd->sys_var_tmp.my_bool_value; } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, session_var(thd, ulonglong)); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, global_var(ulonglong)); } uchar *default_value_ptr(THD *thd) { @@ -1743,12 +1745,12 @@ public: { var->value= 0; } void global_save_default(THD *thd, set_var *var) { DBUG_ASSERT(FALSE); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { thd->sys_var_tmp.ulonglong_value= read_func(thd); return (uchar*) &thd->sys_var_tmp.ulonglong_value; } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(FALSE); return 0; @@ -1808,13 +1810,13 @@ public: { var->value= 0; } void global_save_default(THD *thd, set_var *var) { DBUG_ASSERT(FALSE); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { thd->sys_var_tmp.double_value= thd->start_time + thd->start_time_sec_part/(double)TIME_SECOND_PART_FACTOR; return (uchar*) &thd->sys_var_tmp.double_value; } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(FALSE); return 0; @@ -1878,12 +1880,12 @@ public: } void session_save_default(THD *thd, set_var *var) { } void global_save_default(THD *thd, set_var *var) { } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(FALSE); return 0; } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { return (uchar*)show_comp_option_name[global_var(enum SHOW_COMP_OPTION)]; } @@ -1955,9 +1957,9 @@ public: } uchar *valptr(THD *thd, uchar *val) { return val ? *(uchar**)(val+name_offset) : 0; } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, session_var(thd, uchar*)); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, global_var(uchar*)); } uchar *default_value_ptr(THD *thd) { return valptr(thd, *(uchar**)option.def_value); } @@ -2031,7 +2033,7 @@ public: } uchar *valptr(THD *thd, Time_zone *val) { return (uchar *)(val->get_name()->ptr()); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { /* This is an ugly fix for replication: we don't replicate properly queries @@ -2044,7 +2046,7 @@ public: thd->time_zone_used= 1; return valptr(thd, session_var(thd, Time_zone *)); } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { return valptr(thd, global_var(Time_zone*)); } uchar *default_value_ptr(THD *thd) { return valptr(thd, *(Time_zone**)option.def_value); } @@ -2215,7 +2217,7 @@ public: /* Use value given in variable declaration */ global_save_default(thd, var); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { ulonglong *tmp, res; tmp= (ulonglong*) (((uchar*)&(thd->variables)) + offset); @@ -2223,7 +2225,7 @@ public: *tmp= res; return (uchar*) tmp; } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { return session_value_ptr(thd, base); } @@ -2275,12 +2277,12 @@ public: { DBUG_ASSERT(false); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(false); return NULL; } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base); + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base); }; @@ -2324,12 +2326,12 @@ public: { DBUG_ASSERT(false); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(false); return NULL; } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base); + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base); }; @@ -2364,12 +2366,12 @@ public: /* Record the attempt to use default so we can error. */ var->value= 0; } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(false); return NULL; } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base); + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base); uchar *default_value_ptr(THD *thd) { return 0; } }; @@ -2406,12 +2408,12 @@ public: /* Record the attempt to use default so we can error. */ var->value= 0; } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(false); return NULL; } - uchar *global_value_ptr(THD *thd, const LEX_STRING *base); + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base); uchar *default_value_ptr(THD *thd) { return 0; } }; @@ -2456,8 +2458,8 @@ public: { DBUG_ASSERT(false); } - uchar *session_value_ptr(THD *thd, const LEX_STRING *base); - uchar *global_value_ptr(THD *thd, const LEX_STRING *base) + uchar *session_value_ptr(THD *thd, const LEX_CSTRING *base); + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base) { DBUG_ASSERT(false); return NULL; @@ -2483,5 +2485,5 @@ public: SYSVAR_ASSERT(scope() == GLOBAL); } bool global_update(THD *thd, set_var *var); - uchar *global_value_ptr(THD *thd, const LEX_STRING *base); + uchar *global_value_ptr(THD *thd, const LEX_CSTRING *base); }; diff --git a/sql/table.cc b/sql/table.cc index 2f403de9888..7131e9d4a7d 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -42,6 +42,7 @@ #include "sql_view.h" #include "rpl_filter.h" #include "sql_cte.h" +#include "ha_sequence.h" /* For MySQL 5.7 virtual fields */ #define MYSQL57_GENERATED_FIELD 128 @@ -52,25 +53,25 @@ static Virtual_column_info * unpack_vcol_info_from_frm(THD *, MEM_ROOT *, static bool check_vcol_forward_refs(Field *, Virtual_column_info *); /* INFORMATION_SCHEMA name */ -LEX_STRING INFORMATION_SCHEMA_NAME= {C_STRING_WITH_LEN("information_schema")}; +LEX_CSTRING INFORMATION_SCHEMA_NAME= {STRING_WITH_LEN("information_schema")}; /* PERFORMANCE_SCHEMA name */ -LEX_STRING PERFORMANCE_SCHEMA_DB_NAME= {C_STRING_WITH_LEN("performance_schema")}; +LEX_CSTRING PERFORMANCE_SCHEMA_DB_NAME= {STRING_WITH_LEN("performance_schema")}; /* MYSQL_SCHEMA name */ -LEX_STRING MYSQL_SCHEMA_NAME= {C_STRING_WITH_LEN("mysql")}; +LEX_CSTRING MYSQL_SCHEMA_NAME= {STRING_WITH_LEN("mysql")}; /* GENERAL_LOG name */ -LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")}; +LEX_CSTRING GENERAL_LOG_NAME= {STRING_WITH_LEN("general_log")}; /* SLOW_LOG name */ -LEX_STRING SLOW_LOG_NAME= {C_STRING_WITH_LEN("slow_log")}; +LEX_CSTRING SLOW_LOG_NAME= {STRING_WITH_LEN("slow_log")}; /* Keyword added as a prefix when parsing the defining expression for a virtual column read from the column definition saved in the frm file */ -static LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; +static LEX_CSTRING parse_vcol_keyword= { STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; static int64 last_table_id; @@ -207,8 +208,8 @@ View_creation_ctx * View_creation_ctx::create(THD *thd, static uchar *get_field_name(Field **buff, size_t *length, my_bool not_used __attribute__((unused))) { - *length= (uint) strlen((*buff)->field_name); - return (uchar*) (*buff)->field_name; + *length= (uint) (*buff)->field_name.length; + return (uchar*) (*buff)->field_name.str; } @@ -241,7 +242,8 @@ char *fn_rext(char *name) return name + strlen(name); } -TABLE_CATEGORY get_table_category(const LEX_STRING *db, const LEX_STRING *name) +TABLE_CATEGORY get_table_category(const LEX_CSTRING *db, + const LEX_CSTRING *name) { DBUG_ASSERT(db != NULL); DBUG_ASSERT(name != NULL); @@ -321,7 +323,7 @@ TABLE_SHARE *alloc_table_share(const char *db, const char *table_name, share->path.str= path_buff; share->path.length= path_length; - strmov(share->path.str, path); + strmov(path_buff, path); share->normalized_path.str= share->path.str; share->normalized_path.length= path_length; share->table_category= get_table_category(& share->db, & share->table_name); @@ -431,6 +433,7 @@ void TABLE_SHARE::destroy() ha_share= NULL; // Safety } + delete sequence; free_root(&stats_cb.mem_root, MYF(0)); stats_cb.stats_can_be_read= FALSE; stats_cb.stats_is_read= FALSE; @@ -619,7 +622,7 @@ enum open_frm_error open_table_def(THD *thd, TABLE_SHARE *share, uint flags) share->is_view= 1; if (flags & GTS_VIEW) { - LEX_STRING pathstr= { path, length }; + LEX_CSTRING pathstr= { path, length }; /* Create view file parser and hold it in TABLE_SHARE member view_def. @@ -1174,7 +1177,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, char *keynames, *names, *comment_pos; const uchar *forminfo, *extra2; const uchar *frm_image_end = frm_image + frm_length; - uchar *record, *null_flags, *null_pos, *mysql57_vcol_null_pos; + uchar *record, *null_flags, *null_pos, *mysql57_vcol_null_pos= 0; const uchar *disk_buff, *strpos; ulong pos, record_offset; ulong rec_buff_length; @@ -1271,7 +1274,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, case EXTRA2_DEFAULT_PART_ENGINE: #ifdef WITH_PARTITION_STORAGE_ENGINE { - LEX_STRING name= { (char*)extra2, length }; + LEX_CSTRING name= { (char*)extra2, length }; share->default_part_plugin= ha_resolve_by_name(NULL, &name, false); if (!share->default_part_plugin) goto err; @@ -1327,6 +1330,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, share->db_create_options= db_create_options= uint2korr(frm_image+30); share->db_options_in_use= share->db_create_options; share->mysql_version= uint4korr(frm_image+51); + share->table_type= TABLE_TYPE_NORMAL; share->null_field_first= 0; if (!frm_image[32]) // New frm file in 3.23 { @@ -1340,6 +1344,14 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, enum_value_with_check(thd, share, "transactional", frm_image[39] & 3, HA_CHOICE_MAX); share->page_checksum= (ha_choice) enum_value_with_check(thd, share, "page_checksum", (frm_image[39] >> 2) & 3, HA_CHOICE_MAX); + if (((ha_choice) enum_value_with_check(thd, share, "sequence", + (frm_image[39] >> 4) & 3, + HA_CHOICE_MAX)) == HA_CHOICE_YES) + { + share->table_type= TABLE_TYPE_SEQUENCE; + share->sequence= new (&share->mem_root) SEQUENCE(); + share->non_determinstic_insert= true; + } share->row_type= (enum row_type) enum_value_with_check(thd, share, "row_format", frm_image[40], ROW_TYPE_MAX); @@ -1425,7 +1437,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (next_chunk + 2 < buff_end) { uint str_db_type_length= uint2korr(next_chunk); - LEX_STRING name; + LEX_CSTRING name; name.str= (char*) next_chunk + 2; name.length= str_db_type_length; @@ -1472,7 +1484,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, else if (!tmp_plugin) { /* purecov: begin inspected */ - name.str[name.length]=0; + ((char*) name.str)[name.length]=0; my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str); goto err; /* purecov: end */ @@ -1523,7 +1535,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, { if (keyinfo->flags & HA_USES_PARSER) { - LEX_STRING parser_name; + LEX_CSTRING parser_name; if (next_chunk >= buff_end) { DBUG_PRINT("error", @@ -1736,10 +1748,12 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, enum_field_types field_type; CHARSET_INFO *charset=NULL; Field::geometry_type geom_type= Field::GEOM_GEOMETRY; - LEX_STRING comment; + LEX_CSTRING comment; + LEX_CSTRING name; Virtual_column_info *vcol_info= 0; uint gis_length, gis_decimals, srid= 0; Field::utype unireg_check; + const Type_handler *handler; if (new_frm_ver >= 3) { @@ -1951,12 +1965,16 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, /* Convert pre-10.2.2 timestamps to use Field::default_value */ unireg_check= (Field::utype) MTYP_TYPENR(unireg_type); + name.str= fieldnames.type_names[i]; + name.length= strlen(name.str); + if (!(handler= Type_handler::get_handler_by_real_type(field_type))) + goto err; // Not supported field type *field_ptr= reg_field= make_field(share, &share->mem_root, record+recpos, (uint32) field_length, - null_pos, null_bit_pos, pack_flag, field_type, charset, + null_pos, null_bit_pos, pack_flag, handler, charset, geom_type, srid, unireg_check, (interval_nr ? share->intervals+interval_nr-1 : NULL), - share->fieldnames.type_names[i]); + &name); if (!reg_field) // Not supported field type goto err; @@ -1989,8 +2007,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (vcol_info) { - vcol_info->name.str= const_cast<char*>(reg_field->field_name); - vcol_info->name.length = strlen(reg_field->field_name); + vcol_info->name= reg_field->field_name; if (mysql57_null_bits && !vcol_info->stored_in_db) { /* MySQL 5.7 has null bits last */ @@ -2409,18 +2426,19 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, DBUG_ASSERT(field_nr < share->fields); reg_field= share->field[field_nr]; } + else + reg_field= 0; // Safety vcol_screen_pos+= FRM_VCOL_NEW_HEADER_SIZE; vcol_info->set_vcol_type((enum_vcol_info_type) type); - vcol_info->name.length= name_length; if (name_length) + { vcol_info->name.str= strmake_root(&share->mem_root, (char*)vcol_screen_pos, name_length); - else - { - vcol_info->name.str= const_cast<char*>(reg_field->field_name); - vcol_info->name.length = strlen(reg_field->field_name); + vcol_info->name.length= name_length; } + else + vcol_info->name= reg_field->field_name; vcol_screen_pos+= name_length + expr_length; switch (type) { @@ -2576,7 +2594,8 @@ static bool sql_unusable_for_discovery(THD *thd, handlerton *engine, HA_CREATE_INFO *create_info= &lex->create_info; // ... not CREATE TABLE - if (lex->sql_command != SQLCOM_CREATE_TABLE) + if (lex->sql_command != SQLCOM_CREATE_TABLE && + lex->sql_command != SQLCOM_CREATE_SEQUENCE) return 1; // ... create like if (lex->create_info.like()) @@ -2651,7 +2670,7 @@ int TABLE_SHARE::init_from_sql_statement_string(THD *thd, bool write, else thd->set_n_backup_active_arena(arena, &backup); - thd->reset_db(db.str, db.length); + thd->reset_db((char*) db.str, db.length); lex_start(thd); if ((error= parse_sql(thd, & parser_state, NULL) || @@ -3045,6 +3064,17 @@ enum open_frm_error open_table_from_share(THD *thd, TABLE_SHARE *share, DBUG_ASSERT(!db_stat); } + if (share->sequence && outparam->file) + { + ha_sequence *file; + /* SEQUENCE table. Create a sequence handler over the original handler */ + if (!(file= (ha_sequence*) sql_sequence_hton->create(sql_sequence_hton, share, + &outparam->mem_root))) + goto err; + file->register_original_handler(outparam->file); + outparam->file= file; + } + outparam->reginfo.lock_type= TL_UNLOCK; outparam->current_lock= F_UNLCK; records=0; @@ -3578,30 +3608,6 @@ fix_type_pointers(const char ***array, TYPELIB *point_to_type, uint types, } /* fix_type_pointers */ -TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings) -{ - TYPELIB *result= (TYPELIB*) alloc_root(mem_root, sizeof(TYPELIB)); - if (!result) - return 0; - result->count=strings.elements; - result->name=""; - uint nbytes= (sizeof(char*) + sizeof(uint)) * (result->count + 1); - if (!(result->type_names= (const char**) alloc_root(mem_root, nbytes))) - return 0; - result->type_lengths= (uint*) (result->type_names + result->count + 1); - List_iterator<String> it(strings); - String *tmp; - for (uint i=0; (tmp=it++) ; i++) - { - result->type_names[i]= tmp->ptr(); - result->type_lengths[i]= tmp->length(); - } - result->type_names[result->count]= 0; // End marker - result->type_lengths[result->count]= 0; - return result; -} - - /* Search after a field with given start & length If an exact field isn't found, return longest field with starts @@ -3763,7 +3769,8 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, create_info->default_table_charset->number : 0); fileinfo[38]= (uchar) csid; fileinfo[39]= (uchar) ((uint) create_info->transactional | - ((uint) create_info->page_checksum << 2)); + ((uint) create_info->page_checksum << 2) | + ((create_info->sequence ? HA_CHOICE_YES : 0) << 4)); fileinfo[40]= (uchar) create_info->row_type; /* Bytes 41-46 were for RAID support; now reused for other purposes */ fileinfo[41]= (uchar) (csid >> 8); @@ -3800,6 +3807,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->transactional= share->transactional; create_info->page_checksum= share->page_checksum; create_info->option_list= share->option_list; + create_info->sequence= MY_TEST(share->sequence); DBUG_VOID_RETURN; } @@ -3972,7 +3980,6 @@ bool check_table_name(const char *name, size_t length, bool check_for_path_chars size_t name_length= 0; const char *end= name+length; - if (!check_for_path_chars && (check_for_path_chars= check_mysql50_prefix(name))) { @@ -4123,7 +4130,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) { Field *field= table->field[i]; - if (strncmp(field->field_name, field_def->name.str, + if (strncmp(field->field_name.str, field_def->name.str, field_def->name.length)) { /* @@ -4135,7 +4142,7 @@ Table_check_intact::check(TABLE *table, const TABLE_FIELD_DEF *table_def) "expected column '%s' at position %d, found '%s'.", table->s->db.str, table->alias.c_ptr(), field_def->name.str, i, - field->field_name); + field->field_name.str); } field->sql_type(sql_type); /* @@ -4553,10 +4560,13 @@ bool TABLE::fill_item_list(List<Item> *item_list) const is the same as the number of columns in the table. */ -void TABLE::reset_item_list(List<Item> *item_list) const +void TABLE::reset_item_list(List<Item> *item_list, uint skip) const { List_iterator_fast<Item> it(*item_list); - for (Field **ptr= field; *ptr; ptr++) + Field **ptr= field; + for ( ; skip && *ptr; skip--) + ptr++; + for (; *ptr; ptr++) { Item_field *item_field= (Item_field*) it++; DBUG_ASSERT(item_field != 0); @@ -4572,7 +4582,7 @@ void TABLE::reset_item_list(List<Item> *item_list) const buffer buffer for md5 writing */ -void TABLE_LIST::calc_md5(char *buffer) +void TABLE_LIST::calc_md5(const char *buffer) { uchar digest[16]; compute_md5_hash(digest, select_stmt.str, @@ -4660,8 +4670,9 @@ bool TABLE_LIST::create_field_translation(THD *thd) while ((item= it++)) { - DBUG_ASSERT(item->name && item->name[0]); - transl[field_count].name= thd->strdup(item->name); + DBUG_ASSERT(item->name.str && item->name.str[0]); + transl[field_count].name.str= thd->strmake(item->name.str, item->name.length); + transl[field_count].name.length= item->name.length; transl[field_count++].item= item; } field_translation= transl; @@ -5496,7 +5507,7 @@ bool TABLE_LIST::prepare_security(THD *thd) while ((tbl= tb++)) { DBUG_ASSERT(tbl->referencing_view); - char *local_db, *local_table_name; + const char *local_db, *local_table_name; if (tbl->view) { local_db= tbl->view_db.str; @@ -5621,15 +5632,15 @@ Natural_join_column::Natural_join_column(Item_field *field_param, } -const char *Natural_join_column::name() +LEX_CSTRING *Natural_join_column::name() { if (view_field) { DBUG_ASSERT(table_field == NULL); - return view_field->name; + return &view_field->name; } - return table_field->field_name; + return &table_field->field_name; } @@ -5639,7 +5650,7 @@ Item *Natural_join_column::create_item(THD *thd) { DBUG_ASSERT(table_field == NULL); return create_view_field(thd, table_ref, &view_field->item, - view_field->name); + &view_field->name); } return table_field; } @@ -5708,9 +5719,9 @@ void Field_iterator_view::set(TABLE_LIST *table) } -const char *Field_iterator_table::name() +LEX_CSTRING *Field_iterator_table::name() { - return (*ptr)->field_name; + return &(*ptr)->field_name; } @@ -5719,6 +5730,7 @@ Item *Field_iterator_table::create_item(THD *thd) SELECT_LEX *select= thd->lex->current_select; Item_field *item= new (thd->mem_root) Item_field(thd, &select->context, *ptr); + DBUG_ASSERT(strlen(item->name.str) == item->name.length); if (item && thd->variables.sql_mode & MODE_ONLY_FULL_GROUP_BY && !thd->lex->in_sum_func && select->cur_pos_in_select_list != UNDEF_POS) { @@ -5730,19 +5742,19 @@ Item *Field_iterator_table::create_item(THD *thd) } -const char *Field_iterator_view::name() +LEX_CSTRING *Field_iterator_view::name() { - return ptr->name; + return &ptr->name; } Item *Field_iterator_view::create_item(THD *thd) { - return create_view_field(thd, view, &ptr->item, ptr->name); + return create_view_field(thd, view, &ptr->item, &ptr->name); } Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, - const char *name) + LEX_CSTRING *name) { bool save_wrapper= thd->lex->select_lex.no_wrap_view_item; Item *field= *field_ref; @@ -7401,7 +7413,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) DBUG_ASSERT(vcol_info); DBUG_ASSERT(vcol_info->expr); - bool update, swap_values= 0; + bool update= 0, swap_values= 0; switch (update_mode) { case VCOL_UPDATE_FOR_READ: update= !vcol_info->stored_in_db @@ -7445,7 +7457,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) if (vcol_info->expr->save_in_field(vf, 0)) field_error= error= 1; DBUG_PRINT("info", ("field '%s' - updated error: %d", - vf->field_name, field_error)); + vf->field_name.str, field_error)); if (swap_values && (vf->flags & BLOB_FLAG)) { /* @@ -7459,7 +7471,7 @@ int TABLE::update_virtual_fields(handler *h, enum_vcol_update_mode update_mode) } else { - DBUG_PRINT("info", ("field '%s' - skipped", vf->field_name)); + DBUG_PRINT("info", ("field '%s' - skipped", vf->field_name.str)); } } if (handler_pushed) @@ -7533,7 +7545,7 @@ int TABLE::update_default_fields(bool update_command, bool ignore_errors) res|= field->evaluate_update_default_function(); if (!ignore_errors && res) { - my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name); + my_error(ER_CALCULATING_DEFAULT_VALUE, MYF(0), field->field_name.str); break; } res= 0; @@ -7953,9 +7965,8 @@ int TABLE_LIST::fetch_number_of_rows() if (jtbm_subselect) return 0; if (is_materialized_derived() && !fill_me) - { - table->file->stats.records= ((select_union*)derived->result)->records; + table->file->stats.records= ((select_unit*)(get_unit()->result))->records; set_if_bigger(table->file->stats.records, 2); table->used_stat_records= table->file->stats.records; } @@ -8288,3 +8299,38 @@ LEX_CSTRING *fk_option_name(enum_fk_option opt) }; return names + opt; } + + +Field *TABLE::find_field_by_name(LEX_CSTRING *str) const +{ + uint length= str->length; + for (Field **tmp= field; *tmp; tmp++) + { + if ((*tmp)->field_name.length == length && + !my_strcasecmp(system_charset_info, (*tmp)->field_name.str, str->str)) + return *tmp; + } + return NULL; +} + + +bool TABLE::export_structure(THD *thd, Row_definition_list *defs) +{ + for (Field **src= field; *src; src++) + { + uint offs; + if (defs->find_row_field_by_name(&src[0]->field_name, &offs)) + { + my_error(ER_DUP_FIELDNAME, MYF(0), src[0]->field_name.str); + return true; + } + Spvar_definition *def= new (thd->mem_root) Spvar_definition(thd, *src); + if (!def) + return true; + def->flags&= (uint) ~NOT_NULL_FLAG; + if ((def->sp_prepare_create_field(thd, thd->mem_root)) || + (defs->push_back(def, thd->mem_root))) + return true; + } + return false; +} diff --git a/sql/table.h b/sql/table.h index 22846e05bde..77a75335f75 100644 --- a/sql/table.h +++ b/sql/table.h @@ -54,6 +54,7 @@ struct TDC_element; class Virtual_column_info; class Table_triggers_list; class TMP_TABLE_PARAM; +class SEQUENCE; /* Used to identify NESTED_JOIN structures within a join (applicable only to @@ -448,15 +449,15 @@ enum enum_table_category }; typedef enum enum_table_category TABLE_CATEGORY; -TABLE_CATEGORY get_table_category(const LEX_STRING *db, - const LEX_STRING *name); +TABLE_CATEGORY get_table_category(const LEX_CSTRING *db, + const LEX_CSTRING *name); typedef struct st_table_field_type { - LEX_STRING name; - LEX_STRING type; - LEX_STRING cset; + LEX_CSTRING name; + LEX_CSTRING type; + LEX_CSTRING cset; } TABLE_FIELD_TYPE; @@ -600,7 +601,7 @@ struct TABLE_SHARE TABLE_STATISTICS_CB stats_cb; uchar *default_values; /* row with default values */ - LEX_STRING comment; /* Comment about table */ + LEX_CSTRING comment; /* Comment about table */ CHARSET_INFO *table_charset; /* Default charset of string fields */ MY_BITMAP *check_set; /* Fields used by check constrant */ @@ -615,12 +616,12 @@ struct TABLE_SHARE should correspond to each other. To ensure this one can use set_table_cache() methods. */ - LEX_STRING table_cache_key; - LEX_STRING db; /* Pointer to db */ - LEX_STRING table_name; /* Table name (for open) */ - LEX_STRING path; /* Path to .frm file (from datadir) */ - LEX_STRING normalized_path; /* unpack_filename(path) */ - LEX_STRING connect_string; + LEX_CSTRING table_cache_key; + LEX_CSTRING db; /* Pointer to db */ + LEX_CSTRING table_name; /* Table name (for open) */ + LEX_CSTRING path; /* Path to .frm file (from datadir) */ + LEX_CSTRING normalized_path; /* unpack_filename(path) */ + LEX_CSTRING connect_string; /* Set of keys in use, implemented as a Bitmap. @@ -642,6 +643,7 @@ struct TABLE_SHARE db_plugin ? plugin_hton(db_plugin) : NULL; } enum row_type row_type; /* How rows are stored */ + enum Table_type table_type; enum tmp_table_type tmp_table; /** Transactional or not. */ @@ -716,6 +718,9 @@ struct TABLE_SHARE */ const File_parser *view_def; + /* For sequence tables, the current sequence state */ + SEQUENCE *sequence; + /* Cache for row-based replication table share checks that does not need to be repeated. Possible values are: -1 when cache value is @@ -1300,10 +1305,11 @@ public: bool stats_is_read; /* Persistent statistics is read for the table */ bool histograms_are_read; MDL_ticket *mdl_ticket; + List<Field> splitting_fields; void init(THD *thd, TABLE_LIST *tl); bool fill_item_list(List<Item> *item_list) const; - void reset_item_list(List<Item> *item_list) const; + void reset_item_list(List<Item> *item_list, uint skip) const; void clear_column_bitmaps(void); void prepare_for_position(void); MY_BITMAP *prepare_for_keyread(uint index, MY_BITMAP *map); @@ -1440,6 +1446,8 @@ public: TABLE *tmp_table, TMP_TABLE_PARAM *tmp_table_param, bool with_cleanup); + Field *find_field_by_name(LEX_CSTRING *str) const; + bool export_structure(THD *thd, class Row_definition_list *defs); }; @@ -1486,16 +1494,16 @@ enum enum_fk_option { FK_OPTION_UNDEF, FK_OPTION_RESTRICT, FK_OPTION_CASCADE, typedef struct st_foreign_key_info { - LEX_STRING *foreign_id; - LEX_STRING *foreign_db; - LEX_STRING *foreign_table; - LEX_STRING *referenced_db; - LEX_STRING *referenced_table; + LEX_CSTRING *foreign_id; + LEX_CSTRING *foreign_db; + LEX_CSTRING *foreign_table; + LEX_CSTRING *referenced_db; + LEX_CSTRING *referenced_table; enum_fk_option update_method; enum_fk_option delete_method; - LEX_STRING *referenced_key_name; - List<LEX_STRING> foreign_fields; - List<LEX_STRING> referenced_fields; + LEX_CSTRING *referenced_key_name; + List<LEX_CSTRING> foreign_fields; + List<LEX_CSTRING> referenced_fields; } FOREIGN_KEY_INFO; LEX_CSTRING *fk_option_name(enum_fk_option opt); @@ -1558,7 +1566,8 @@ typedef struct st_schema_table /* Handle fileds for old SHOW */ int (*old_format) (THD *thd, struct st_schema_table *schema_table); int (*process_table) (THD *thd, TABLE_LIST *tables, TABLE *table, - bool res, LEX_STRING *db_name, LEX_STRING *table_name); + bool res, const LEX_CSTRING *db_name, + const LEX_CSTRING *table_name); int idx_field1, idx_field2; bool hidden; uint i_s_requested_object; /* the object we need to open(TABLE | VIEW) */ @@ -1618,10 +1627,6 @@ class IS_table_read_plan; #define JOIN_TYPE_RIGHT 2U #define JOIN_TYPE_OUTER 4U /* Marker that this is an outer join */ -#define VIEW_SUID_INVOKER 0 -#define VIEW_SUID_DEFINER 1 -#define VIEW_SUID_DEFAULT 2 - /* view WITH CHECK OPTION parameter options */ #define VIEW_CHECK_NONE 0 #define VIEW_CHECK_LOCAL 1 @@ -1635,16 +1640,16 @@ class IS_table_read_plan; /** The threshold size a blob field buffer before it is freed */ #define MAX_TDC_BLOB_SIZE 65536 -class select_union; +class select_unit; class TMP_TABLE_PARAM; Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref, - const char *name); + LEX_CSTRING *name); struct Field_translator { Item *item; - const char *name; + LEX_CSTRING name; }; @@ -1671,7 +1676,7 @@ public: public: Natural_join_column(Field_translator *field_param, TABLE_LIST *tab); Natural_join_column(Item_field *field_param, TABLE_LIST *tab); - const char *name(); + LEX_CSTRING *name(); Item *create_item(THD *thd); Field *field(); const char *table_name(); @@ -1792,8 +1797,8 @@ struct TABLE_LIST TABLE_LIST *next_local; /* link in a global list of all queries tables */ TABLE_LIST *next_global, **prev_global; - char *db, *alias, *table_name, *schema_table_name; - char *option; /* Used by cache index */ + const char *db, *alias, *table_name, *schema_table_name; + const char *option; /* Used by cache index */ Item *on_expr; /* Used with outer join */ Item *sj_on_expr; @@ -1868,7 +1873,7 @@ struct TABLE_LIST select_result for derived table to pass it from table creation to table filling procedure */ - select_union *derived_result; + select_unit *derived_result; /* Stub used for materialized derived tables. */ table_map map; /* ID bit of table (1,2,4,8,16...) */ table_map get_map() @@ -1984,12 +1989,12 @@ struct TABLE_LIST Item *where; /* VIEW WHERE clause condition */ Item *check_option; /* WITH CHECK OPTION condition */ LEX_STRING select_stmt; /* text of (CREATE/SELECT) statement */ - LEX_STRING md5; /* md5 of query text */ - LEX_STRING source; /* source of CREATE VIEW */ - LEX_STRING view_db; /* saved view database */ - LEX_STRING view_name; /* saved view name */ + LEX_CSTRING md5; /* md5 of query text */ + LEX_CSTRING source; /* source of CREATE VIEW */ + LEX_CSTRING view_db; /* saved view database */ + LEX_CSTRING view_name; /* saved view name */ LEX_STRING timestamp; /* GMT time stamp of last operation */ - st_lex_user definer; /* definer of view */ + LEX_USER definer; /* definer of view */ ulonglong file_version; /* version of file's field set */ ulonglong mariadb_version; /* version of server on creation */ ulonglong updatable_view; /* VIEW can be updated */ @@ -2056,8 +2061,8 @@ struct TABLE_LIST bool where_processed; /* TRUE <=> VIEW CHECK OPTION expression has been processed */ bool check_option_processed; - /* FRMTYPE_ERROR if any type is acceptable */ - enum frm_type_enum required_type; + /* TABLE_TYPE_UNKNOWN if any type is acceptable */ + Table_type required_type; handlerton *db_type; /* table_type for handler */ char timestamp_buffer[20]; /* buffer for timestamp (19+1) */ /* @@ -2096,6 +2101,7 @@ struct TABLE_LIST bool merged_for_insert; /* TRUE <=> don't prepare this derived table/view as it should be merged.*/ bool skip_prepare_derived; + bool sequence; /* Part of NEXTVAL/CURVAL/LASTVAL */ /* Items created by create_view_field and collected to change them in case @@ -2120,8 +2126,8 @@ struct TABLE_LIST These attributes MUST NOT be used for any purposes but the parsing. */ - LEX_STRING view_client_cs_name; - LEX_STRING view_connection_cl_name; + LEX_CSTRING view_client_cs_name; + LEX_CSTRING view_connection_cl_name; /* View definition (SELECT-statement) in the UTF-form. @@ -2171,7 +2177,7 @@ struct TABLE_LIST List<String> *partition_names; #endif /* WITH_PARTITION_STORAGE_ENGINE */ - void calc_md5(char *buffer); + void calc_md5(const char *buffer); int view_check_option(THD *thd, bool ignore_failure); bool create_field_translation(THD *thd); bool setup_underlying(THD *thd); @@ -2354,7 +2360,7 @@ struct TABLE_LIST @brief Returns the name of the database that the referenced table belongs to. */ - char *get_db_name() const { return view != NULL ? view_db.str : db; } + const char *get_db_name() const { return view != NULL ? view_db.str : db; } /** @brief Returns the name of the table that this TABLE_LIST represents. @@ -2362,7 +2368,7 @@ struct TABLE_LIST @details The unqualified table name or view name for a table or view, respectively. */ - char *get_table_name() const { return view != NULL ? view_name.str : table_name; } + const char *get_table_name() const { return view != NULL ? view_name.str : table_name; } bool is_active_sjm(); bool is_jtbm() { return MY_TEST(jtbm_subselect != NULL); } st_select_lex_unit *get_unit(); @@ -2416,7 +2422,7 @@ public: virtual void set(TABLE_LIST *)= 0; virtual void next()= 0; virtual bool end_of_fields()= 0; /* Return 1 at end of list */ - virtual const char *name()= 0; + virtual LEX_CSTRING *name()= 0; virtual Item *create_item(THD *)= 0; virtual Field *field()= 0; }; @@ -2436,7 +2442,7 @@ public: void set_table(TABLE *table) { ptr= table->field; } void next() { ptr++; } bool end_of_fields() { return *ptr == 0; } - const char *name(); + LEX_CSTRING *name(); Item *create_item(THD *thd); Field *field() { return *ptr; } }; @@ -2453,7 +2459,7 @@ public: void set(TABLE_LIST *table); void next() { ptr++; } bool end_of_fields() { return ptr == array_end; } - const char *name(); + LEX_CSTRING *name(); Item *create_item(THD *thd); Item **item_ptr() {return &ptr->item; } Field *field() { return 0; } @@ -2477,7 +2483,7 @@ public: void set(TABLE_LIST *table); void next(); bool end_of_fields() { return !cur_column_ref; } - const char *name() { return cur_column_ref->name(); } + LEX_CSTRING *name() { return cur_column_ref->name(); } Item *create_item(THD *thd) { return cur_column_ref->create_item(thd); } Field *field() { return cur_column_ref->field(); } Natural_join_column *column_ref() { return cur_column_ref; } @@ -2514,7 +2520,7 @@ public: void next(); bool end_of_fields() { return (table_ref == last_leaf && field_it->end_of_fields()); } - const char *name() { return field_it->name(); } + LEX_CSTRING *name() { return field_it->name(); } const char *get_table_name(); const char *get_db_name(); GRANT_INFO *grant(); @@ -2695,7 +2701,7 @@ int rename_file_ext(const char * from,const char * to,const char * ext); char *get_field(MEM_ROOT *mem, Field *field); bool get_field(MEM_ROOT *mem, Field *field, class String *res); -bool validate_comment_length(THD *thd, LEX_STRING *comment, size_t max_len, +bool validate_comment_length(THD *thd, LEX_CSTRING *comment, size_t max_len, uint err_code, const char *name); int closefrm(TABLE *table); @@ -2718,14 +2724,14 @@ static inline int set_zone(int nr,int min_zone,int max_zone) } /* performance schema */ -extern LEX_STRING PERFORMANCE_SCHEMA_DB_NAME; +extern LEX_CSTRING PERFORMANCE_SCHEMA_DB_NAME; -extern LEX_STRING GENERAL_LOG_NAME; -extern LEX_STRING SLOW_LOG_NAME; +extern LEX_CSTRING GENERAL_LOG_NAME; +extern LEX_CSTRING SLOW_LOG_NAME; /* information schema */ -extern LEX_STRING INFORMATION_SCHEMA_NAME; -extern LEX_STRING MYSQL_SCHEMA_NAME; +extern LEX_CSTRING INFORMATION_SCHEMA_NAME; +extern LEX_CSTRING MYSQL_SCHEMA_NAME; inline bool is_infoschema_db(const char *name, size_t len) { @@ -2740,7 +2746,6 @@ inline bool is_infoschema_db(const char *name) INFORMATION_SCHEMA_NAME.str, name); } -TYPELIB *typelib(MEM_ROOT *mem_root, List<String> &strings); inline void mark_as_null_row(TABLE *table) { diff --git a/sql/temporary_tables.cc b/sql/temporary_tables.cc index 8077324d202..5e5a05084b3 100644 --- a/sql/temporary_tables.cc +++ b/sql/temporary_tables.cc @@ -73,7 +73,9 @@ TABLE *THD::create_and_open_tmp_table(handlerton *hton, if ((share= create_temporary_table(hton, frm, path, db, table_name))) { + open_options|= HA_OPEN_FOR_CREATE; table= open_temporary_table(share, table_name, open_in_engine); + open_options&= ~HA_OPEN_FOR_CREATE; /* Failed to open a temporary table instance. As we are not passing @@ -1103,8 +1105,10 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share, if (open_table_from_share(this, share, alias, open_in_engine ? (uint)HA_OPEN_KEYFILE : 0, - EXTRA_RECORD, ha_open_options, table, - open_in_engine ? false : true)) + EXTRA_RECORD, + (ha_open_options | + (open_options & HA_OPEN_FOR_CREATE)), + table, open_in_engine ? false : true)) { my_free(table); DBUG_RETURN(NULL); @@ -1112,7 +1116,7 @@ TABLE *THD::open_temporary_table(TMP_TABLE_SHARE *share, table->reginfo.lock_type= TL_WRITE; /* Simulate locked */ table->grant.privilege= TMP_TABLE_ACLS; - share->tmp_table= (table->file->has_transactions() ? + share->tmp_table= (table->file->has_transaction_manager() ? TRANSACTIONAL_TMP_TABLE : NON_TRANSACTIONAL_TMP_TABLE); table->pos_in_table_list= 0; diff --git a/sql/transaction.cc b/sql/transaction.cc index d01519b8bd4..78cd3047f82 100644 --- a/sql/transaction.cc +++ b/sql/transaction.cc @@ -576,7 +576,7 @@ bool trans_rollback_stmt(THD *thd) /* Find a named savepoint in the current transaction. */ static SAVEPOINT ** -find_savepoint(THD *thd, LEX_STRING name) +find_savepoint(THD *thd, LEX_CSTRING name) { SAVEPOINT **sv= &thd->transaction.savepoints; @@ -602,7 +602,7 @@ find_savepoint(THD *thd, LEX_STRING name) @retval TRUE Failure */ -bool trans_savepoint(THD *thd, LEX_STRING name) +bool trans_savepoint(THD *thd, LEX_CSTRING name) { SAVEPOINT **sv, *newsv; DBUG_ENTER("trans_savepoint"); @@ -679,7 +679,7 @@ bool trans_savepoint(THD *thd, LEX_STRING name) @retval TRUE Failure */ -bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) +bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name) { int res= FALSE; SAVEPOINT *sv= *find_savepoint(thd, name); @@ -756,7 +756,7 @@ bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name) @retval TRUE Failure */ -bool trans_release_savepoint(THD *thd, LEX_STRING name) +bool trans_release_savepoint(THD *thd, LEX_CSTRING name) { int res= FALSE; SAVEPOINT *sv= *find_savepoint(thd, name); diff --git a/sql/transaction.h b/sql/transaction.h index 90de11aabe3..b6b22053150 100644 --- a/sql/transaction.h +++ b/sql/transaction.h @@ -34,9 +34,9 @@ bool trans_rollback_implicit(THD *thd); bool trans_commit_stmt(THD *thd); bool trans_rollback_stmt(THD *thd); -bool trans_savepoint(THD *thd, LEX_STRING name); -bool trans_rollback_to_savepoint(THD *thd, LEX_STRING name); -bool trans_release_savepoint(THD *thd, LEX_STRING name); +bool trans_savepoint(THD *thd, LEX_CSTRING name); +bool trans_rollback_to_savepoint(THD *thd, LEX_CSTRING name); +bool trans_release_savepoint(THD *thd, LEX_CSTRING name); bool trans_xa_start(THD *thd); bool trans_xa_end(THD *thd); diff --git a/sql/udf_example.c b/sql/udf_example.c index c7e2f989829..98700953b2c 100644 --- a/sql/udf_example.c +++ b/sql/udf_example.c @@ -56,7 +56,7 @@ ** ** Function 'myfunc_int' returns summary length of all its arguments. ** -** Function 'sequence' returns an sequence starting from a certain number. +** Function 'udf_sequence' returns an sequence starting from a certain number. ** ** Function 'myfunc_argument_name' returns name of argument. ** @@ -80,7 +80,7 @@ ** CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so"; ** CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so"; ** CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so"; -** CREATE FUNCTION sequence RETURNS INTEGER SONAME "udf_example.so"; +** CREATE FUNCTION udf_sequence RETURNS INTEGER SONAME "udf_example.so"; ** CREATE FUNCTION 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"; @@ -162,9 +162,9 @@ double myfunc_double(UDF_INIT *initid, UDF_ARGS *args, char *is_null, my_bool myfunc_int_init(UDF_INIT *initid, UDF_ARGS *args, char *message); longlong myfunc_int(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); -my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); - void sequence_deinit(UDF_INIT *initid); -longlong sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, +my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message); + void udf_sequence_deinit(UDF_INIT *initid); +longlong udf_sequence(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); my_bool avgcost_init( UDF_INIT* initid, UDF_ARGS* args, char* message ); void avgcost_deinit( UDF_INIT* initid ); @@ -642,7 +642,7 @@ my_bool myfunc_int_init(UDF_INIT *initid __attribute__((unused)), or 1 if no arguments have been given */ -my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) +my_bool udf_sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { if (args->arg_count > 1) { @@ -659,22 +659,22 @@ my_bool sequence_init(UDF_INIT *initid, UDF_ARGS *args, char *message) } bzero(initid->ptr,sizeof(longlong)); /* - sequence() is a non-deterministic function : it has different value + udf_sequence() is a non-deterministic function : it has different value even if called with the same arguments. */ initid->const_item=0; return 0; } -void sequence_deinit(UDF_INIT *initid) +void udf_sequence_deinit(UDF_INIT *initid) { if (initid->ptr) free(initid->ptr); } -longlong sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, - char *is_null __attribute__((unused)), - char *error __attribute__((unused))) +longlong udf_sequence(UDF_INIT *initid __attribute__((unused)), UDF_ARGS *args, + char *is_null __attribute__((unused)), + char *error __attribute__((unused))) { ulonglong val=0; if (args->arg_count) diff --git a/sql/udf_example.def b/sql/udf_example.def index 41150b24e8f..74230b638bf 100644 --- a/sql/udf_example.def +++ b/sql/udf_example.def @@ -14,9 +14,9 @@ EXPORTS myfunc_double myfunc_int_init myfunc_int - sequence_init - sequence_deinit - sequence + udf_sequence_init + udf_sequence_deinit + udf_sequence avgcost_init avgcost_deinit avgcost_reset diff --git a/sql/unireg.cc b/sql/unireg.cc index 06151d9318b..5d5b82ba015 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -73,7 +73,7 @@ static uchar *extra2_write_len(uchar *pos, size_t len) } static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type, - LEX_STRING *str) + const LEX_CSTRING *str) { *pos++ = type; pos= extra2_write_len(pos, str->length); @@ -84,7 +84,7 @@ static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type, static uchar *extra2_write(uchar *pos, enum extra2_frm_value_type type, LEX_CUSTRING *str) { - return extra2_write(pos, type, reinterpret_cast<LEX_STRING *>(str)); + return extra2_write(pos, type, reinterpret_cast<LEX_CSTRING *>(str)); } /** @@ -107,7 +107,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, List<Create_field> &create_fields, uint keys, KEY *key_info, handler *db_file) { - LEX_STRING str_db_type; + LEX_CSTRING str_db_type; uint reclength, key_info_length, i; ulong key_buff_length; ulong filepos, data_offset; @@ -616,7 +616,8 @@ static bool pack_header(THD *thd, uchar *forminfo, while ((field=it++)) { if (validate_comment_length(thd, &field->comment, COLUMN_COMMENT_MAXLEN, - ER_TOO_LONG_FIELD_COMMENT, field->field_name)) + ER_TOO_LONG_FIELD_COMMENT, + field->field_name.str)) DBUG_RETURN(1); totlength+= (size_t)field->length; @@ -625,14 +626,14 @@ static bool pack_header(THD *thd, uchar *forminfo, We mark first TIMESTAMP field with NOW() in DEFAULT or ON UPDATE as auto-update field. */ - if (field->sql_type == MYSQL_TYPE_TIMESTAMP && + if (field->real_field_type() == MYSQL_TYPE_TIMESTAMP && MTYP_TYPENR(field->unireg_check) != Field::NONE && !time_stamp_pos) time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1; length=field->pack_length; if ((uint) field->offset+ (uint) data_offset+ length > reclength) reclength=(uint) (field->offset+ data_offset + length); - n_length+= (ulong) strlen(field->field_name)+1; + n_length+= field->field_name.length + 1; field->interval_id=0; field->save_interval= 0; if (field->interval) @@ -776,7 +777,7 @@ static size_t packed_fields_length(List<Create_field> &create_fields) } length+= FCOMP; - length+= strlen(field->field_name)+1; + length+= field->field_name.length + 1; length+= field->comment.length; } length+= 2; @@ -807,8 +808,8 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, int2store(buff+8,field->pack_flag); buff[10]= (uchar) field->unireg_check; buff[12]= (uchar) field->interval_id; - buff[13]= (uchar) field->sql_type; - if (field->sql_type == MYSQL_TYPE_GEOMETRY) + buff[13]= (uchar) field->real_field_type(); + if (field->real_field_type() == MYSQL_TYPE_GEOMETRY) { buff[11]= 0; buff[14]= (uchar) field->geom_type; @@ -837,7 +838,7 @@ static bool pack_fields(uchar **buff_arg, List<Create_field> &create_fields, it.rewind(); while ((field=it++)) { - buff= (uchar*)strmov((char*) buff, field->field_name); + buff= (uchar*)strmov((char*) buff, field->field_name.str); *buff++=NAMES_SEP_CHAR; } *buff++= 0; @@ -953,13 +954,13 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, null_pos + null_count / 8, null_count & 7, field->pack_flag, - field->sql_type, + field->type_handler(), field->charset, field->geom_type, field->srid, field->unireg_check, field->save_interval ? field->save_interval : field->interval, - field->field_name); + &field->field_name); if (!regfield) { error= 1; @@ -975,7 +976,8 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, null_count++; } - if (field->sql_type == MYSQL_TYPE_BIT && !f_bit_as_char(field->pack_flag)) + if (field->real_field_type() == MYSQL_TYPE_BIT && + !f_bit_as_char(field->pack_flag)) null_count+= field->length & 7; if (field->default_value && !field->default_value->flags && @@ -989,7 +991,7 @@ static bool make_empty_rec(THD *thd, uchar *buff, uint table_options, /* If not ok or warning of level 'note' */ if (res != 0 && res != 3) { - my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name); + my_error(ER_INVALID_DEFAULT, MYF(0), regfield->field_name.str); error= 1; delete regfield; //To avoid memory leak goto err; diff --git a/sql/wsrep_binlog.cc b/sql/wsrep_binlog.cc index 483da2296ad..ad5ef9c5c55 100644 --- a/sql/wsrep_binlog.cc +++ b/sql/wsrep_binlog.cc @@ -451,8 +451,8 @@ void wsrep_dump_rbr_buf_with_header(THD *thd, const void *rbr_buf, char filename[PATH_MAX]= {0}; File file; IO_CACHE cache; - Log_event_writer writer(&cache); - Format_description_log_event *ev=NULL; + Log_event_writer writer(&cache, 0); + Format_description_log_event *ev= 0; int len= my_snprintf(filename, PATH_MAX, "%s/GRA_%lld_%lld_v2.log", wsrep_data_home_dir, (longlong) thd->thread_id, diff --git a/sql/wsrep_hton.cc b/sql/wsrep_hton.cc index b21ae10b371..d047c3580ef 100644 --- a/sql/wsrep_hton.cc +++ b/sql/wsrep_hton.cc @@ -96,7 +96,8 @@ void wsrep_register_hton(THD* thd, bool all) * replicated unless we declare wsrep hton as read/write here */ if (i->is_trx_read_write() || - (thd->lex->sql_command == SQLCOM_CREATE_TABLE && + ((thd->lex->sql_command == SQLCOM_CREATE_TABLE || + thd->lex->sql_command == SQLCOM_CREATE_SEQUENCE) && thd->wsrep_exec_mode == LOCAL_STATE)) { thd->ha_data[wsrep_hton->slot].ha_info[all].set_trx_read_write(); diff --git a/sql/wsrep_mysqld.cc b/sql/wsrep_mysqld.cc index d95c85f3008..02d0ee50e6b 100644 --- a/sql/wsrep_mysqld.cc +++ b/sql/wsrep_mysqld.cc @@ -1231,7 +1231,7 @@ int wsrep_to_buf_helper( THD* thd, const char *query, uint query_len, uchar** buf, size_t* buf_len) { IO_CACHE tmp_io_cache; - Log_event_writer writer(&tmp_io_cache); + Log_event_writer writer(&tmp_io_cache,0); if (open_cached_file(&tmp_io_cache, mysql_tmpdir, TEMP_PREFIX, 65536, MYF(MY_WME))) return 1; @@ -1317,8 +1317,8 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len) { C_STRING_WITH_LEN("ALTER ") }, { C_STRING_WITH_LEN("CREATE OR REPLACE ") }}; - buff.append(command[thd->lex->create_view_mode].str, - command[thd->lex->create_view_mode].length); + buff.append(command[thd->lex->create_view->mode].str, + command[thd->lex->create_view->mode].length); LEX_USER *definer; @@ -1346,9 +1346,9 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len) return 1; } - views->algorithm = lex->create_view_algorithm; - views->view_suid = lex->create_view_suid; - views->with_check = lex->create_view_check; + views->algorithm = lex->create_view->algorithm; + views->view_suid = lex->create_view->suid; + views->with_check = lex->create_view->check; view_store_options(thd, views, &buff); buff.append(STRING_WITH_LEN("VIEW ")); @@ -1377,8 +1377,8 @@ create_view_query(THD *thd, uchar** buf, size_t* buf_len) } buff.append(STRING_WITH_LEN(" AS ")); //buff.append(views->source.str, views->source.length); - buff.append(thd->lex->create_view_select.str, - thd->lex->create_view_select.length); + buff.append(thd->lex->create_view->select.str, + thd->lex->create_view->select.length); //int errcode= query_error_code(thd, TRUE); //if (thd->binlog_query(THD::STMT_QUERY_TYPE, // buff.ptr(), buff.length(), FALSE, FALSE, FALSE, errcod @@ -1473,7 +1473,7 @@ static bool wsrep_can_run_in_toi(THD *thd, const char *db, const char *table, 1: TOI replication was skipped -1: TOI replication failed */ -static int wsrep_TOI_begin(THD *thd, char *db_, char *table_, +static int wsrep_TOI_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list) { wsrep_status_t ret(WSREP_WARNING); @@ -1543,8 +1543,8 @@ static int wsrep_TOI_begin(THD *thd, char *db_, char *table_, ret, (thd->db ? thd->db : "(null)"), (thd->query()) ? thd->query() : "void"); - my_error(ER_LOCK_DEADLOCK, MYF(0), "WSREP replication failed. Check " - "your wsrep connection state and retry the query."); + my_message(ER_LOCK_DEADLOCK, "WSREP replication failed. Check " + "your wsrep connection state and retry the query.", MYF(0)); wsrep_keys_free(&key_arr); rc= -1; } @@ -1582,7 +1582,7 @@ static void wsrep_TOI_end(THD *thd) { } } -static int wsrep_RSU_begin(THD *thd, char *db_, char *table_) +static int wsrep_RSU_begin(THD *thd, const char *db_, const char *table_) { wsrep_status_t ret(WSREP_WARNING); WSREP_DEBUG("RSU BEGIN: %lld, %d : %s", (long long)wsrep_thd_trx_seqno(thd), @@ -1665,7 +1665,7 @@ static void wsrep_RSU_end(THD *thd) thd->variables.wsrep_on = 1; } -int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_, +int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list) { int ret= 0; @@ -1854,7 +1854,8 @@ bool wsrep_grant_mdl_exception(MDL_context *requestor_ctx, /* Print some debug information. */ if (wsrep_debug) { - if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE) + if (request_thd->lex->sql_command == SQLCOM_DROP_TABLE || + request_thd->lex->sql_command == SQLCOM_DROP_SEQUENCE) { WSREP_DEBUG("DROP caused BF abort"); } @@ -2308,25 +2309,23 @@ static int wsrep_create_sp(THD *thd, uchar** buf, size_t* buf_len) sp_head *sp = thd->lex->sphead; sql_mode_t saved_mode= thd->variables.sql_mode; String retstr(64); + LEX_CSTRING returns= empty_clex_str; retstr.set_charset(system_charset_info); log_query.set_charset(system_charset_info); - if (sp->m_type == TYPE_ENUM_FUNCTION) + if (sp->m_handler->type() == TYPE_ENUM_FUNCTION) { sp_returns_type(thd, retstr, sp); - } - - if (!show_create_sp(thd, &log_query, - sp->m_type, - (sp->m_explicit_name ? sp->m_db.str : NULL), - (sp->m_explicit_name ? sp->m_db.length : 0), - sp->m_name.str, sp->m_name.length, - sp->m_params.str, sp->m_params.length, - retstr.c_ptr(), retstr.length(), - sp->m_body.str, sp->m_body.length, - sp->m_chistics, &(thd->lex->definer->user), - &(thd->lex->definer->host), + returns= retstr.lex_cstring(); + } + if (sp->m_handler-> + show_create_sp(thd, &log_query, + sp->m_explicit_name ? sp->m_db : null_clex_str, + sp->m_name, sp->m_params, returns, + sp->m_body, sp->chistics(), + thd->lex->definer[0], + thd->lex->create_info, saved_mode)) { WSREP_WARN("SP create string failed: schema: %s, query: %s", @@ -2621,8 +2620,8 @@ static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len) LEX *lex= thd->lex; String stmt_query; - LEX_STRING definer_user; - LEX_STRING definer_host; + LEX_CSTRING definer_user; + LEX_CSTRING definer_host; if (!lex->definer) { @@ -2659,7 +2658,7 @@ static int wsrep_create_trigger_query(THD *thd, uchar** buf, size_t* buf_len) append_definer(thd, &stmt_query, &definer_user, &definer_host); - LEX_STRING stmt_definition; + LEX_CSTRING stmt_definition; uint not_used; stmt_definition.str= (char*) thd->lex->stmt_definition_begin; stmt_definition.length= thd->lex->stmt_definition_end diff --git a/sql/wsrep_mysqld.h b/sql/wsrep_mysqld.h index f154b51516b..0502a29527f 100644 --- a/sql/wsrep_mysqld.h +++ b/sql/wsrep_mysqld.h @@ -284,7 +284,7 @@ extern PSI_mutex_key key_LOCK_wsrep_desync; extern PSI_file_key key_file_wsrep_gra_log; #endif /* HAVE_PSI_INTERFACE */ struct TABLE_LIST; -int wsrep_to_isolation_begin(THD *thd, char *db_, char *table_, +int wsrep_to_isolation_begin(THD *thd, const char *db_, const char *table_, const TABLE_LIST* table_list); void wsrep_to_isolation_end(THD *thd); void wsrep_cleanup_transaction(THD *thd); diff --git a/sql/wsrep_thd.cc b/sql/wsrep_thd.cc index d62c38580b6..5f29d28b19f 100644 --- a/sql/wsrep_thd.cc +++ b/sql/wsrep_thd.cc @@ -103,7 +103,7 @@ static rpl_group_info* wsrep_relay_group_init(const char* log_fname) new Format_description_log_event(4); } - static LEX_STRING connection_name= { C_STRING_WITH_LEN("wsrep") }; + static LEX_CSTRING connection_name= { STRING_WITH_LEN("wsrep") }; /* Master_info's constructor initializes rpl_filter by either an already @@ -316,7 +316,6 @@ void wsrep_replay_transaction(THD *thd) else { WSREP_DEBUG("replay failed, rolling back"); - //my_error(ER_LOCK_DEADLOCK, MYF(0), "wsrep aborted transaction"); } thd->wsrep_conflict_state= ABORTED; wsrep->post_rollback(wsrep, &thd->wsrep_ws_handle); diff --git a/sql/wsrep_var.cc b/sql/wsrep_var.cc index 41a8f6c7eb0..8107ab12c6b 100644 --- a/sql/wsrep_var.cc +++ b/sql/wsrep_var.cc @@ -331,7 +331,7 @@ bool wsrep_provider_update (sys_var *self, THD* thd, enum_var_type type) //when fails if (wsrep_init()) { - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), tmp); + my_error(ER_CANT_OPEN_LIBRARY, MYF(0), tmp, my_error, "wsrep_init failed"); rcode = true; } free(tmp); @@ -629,7 +629,7 @@ bool wsrep_max_ws_size_update (sys_var *self, THD *thd, enum_var_type) char max_ws_size_opt[128]; my_snprintf(max_ws_size_opt, sizeof(max_ws_size_opt), - "repl.max_ws_size=%d", wsrep_max_ws_size); + "repl.max_ws_size=%lu", wsrep_max_ws_size); wsrep_status_t ret= wsrep->options_set(wsrep, max_ws_size_opt); if (ret != WSREP_OK) { |