diff options
Diffstat (limited to 'sql')
90 files changed, 7708 insertions, 1818 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt index e8bc72f15d7..a13e726fe99 100755 --- a/sql/CMakeLists.txt +++ b/sql/CMakeLists.txt @@ -46,7 +46,7 @@ SET (SQL_SOURCE discover.cc ../libmysql/errmsg.c field.cc field_conv.cc filesort.cc gstream.cc ha_partition.cc - handler.cc hash_filo.cc hash_filo.h + handler.cc hash_filo.cc hash_filo.h sql_plugin_services.h hostname.cc init.cc item.cc item_buff.cc item_cmpfunc.cc item_create.cc item_func.cc item_geofunc.cc item_row.cc item_strfunc.cc item_subselect.cc item_sum.cc item_timefunc.cc @@ -54,7 +54,7 @@ SET (SQL_SOURCE log_event.cc rpl_record.cc rpl_reporting.cc log_event_old.cc rpl_record_old.cc message.h mf_iocache.cc my_decimal.cc ../sql-common/my_time.c - mysqld.cc net_serv.cc + mysqld.cc net_serv.cc ../sql-common/client_plugin.c nt_servc.cc nt_servc.h opt_range.cc opt_range.h opt_sum.cc ../sql-common/pack.c parse_file.cc password.c procedure.cc protocol.cc records.cc repl_failsafe.cc rpl_filter.cc set_var.cc @@ -77,6 +77,7 @@ SET (SQL_SOURCE rpl_rli.cc rpl_mi.cc sql_servers.cc sql_connect.cc scheduler.cc sql_profile.cc event_parse_data.cc opt_table_elimination.cc + create_options.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.cc ${PROJECT_SOURCE_DIR}/sql/sql_yacc.h ${PROJECT_SOURCE_DIR}/include/mysqld_error.h diff --git a/sql/Makefile.am b/sql/Makefile.am index af34e961480..b738565f818 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -49,7 +49,7 @@ mysqld_LDADD = libndb.la \ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ item_strfunc.h item_timefunc.h \ - item_xmlfunc.h \ + item_xmlfunc.h sql_plugin_services.h \ item_create.h item_subselect.h item_row.h \ mysql_priv.h item_geofunc.h sql_bitmap.h \ procedure.h sql_class.h sql_lex.h sql_list.h \ @@ -78,7 +78,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ sql_plugin.h authors.h event_parse_data.h \ event_data_objects.h event_scheduler.h \ sql_partition.h partition_info.h partition_element.h \ - contributors.h sql_servers.h + contributors.h sql_servers.h \ + create_options.h mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ @@ -124,9 +125,9 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \ sql_plugin.cc sql_binlog.cc \ sql_builtin.cc sql_tablespace.cc partition_info.cc \ sql_servers.cc event_parse_data.cc \ - opt_table_elimination.cc + opt_table_elimination.cc create_options.cc -nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c +nodist_mysqld_SOURCES = mini_client_errors.c pack.c client.c my_time.c my_user.c client_plugin.c libndb_la_CPPFLAGS= @ndbcluster_includes@ libndb_la_SOURCES= ha_ndbcluster.cc \ @@ -140,10 +141,10 @@ mysql_tzinfo_to_sql_SOURCES = tztime.cc mysql_tzinfo_to_sql_CXXFLAGS= -DTZINFO2SQL DEFS = -DMYSQL_SERVER \ - -DDEFAULT_MYSQL_HOME="\"$(MYSQLBASEdir)\"" \ - -DMYSQL_DATADIR="\"$(MYSQLDATAdir)\"" \ - -DSHAREDIR="\"$(MYSQLSHAREdir)\"" \ - -DPLUGINDIR="\"$(pkgplugindir)\"" \ + -DDEFAULT_MYSQL_HOME='"$(MYSQLBASEdir)"' \ + -DMYSQL_DATADIR='"$(MYSQLDATAdir)"' \ + -DSHAREDIR='"$(MYSQLSHAREdir)"' \ + -DPLUGINDIR='"$(pkgplugindir)"' \ -DHAVE_EVENT_SCHEDULER \ @DEFS@ @@ -167,6 +168,8 @@ link_sources: @LN_CP_F@ $(top_srcdir)/sql-common/pack.c pack.c rm -f client.c @LN_CP_F@ $(top_srcdir)/sql-common/client.c client.c + rm -f client_plugin.c + @LN_CP_F@ $(top_srcdir)/sql-common/client_plugin.c client_plugin.c rm -f my_time.c @LN_CP_F@ $(top_srcdir)/sql-common/my_time.c my_time.c rm -f my_user.c diff --git a/sql/authors.h b/sql/authors.h index dfe3b143e2f..4a321bebb7d 100644 --- a/sql/authors.h +++ b/sql/authors.h @@ -34,23 +34,35 @@ struct show_table_authors_st { */ struct show_table_authors_st show_table_authors[]= { + { "Michael (Monty) Widenius", "Tusby, Finland", + "Lead developer and main author" }, + { "David Axmark", "London, England", + "MySQL founder; Small stuff long time ago, Monty ripped it out!" }, + { "Sergei Golubchik", "Kerpen, Germany", + "Full-text search, precision math" }, + { "Igor Babaev", "Bellevue, USA", "Optimizer, keycache, core work"}, + { "Sergey Petrunia", "St. Petersburg, Russia", "Optimizer"}, + { "Oleksandr Byelkin", "Lugansk, Ukraine", + "Query Cache (4.0), Subqueries (4.1), Views (5.0)" }, { "Brian (Krow) Aker", "Seattle, WA, USA", "Architecture, archive, federated, bunch of little stuff :)" }, - { "Venu Anuganti", "", "Client/server protocol (4.1)" }, - { "David Axmark", "Uppsala, Sweden", - "Small stuff long time ago, Monty ripped it out!" }, + { "Kristian Nielsen", "Copenhagen, Denmark", + "General build stuff," }, { "Alexander (Bar) Barkov", "Izhevsk, Russia", "Unicode and character sets (4.1)" }, + { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" }, + { "Venu Anuganti", "", "Client/server protocol (4.1)" }, + { "Konstantin Osipov", "Moscow, Russia", + "Prepared statements (4.1), Cursors (5.0)" }, + { "Dmitri Lenev", "Moscow, Russia", + "Time zones support (4.1), Triggers (5.0)" }, { "Omer BarNir", "Sunnyvale, CA, USA", "Testing (sometimes) and general QA stuff" }, - { "Guilhem Bichot", "Bordeaux, France", "Replication (since 4.0)" }, { "John Birrell", "", "Emulation of pthread_mutex() for OS/2" }, { "Andreas F. Bobak", "", "AGGREGATE extension to user-defined functions" }, { "Alexey Botchkov (Holyfoot)", "Izhevsk, Russia", "GIS extensions (4.1), embedded server (4.1), precision math (5.0)"}, { "Reggie Burnett", "Nashville, TN, USA", "Windows development, Connectors" }, - { "Oleksandr Byelkin", "Lugansk, Ukraine", - "Query Cache (4.0), Subqueries (4.1), Views (5.0)" }, { "Kent Boortz", "Orebro, Sweden", "Test platform, and general build stuff" }, { "Tim Bunce", "", "mysqlhotcopy" }, { "Yves Carlier", "", "mysqlaccess" }, @@ -67,8 +79,6 @@ struct show_table_authors_st show_table_authors[]= { { "Yuri Dario", "", "OS/2 port" }, { "Andrei Elkin", "Espoo, Finland", "Replication" }, { "Patrick Galbraith", "Sharon, NH", "Federated Engine, mysqlslap" }, - { "Sergei Golubchik", "Kerpen, Germany", - "Full-text search, precision math" }, { "Lenz Grimmer", "Hamburg, Germany", "Production (build and release) engineering" }, { "Nikolay Grishakin", "Austin, TX, USA", "Testing - Server" }, @@ -83,8 +93,6 @@ struct show_table_authors_st show_table_authors[]= { { "Hakan Küçükyılmaz", "Walldorf, Germany", "Testing - Server" }, { "Greg (Groggy) Lehey", "Uchunga, SA, Australia", "Backup" }, { "Matthias Leich", "Berlin, Germany", "Testing - Server" }, - { "Dmitri Lenev", "Moscow, Russia", - "Time zones support (4.1), Triggers (5.0)" }, { "Arjen Lentz", "Brisbane, Australia", "Documentation (2001-2004), Dutch error messages, LOG2()" }, { "Marc Liyanage", "", "Created Mac OS X packages" }, @@ -96,8 +104,6 @@ struct show_table_authors_st show_table_authors[]= { { "Jonathan (Jeb) Miller", "Kyle, TX, USA", "Testing - Cluster, Replication" }, { "Elliot Murphy", "Cocoa, FL, USA", "Replication and backup" }, - { "Kristian Nielsen", "Copenhagen, Denmark", - "General build stuff" }, { "Pekka Nouisiainen", "Stockholm, Sweden", "NDB Cluster: BLOB support, character set support, ordered indexes" }, { "Alexander Nozdrin", "Moscow, Russia", @@ -105,8 +111,6 @@ struct show_table_authors_st show_table_authors[]= { { "Per Eric Olsson", "", "Testing of dynamic record format" }, { "Jonas Oreland", "Stockholm, Sweden", "NDB Cluster, Online Backup, lots of other things" }, - { "Konstantin Osipov", "Moscow, Russia", - "Prepared statements (4.1), Cursors (5.0)" }, { "Alexander (Sasha) Pachev", "Provo, UT, USA", "Statement-based replication, SHOW CREATE TABLE, mysql-bench" }, { "Irena Pancirov", "", "Port to Windows with Borland compiler" }, @@ -144,9 +148,9 @@ struct show_table_authors_st show_table_authors[]= { { "Sergey Vojtovich", "Izhevsk, Russia", "Plugins infrastructure (5.1)" }, { "Matt Wagner", "Northfield, MN, USA", "Bug fixing" }, { "Jim Winstead Jr.", "Los Angeles, CA, USA", "Bug fixing" }, - { "Michael (Monty) Widenius", "Tusby, Finland", - "Lead developer and main author" }, { "Peter Zaitsev", "Tacoma, WA, USA", "SHA1(), AES_ENCRYPT(), AES_DECRYPT(), bug fixing" }, + {"Mark Mark Callaghan", "Texas, USA", "Statistics patches"}, + {"Percona", "CA, USA", "Microslow patches"}, {NULL, NULL, NULL} }; diff --git a/sql/client_settings.h b/sql/client_settings.h index 4f06c15a29e..850903a24e5 100644 --- a/sql/client_settings.h +++ b/sql/client_settings.h @@ -15,6 +15,7 @@ #include <thr_alarm.h> +#include <sql_common.h> #define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | \ CLIENT_SECURE_CONNECTION | CLIENT_TRANSACTIONS | \ @@ -28,10 +29,10 @@ #define mysql_master_send_query(A, B, C) 1 #define mysql_slave_send_query(A, B, C) 1 #define mysql_rpl_probe(mysql) 0 -#undef HAVE_SMEM #undef _CUSTOMCONFIG_ -#define mysql_server_init(a,b,c) 0 +#define mysql_server_init(a,b,c) mysql_client_plugin_init() +#define mysql_server_end() mysql_client_plugin_deinit() #ifdef HAVE_REPLICATION C_MODE_START diff --git a/sql/create_options.cc b/sql/create_options.cc new file mode 100644 index 00000000000..f4868f95330 --- /dev/null +++ b/sql/create_options.cc @@ -0,0 +1,612 @@ +/* Copyright (C) 2010 Monty Program Ab + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + + Engine defined options of tables/fields/keys in CREATE/ALTER TABLE. +*/ + +#include "mysql_priv.h" +#include "create_options.h" +#include <my_getopt.h> + +#define FRM_QUOTED_VALUE 0x8000 + +/** + Links this item to the given list end + + @param start The list beginning or NULL + @param end The list last element or does not matter +*/ + +void engine_option_value::link(engine_option_value **start, + engine_option_value **end) +{ + DBUG_ENTER("engine_option_value::link"); + DBUG_PRINT("enter", ("name: '%s' (%u) value: '%s' (%u)", + name.str, (uint) name.length, + value.str, (uint) value.length)); + engine_option_value *opt; + /* check duplicates to avoid writing them to frm*/ + for(opt= *start; + opt && ((opt->parsed && !opt->value.str) || + my_strnncoll(system_charset_info, + (uchar *)name.str, name.length, + (uchar*)opt->name.str, opt->name.length)); + opt= opt->next) /* no-op */; + if (opt) + { + opt->value.str= NULL; /* remove previous value */ + opt->parsed= TRUE; /* and don't issue warnings for it anymore */ + } + /* + Add this option to the end of the list + + @note: We add even if it is opt->value.str == NULL because it can be + ALTER TABLE to remove the option. + */ + if (*start) + { + (*end)->next= this; + *end= this; + } + else + { + /* + note that is *start == 0, the value of *end does not matter, + it can be uninitialized. + */ + *start= *end= this; + } + DBUG_VOID_RETURN; +} + +static bool report_wrong_value(THD *thd, const char *name, const char *val, + my_bool suppress_warning) +{ + if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS)) + { + my_error(ER_BAD_OPTION_VALUE, MYF(0), val, name); + return 1; + } + + /* + We may need to suppress warnings to avoid duplicate messages + about the same option (option list is parsed more than once during + CREATE/ALTER table). + Errors are not suppressed, as they abort the execution on the first parsing. + */ + if (!suppress_warning) + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_BAD_OPTION_VALUE, + ER(ER_BAD_OPTION_VALUE), val, name); + return 0; +} + +static bool report_unknown_option(THD *thd, engine_option_value *val, + my_bool suppress_warning) +{ + DBUG_ENTER("report_unknown_option"); + if (val->parsed) + { + DBUG_PRINT("info", ("parsed => exiting")); + DBUG_RETURN(FALSE); + } + + if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS)) + { + my_error(ER_UNKNOWN_OPTION, MYF(0), val->name.str); + DBUG_RETURN(TRUE); + } + + if (!suppress_warning) + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_UNKNOWN_OPTION, + ER(ER_UNKNOWN_OPTION), + val->name.str); + DBUG_RETURN(FALSE); +} + +static bool set_one_value(ha_create_table_option *opt, + THD *thd, LEX_STRING *value, void *base, + my_bool suppress_warning, + MEM_ROOT *root) +{ + DBUG_ENTER("set_one_value"); + DBUG_PRINT("enter", ("opt: 0x%lx type: %u name '%s' value: '%s'", + (ulong) opt, + opt->type, opt->name, + (value->str ? value->str : "<DEFAULT>"))); + switch (opt->type) + { + case HA_OPTION_TYPE_ULL: + { + ulonglong *val= (ulonglong*)((char*)base + opt->offset); + if (!value->str) + { + *val= opt->def_value; + DBUG_RETURN(0); + } + + my_option optp= { opt->name, 1, 0, (uchar **)val, 0, 0, GET_ULL, + REQUIRED_ARG, opt->def_value, opt->min_value, opt->max_value, + 0, opt->block_size, 0}; + + ulonglong orig_val= strtoull(value->str, NULL, 10); + my_bool unused; + *val= orig_val; + *val= getopt_ull_limit_value(*val, &optp, &unused); + if (*val == orig_val) + DBUG_RETURN(0); + + DBUG_RETURN(report_wrong_value(thd, opt->name, value->str, + suppress_warning)); + } + case HA_OPTION_TYPE_STRING: + { + char **val= (char **)((char *)base + opt->offset); + if (!value->str) + { + *val= 0; + DBUG_RETURN(0); + } + + if (!(*val= strmake_root(root, value->str, value->length))) + DBUG_RETURN(1); + DBUG_RETURN(0); + } + case HA_OPTION_TYPE_ENUM: + { + uint *val= (uint *)((char *)base + opt->offset), num; + + *val= opt->def_value; + if (!value->str) + DBUG_RETURN(0); + + const char *start= opt->values, *end; + + num= 0; + while (*start) + { + for (end=start; + *end && *end != ','; + end+= my_mbcharlen(system_charset_info, *end)) /* no-op */; + if (!my_strnncoll(system_charset_info, + (uchar*)start, end-start, + (uchar*)value->str, value->length)) + { + *val= num; + DBUG_RETURN(0); + } + if (*end) *end++; + start= end; + num++; + } + + DBUG_RETURN(report_wrong_value(thd, opt->name, value->str, + suppress_warning)); + } + case HA_OPTION_TYPE_BOOL: + { + bool *val= (bool *)((char *)base + opt->offset); + *val= opt->def_value; + + if (!value->str) + DBUG_RETURN(0); + + if (!my_strnncoll(system_charset_info, + (const uchar*)"NO", 2, + (uchar *)value->str, value->length) || + !my_strnncoll(system_charset_info, + (const uchar*)"OFF", 3, + (uchar *)value->str, value->length) || + !my_strnncoll(system_charset_info, + (const uchar*)"0", 1, + (uchar *)value->str, value->length)) + { + *val= FALSE; + DBUG_RETURN(FALSE); + } + + if (!my_strnncoll(system_charset_info, + (const uchar*)"YES", 3, + (uchar *)value->str, value->length) || + !my_strnncoll(system_charset_info, + (const uchar*)"ON", 2, + (uchar *)value->str, value->length) || + !my_strnncoll(system_charset_info, + (const uchar*)"1", 1, + (uchar *)value->str, value->length)) + { + *val= TRUE; + DBUG_RETURN(FALSE); + } + + DBUG_RETURN(report_wrong_value(thd, opt->name, value->str, + suppress_warning)); + } + } + DBUG_ASSERT(0); + my_error(ER_UNKNOWN_ERROR, MYF(0)); + DBUG_RETURN(1); +} + +static const size_t ha_option_type_sizeof[]= +{ sizeof(ulonglong), sizeof(char *), sizeof(uint), sizeof(bool)}; + +/** + Creates option structure and parses list of options in it + + @param thd thread handler + @param option_struct where to store pointer on the option struct + @param option_list list of options given by user + @param rules list of option description by engine + @param suppress_warning second parse so we do not need warnings + @param root MEM_ROOT where allocate memory + + @retval TRUE Error + @retval FALSE OK +*/ + +my_bool parse_option_list(THD* thd, void **option_struct, + engine_option_value *option_list, + ha_create_table_option *rules, + my_bool suppress_warning, + MEM_ROOT *root) +{ + ha_create_table_option *opt; + size_t option_struct_size= 0; + engine_option_value *val= option_list; + DBUG_ENTER("parse_option_list"); + DBUG_PRINT("enter", + ("struct: 0x%lx list: 0x%lx rules: 0x%lx suppres %u root 0x%lx", + (ulong) *option_struct, (ulong)option_list, (ulong)rules, + (uint) suppress_warning, (ulong) root)); + + if (rules) + { + LEX_STRING default_val= {NULL, 0}; + for (opt= rules; opt->name; opt++) + set_if_bigger(option_struct_size, opt->offset + + ha_option_type_sizeof[opt->type]); + + *option_struct= alloc_root(root, option_struct_size); + + /* set all values to default */ + for (opt= rules; opt->name; opt++) + set_one_value(opt, thd, &default_val, *option_struct, + suppress_warning, root); + } + + for (; val; val= val->next) + { + for (opt= rules; opt && opt->name; opt++) + { + if (my_strnncoll(system_charset_info, + (uchar*)opt->name, opt->name_length, + (uchar*)val->name.str, val->name.length)) + continue; + + if (set_one_value(opt, thd, &val->value, + *option_struct, suppress_warning || val->parsed, root)) + DBUG_RETURN(TRUE); + val->parsed= true; + break; + } + if (report_unknown_option(thd, val, suppress_warning)) + DBUG_RETURN(TRUE); + val->parsed= true; + } + + DBUG_RETURN(FALSE); +} + + +/** + Parses all table/fields/keys options + + @param thd thread handler + @param file handler of the table + @parem share descriptor of the table + + @retval TRUE Error + @retval FALSE OK +*/ + +my_bool parse_engine_table_options(THD *thd, handlerton *ht, + TABLE_SHARE *share) +{ + MEM_ROOT *root= &share->mem_root; + DBUG_ENTER("parse_engine_table_options"); + + if (parse_option_list(thd, &share->option_struct, share->option_list, + ht->table_options, TRUE, root)) + DBUG_RETURN(TRUE); + + for (Field **field= share->field; *field; field++) + { + if (parse_option_list(thd, &(*field)->option_struct, (*field)->option_list, + ht->field_options, TRUE, root)) + DBUG_RETURN(TRUE); + } + + for (uint index= 0; index < share->keys; index ++) + { + if (parse_option_list(thd, &share->key_info[index].option_struct, + share->key_info[index].option_list, + ht->index_options, TRUE, root)) + DBUG_RETURN(TRUE); + } + + DBUG_RETURN(FALSE); +} + + +/** + Returns representation length of key and value in the frm file +*/ + +uint engine_option_value::frm_length() +{ + /* + 1 byte - name length + 2 bytes - value length + + if value.str is NULL, this option is not written to frm (=DEFAULT) + */ + return value.str ? 1 + name.length + 2 + value.length : 0; +} + + +/** + Returns length of representation of option list in the frm file +*/ + +static uint option_list_frm_length(engine_option_value *opt) +{ + uint res= 0; + + for (; opt; opt= opt->next) + res+= opt->frm_length(); + + return res; +} + + +/** + Calculates length of options image in the .frm + + @param table_option_list list of table options + @param create_fields field descriptors list + @param keys number of keys + @param key_info array of key descriptors + + @returns length of image in frm +*/ + +uint engine_table_options_frm_length(engine_option_value *table_option_list, + List<Create_field> &create_fields, + uint keys, KEY *key_info) +{ + List_iterator<Create_field> it(create_fields); + Create_field *field; + uint res, index; + DBUG_ENTER("engine_table_options_frm_length"); + + res= option_list_frm_length(table_option_list); + + while ((field= it++)) + res+= option_list_frm_length(field->option_list); + + for (index= 0; index < keys; index++, key_info++) + res+= option_list_frm_length(key_info->option_list); + + /* + if there's at least one option somewhere (res > 0) + we write option lists for all fields and keys, zero-terminated. + If there're no options we write nothing at all (backward compatibility) + */ + DBUG_RETURN(res ? res + 1 + create_fields.elements + keys : 0); +} + + +/** + Writes image of the key and value to the frm image buffer + + @param buff pointer to the buffer free space beginning + + @returns pointer to byte after last recorded in the buffer +*/ + +uchar *engine_option_value::frm_image(uchar *buff) +{ + if (value.str) + { + *buff++= name.length; + memcpy(buff, name.str, name.length); + buff+= name.length; + int2store(buff, value.length | (quoted_value ? FRM_QUOTED_VALUE : 0)); + buff+= 2; + memcpy(buff, (const uchar *) value.str, value.length); + buff+= value.length; + } + return buff; +} + +/** + Writes image of the key and value to the frm image buffer + + @param buff pointer to the buffer to store the options in + @param opt list of options; + + @returns pointer to the end of the stored data in the buffer +*/ +static uchar *option_list_frm_image(uchar *buff, engine_option_value *opt) +{ + for (; opt; opt= opt->next) + buff= opt->frm_image(buff); + + *buff++= 0; + return buff; +} + + +/** + Writes options image in the .frm buffer + + @param buff pointer to the buffer + @param table_option_list list of table options + @param create_fields field descriptors list + @param keys number of keys + @param key_info array of key descriptors + + @returns pointer to byte after last recorded in the buffer +*/ + +uchar *engine_table_options_frm_image(uchar *buff, + engine_option_value *table_option_list, + List<Create_field> &create_fields, + uint keys, KEY *key_info) +{ + List_iterator<Create_field> it(create_fields); + Create_field *field; + KEY *key_info_end= key_info + keys; + DBUG_ENTER("engine_table_options_frm_image"); + + buff= option_list_frm_image(buff, table_option_list); + + while ((field= it++)) + buff= option_list_frm_image(buff, field->option_list); + + while (key_info < key_info_end) + buff= option_list_frm_image(buff, (key_info++)->option_list); + + DBUG_RETURN(buff); +} + +/** + Reads name and value from buffer, then link it in the list + + @param buff the buffer to read from + @param start The list beginning or NULL + @param end The list last element or does not matter + @param root MEM_ROOT for allocating + + @returns pointer to byte after last recorded in the buffer +*/ +uchar *engine_option_value::frm_read(const uchar *buff, engine_option_value **start, + engine_option_value **end, MEM_ROOT *root) +{ + LEX_STRING name, value; + uint len; + + name.length= buff[0]; + buff++; + if (!(name.str= strmake_root(root, (const char*)buff, name.length))) + return NULL; + buff+= name.length; + len= uint2korr(buff); + value.length= len & ~FRM_QUOTED_VALUE; + buff+= 2; + if (!(value.str= strmake_root(root, (const char*)buff, value.length))) + return NULL; + buff+= value.length; + + engine_option_value *ptr=new (root) + engine_option_value(name, value, len & FRM_QUOTED_VALUE, start, end); + if (!ptr) + return NULL; + + return (uchar *)buff; +} + + +/** + Reads options from this buffer + + @param buff the buffer to read from + @param length buffer length + @param share table descriptor + @param root MEM_ROOT for allocating + + @retval TRUE Error + @retval FALSE OK +*/ + +my_bool engine_table_options_frm_read(const uchar *buff, uint length, + TABLE_SHARE *share) +{ + const uchar *buff_end= buff + length; + engine_option_value *end; + MEM_ROOT *root= &share->mem_root; + uint count; + DBUG_ENTER("engine_table_options_frm_read"); + + while (buff < buff_end && *buff) + { + if (!(buff= engine_option_value::frm_read(buff, &share->option_list, &end, + root))) + DBUG_RETURN(TRUE); + } + buff++; + + for (count=0; count < share->fields; count++) + { + while (buff < buff_end && *buff) + { + if (!(buff= engine_option_value::frm_read(buff, + &share->field[count]->option_list, + &end, root))) + DBUG_RETURN(TRUE); + } + buff++; + } + + for (count=0; count < share->keys; count++) + { + while (buff < buff_end && *buff) + { + if (!(buff= engine_option_value::frm_read(buff, + &share->key_info[count].option_list, + &end, root))) + DBUG_RETURN(TRUE); + } + buff++; + } + + DBUG_RETURN(buff != buff_end); +} + +/** + Merges two lists of engine_option_value's with duplicate removal. +*/ + +engine_option_value *merge_engine_table_options(engine_option_value *first, + engine_option_value *second, + MEM_ROOT *root) +{ + engine_option_value *end, *opt; + DBUG_ENTER("merge_engine_table_options"); + + /* find last element */ + if (first && second) + for (end= first; end->next; end= end->next) /* no-op */; + + for (opt= second; opt; opt= opt->next) + new (root) engine_option_value(opt->name, opt->value, opt->quoted_value, + &first, &end); + DBUG_RETURN(first); +} diff --git a/sql/create_options.h b/sql/create_options.h new file mode 100644 index 00000000000..b66bbf43570 --- /dev/null +++ b/sql/create_options.h @@ -0,0 +1,92 @@ +/* Copyright (C) 2010 Monty Program Ab + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/** + @file + + Engine defined options of tables/fields/keys in CREATE/ALTER TABLE. +*/ + +#ifndef SQL_CREATE_OPTIONS_INCLUDED +#define SQL_CREATE_OPTIONS_INCLUDED + +#include "handler.h" + +class engine_option_value: public Sql_alloc +{ + public: + LEX_STRING name; + LEX_STRING 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' + + engine_option_value(LEX_STRING &name_arg, LEX_STRING &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 **start, engine_option_value **end) : + name(name_arg), value(null_lex_str), + next(NULL), parsed(false), quoted_value(false) + { + link(start, end); + } + engine_option_value(LEX_STRING &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))) + { + value.length= longlong10_to_str(value_arg, value.str, 10) - value.str; + link(start, end); + } + } + static uchar *frm_read(const uchar *buff, engine_option_value **start, + engine_option_value **end, MEM_ROOT *root); + void link(engine_option_value **start, engine_option_value **end); + uint frm_length(); + uchar *frm_image(uchar *buff); +}; + +typedef struct st_key KEY; +class Create_field; + +my_bool parse_engine_table_options(THD *thd, handlerton *ht, + TABLE_SHARE *share); +my_bool parse_option_list(THD* thd, void **option_struct, + engine_option_value *option_list, + ha_create_table_option *rules, + my_bool suppress_warning, + MEM_ROOT *root); +my_bool engine_table_options_frm_read(const uchar *buff, + uint length, + TABLE_SHARE *share); +engine_option_value *merge_engine_table_options(engine_option_value *source, + engine_option_value *changes, + MEM_ROOT *root); + +uint engine_table_options_frm_length(engine_option_value *table_option_list, + List<Create_field> &create_fields, + uint keys, KEY *key_info); +uchar *engine_table_options_frm_image(uchar *buff, + engine_option_value *table_option_list, + List<Create_field> &create_fields, + uint keys, KEY *key_info); +#endif diff --git a/sql/event_data_objects.cc b/sql/event_data_objects.cc index cdd1d0abff3..9b428ed6ec9 100644 --- a/sql/event_data_objects.cc +++ b/sql/event_data_objects.cc @@ -1366,7 +1366,7 @@ Event_job_data::execute(THD *thd, bool drop) DBUG_ENTER("Event_job_data::execute"); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); /* MySQL parser currently assumes that current database is either diff --git a/sql/event_db_repository.cc b/sql/event_db_repository.cc index 753e9d21b65..82f9d354888 100644 --- a/sql/event_db_repository.cc +++ b/sql/event_db_repository.cc @@ -105,7 +105,8 @@ const TABLE_FIELD_TYPE event_table_fields[ET_FIELD_COUNT] = { { C_STRING_WITH_LEN("sql_mode") }, { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," - "'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION'," + "'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY'," + "'NO_UNSIGNED_SUBTRACTION'," "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB'," "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40'," "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES'," @@ -422,17 +423,18 @@ Event_db_repository::index_read_for_db_for_i_s(THD *thd, TABLE *schema_table, } key_copy(key_buf, event_table->record[0], key_info, key_len); - if (!(ret= event_table->file->index_read_map(event_table->record[0], key_buf, - (key_part_map)1, - HA_READ_PREFIX))) + if (!(ret= event_table->file->ha_index_read_map(event_table->record[0], + key_buf, + (key_part_map)1, + HA_READ_PREFIX))) { DBUG_PRINT("info",("Found rows. Let's retrieve them. ret=%d", ret)); do { ret= copy_event_to_schema_table(thd, schema_table, event_table); if (ret == 0) - ret= event_table->file->index_next_same(event_table->record[0], - key_buf, key_len); + ret= event_table->file->ha_index_next_same(event_table->record[0], + key_buf, key_len); } while (ret == 0); } DBUG_PRINT("info", ("Scan finished. ret=%d", ret)); @@ -901,8 +903,9 @@ Event_db_repository::find_named_event(LEX_STRING db, LEX_STRING name, key_copy(key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { DBUG_PRINT("info", ("Row not found")); DBUG_RETURN(TRUE); diff --git a/sql/field.cc b/sql/field.cc index ac095b07117..99848bb7fbd 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -57,7 +57,7 @@ const char field_separator=','; ((ulong) ((LL(1) << min(arg, 4) * 8) - LL(1))) #define ASSERT_COLUMN_MARKED_FOR_READ DBUG_ASSERT(!table || (!table->read_set || bitmap_is_set(table->read_set, field_index))) -#define ASSERT_COLUMN_MARKED_FOR_WRITE DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index))) +#define ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED DBUG_ASSERT(!table || (!table->write_set || bitmap_is_set(table->write_set, field_index) || bitmap_is_set(&table->vcol_set, field_index))) /* Rules for merging different types of fields in UNION @@ -1306,13 +1306,13 @@ String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_val) 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) - :ptr(ptr_arg), null_ptr(null_ptr_arg), - table(0), orig_table(0), table_name(0), - field_name(field_name_arg), - 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), null_bit(null_bit_arg), - is_created_from_null_item(FALSE) + :ptr(ptr_arg), null_ptr(null_ptr_arg), table(0), orig_table(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), + null_bit(null_bit_arg), is_created_from_null_item(FALSE), vcol_info(0), + stored_in_db(TRUE) { flags=null_ptr ? 0: NOT_NULL_FLAG; comment.str= (char*) ""; @@ -1611,7 +1611,7 @@ longlong Field::convert_decimal2longlong(const my_decimal *val, int Field_num::store_decimal(const my_decimal *val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err= 0; longlong i= convert_decimal2longlong(val, unsigned_flag, &err); return test(err | store(i, unsigned_flag)); @@ -1683,7 +1683,7 @@ void Field_num::make_field(Send_field *field) int Field_str::store_decimal(const my_decimal *d) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; double val; /* TODO: use decimal2string? */ int err= warn_if_overflow(my_decimal2double(E_DEC_FATAL_ERROR & @@ -1760,7 +1760,7 @@ bool Field::get_time(MYSQL_TIME *ltime) int Field::store_time(MYSQL_TIME *ltime, timestamp_type type_arg) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[MAX_DATE_STRING_REP_LENGTH]; uint length= (uint) my_TIME_to_str(ltime, buff); return store(buff, length, &my_charset_bin); @@ -1887,7 +1887,7 @@ void Field_decimal::overflow(bool negative) int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[STRING_BUFFER_USUAL_SIZE]; String tmp(buff,sizeof(buff), &my_charset_bin); const uchar *from= (uchar*) from_arg; @@ -2253,7 +2253,7 @@ int Field_decimal::store(const char *from_arg, uint len, CHARSET_INFO *cs) int Field_decimal::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; if (unsigned_flag && nr < 0) { overflow(1); @@ -2298,7 +2298,7 @@ int Field_decimal::store(double nr) int Field_decimal::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[22]; uint length, int_part; char fyllchar; @@ -2578,7 +2578,7 @@ void Field_new_decimal::set_value_on_overflow(my_decimal *decimal_value, bool Field_new_decimal::store_value(const my_decimal *decimal_value) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; DBUG_ENTER("Field_new_decimal::store_value"); #ifndef DBUG_OFF @@ -2623,7 +2623,7 @@ bool Field_new_decimal::store_value(const my_decimal *decimal_value) int Field_new_decimal::store(const char *from, uint length, CHARSET_INFO *charset_arg) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err; my_decimal decimal_value; DBUG_ENTER("Field_new_decimal::store(char*)"); @@ -2690,7 +2690,7 @@ int Field_new_decimal::store(const char *from, uint length, int Field_new_decimal::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; my_decimal decimal_value; int err; DBUG_ENTER("Field_new_decimal::store(double)"); @@ -2725,7 +2725,7 @@ int Field_new_decimal::store(double nr) int Field_new_decimal::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; my_decimal decimal_value; int err; @@ -2747,7 +2747,7 @@ int Field_new_decimal::store(longlong nr, bool unsigned_val) int Field_new_decimal::store_decimal(const my_decimal *decimal_value) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; return store_value(decimal_value); } @@ -2968,7 +2968,7 @@ Field_new_decimal::unpack(uchar* to, int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error; longlong rnd; @@ -2980,7 +2980,7 @@ int Field_tiny::store(const char *from,uint len,CHARSET_INFO *cs) int Field_tiny::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; nr=rint(nr); if (unsigned_flag) @@ -3023,7 +3023,7 @@ int Field_tiny::store(double nr) int Field_tiny::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; if (unsigned_flag) @@ -3143,7 +3143,7 @@ void Field_tiny::sql_type(String &res) const int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int store_tmp; int error; longlong rnd; @@ -3164,7 +3164,7 @@ int Field_short::store(const char *from,uint len,CHARSET_INFO *cs) int Field_short::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; int16 res; nr=rint(nr); @@ -3216,7 +3216,7 @@ int Field_short::store(double nr) int Field_short::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; int16 res; @@ -3390,7 +3390,7 @@ void Field_short::sql_type(String &res) const int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int store_tmp; int error; longlong rnd; @@ -3404,7 +3404,7 @@ int Field_medium::store(const char *from,uint len,CHARSET_INFO *cs) int Field_medium::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; nr=rint(nr); if (unsigned_flag) @@ -3450,7 +3450,7 @@ int Field_medium::store(double nr) int Field_medium::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; if (unsigned_flag) @@ -3580,7 +3580,7 @@ void Field_medium::sql_type(String &res) const int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long store_tmp; int error; longlong rnd; @@ -3601,7 +3601,7 @@ int Field_long::store(const char *from,uint len,CHARSET_INFO *cs) int Field_long::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; int32 res; nr=rint(nr); @@ -3653,7 +3653,7 @@ int Field_long::store(double nr) int Field_long::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; int32 res; @@ -3827,7 +3827,7 @@ void Field_long::sql_type(String &res) const int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; char *end; ulonglong tmp; @@ -3857,7 +3857,7 @@ int Field_longlong::store(const char *from,uint len,CHARSET_INFO *cs) int Field_longlong::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; longlong res; @@ -3909,7 +3909,7 @@ int Field_longlong::store(double nr) int Field_longlong::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; if (nr < 0) // Only possible error @@ -4135,7 +4135,7 @@ int Field_float::store(const char *from,uint len,CHARSET_INFO *cs) int Field_float::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= truncate(&nr, FLT_MAX); float j= (float)nr; @@ -4397,7 +4397,7 @@ int Field_double::store(const char *from,uint len,CHARSET_INFO *cs) int Field_double::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= truncate(&nr, DBL_MAX); #ifdef WORDS_BIGENDIAN @@ -4824,7 +4824,7 @@ timestamp_auto_set_type Field_timestamp::get_auto_set_type() const int Field_timestamp::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME l_time; my_time_t tmp= 0; int error; @@ -4887,7 +4887,7 @@ int Field_timestamp::store(double nr) int Field_timestamp::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME l_time; my_time_t timestamp= 0; int error; @@ -5186,7 +5186,7 @@ int Field_time::store_time(MYSQL_TIME *ltime, timestamp_type time_type) int Field_time::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long tmp; int error= 0; if (nr > (double)TIME_MAX_VALUE) @@ -5224,7 +5224,7 @@ int Field_time::store(double nr) int Field_time::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long tmp; int error= 0; if (nr < (longlong) -TIME_MAX_VALUE && !unsigned_val) @@ -5395,7 +5395,7 @@ void Field_time::sql_type(String &res) const int Field_year::store(const char *from, uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char *end; int error; longlong nr= cs->cset->strntoull10rnd(cs, from, len, 0, &end, &error); @@ -5443,7 +5443,7 @@ int Field_year::store(double nr) int Field_year::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; if (nr < 0 || (nr >= 100 && nr <= 1900) || nr > 2155) { *ptr= 0; @@ -5516,7 +5516,7 @@ void Field_year::sql_type(String &res) const int Field_date::store(const char *from, uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME l_time; uint32 tmp; int error; @@ -5571,7 +5571,7 @@ int Field_date::store(double nr) int Field_date::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME not_used; int error; longlong initial_nr= nr; @@ -5750,7 +5750,7 @@ void Field_date::sql_type(String &res) const int Field_newdate::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long tmp; MYSQL_TIME l_time; int error; @@ -5800,7 +5800,7 @@ int Field_newdate::store(double nr) int Field_newdate::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME l_time; longlong tmp; int error; @@ -5836,7 +5836,7 @@ int Field_newdate::store(longlong nr, bool unsigned_val) int Field_newdate::store_time(MYSQL_TIME *ltime,timestamp_type time_type) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; long tmp; int error= 0; if (time_type == MYSQL_TIMESTAMP_DATE || @@ -5983,7 +5983,7 @@ void Field_newdate::sql_type(String &res) const int Field_datetime::store(const char *from,uint len,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME time_tmp; int error; ulonglong tmp= 0; @@ -6036,7 +6036,7 @@ int Field_datetime::store(double nr) int Field_datetime::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; MYSQL_TIME not_used; int error; longlong initial_nr= nr; @@ -6074,7 +6074,7 @@ int Field_datetime::store(longlong nr, bool unsigned_val) int Field_datetime::store_time(MYSQL_TIME *ltime,timestamp_type time_type) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; longlong tmp; int error= 0; /* @@ -6377,7 +6377,7 @@ Field_longstr::report_if_important_data(const char *ptr, const char *end, int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; const char *well_formed_error_pos; const char *cannot_convert_error_pos; @@ -6418,7 +6418,7 @@ int Field_string::store(const char *from,uint length,CHARSET_INFO *cs) int Field_str::store(double nr) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; char buff[DOUBLE_TO_STRING_CONVERSION_BUFFER_SIZE]; uint length; uint local_char_length= field_length / charset()->mbmaxlen; @@ -7022,7 +7022,7 @@ int Field_varstring::do_save_field_metadata(uchar *metadata_ptr) int Field_varstring::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length; const char *well_formed_error_pos; const char *cannot_convert_error_pos; @@ -7686,7 +7686,7 @@ void Field_blob::put_length(uchar *pos, uint32 length) int Field_blob::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; uint copy_length, new_length; const char *well_formed_error_pos; const char *cannot_convert_error_pos; @@ -8447,7 +8447,7 @@ void Field_enum::store_type(ulonglong value) int Field_enum::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int err= 0; uint32 not_used; char buff[STRING_BUFFER_USUAL_SIZE]; @@ -8496,7 +8496,7 @@ int Field_enum::store(double nr) int Field_enum::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; if ((ulonglong) nr > typelib->count || nr == 0) { @@ -8665,7 +8665,7 @@ Field *Field_enum::new_field(MEM_ROOT *root, struct st_table *new_table, int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; bool got_warning= 0; int err= 0; char *not_used; @@ -8705,7 +8705,7 @@ int Field_set::store(const char *from,uint length,CHARSET_INFO *cs) int Field_set::store(longlong nr, bool unsigned_val) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int error= 0; ulonglong max_nr= set_bits(ulonglong, typelib->count); if ((ulonglong) nr > max_nr) @@ -9012,7 +9012,7 @@ uint Field_bit::is_equal(Create_field *new_field) int Field_bit::store(const char *from, uint length, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int delta; for (; length && !*from; from++, length--) // skip left 0's @@ -9423,7 +9423,7 @@ Field_bit_as_char::Field_bit_as_char(uchar *ptr_arg, uint32 len_arg, int Field_bit_as_char::store(const char *from, uint length, CHARSET_INFO *cs) { - ASSERT_COLUMN_MARKED_FOR_WRITE; + ASSERT_COLUMN_MARKED_FOR_WRITE_OR_COMPUTED; int delta; uchar bits= (uchar) (field_length & 7); @@ -9531,6 +9531,8 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg, ((decimals_arg & FIELDFLAG_MAX_DEC) << FIELDFLAG_DEC_SHIFT) | (maybe_null ? FIELDFLAG_MAYBE_NULL : 0) | (is_unsigned ? 0 : FIELDFLAG_DECIMAL)); + vcol_info= 0; + stored_in_db= TRUE; } @@ -9550,6 +9552,7 @@ void Create_field::init_for_tmp_table(enum_field_types sql_type_arg, @param fld_interval_list Interval list (if any) @param fld_charset Field charset @param fld_geom_type Field geometry type (if any) + @param fld_vcol_info Virtual column data @retval FALSE on success @@ -9562,17 +9565,20 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, uint fld_type_modifier, Item *fld_default_value, Item *fld_on_update_value, LEX_STRING *fld_comment, char *fld_change, List<String> *fld_interval_list, - CHARSET_INFO *fld_charset, uint fld_geom_type) + CHARSET_INFO *fld_charset, uint fld_geom_type, + Virtual_column_info *fld_vcol_info, + engine_option_value *create_opt) { uint sign_len, allowed_type_modifier= 0; ulong max_field_charlength= MAX_FIELD_CHARLENGTH; DBUG_ENTER("Create_field::init()"); - + field= 0; field_name= fld_name; def= fld_default_value; flags= fld_type_modifier; + option_list= create_opt; unireg_check= (fld_type_modifier & AUTO_INCREMENT_FLAG ? Field::NEXT_NUMBER : Field::NONE); decimals= fld_decimals ? (uint)atoi(fld_decimals) : 0; @@ -9593,6 +9599,33 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, interval_list.empty(); comment= *fld_comment; + vcol_info= fld_vcol_info; + stored_in_db= TRUE; + + /* Initialize data for a computed field */ + if ((uchar)fld_type == (uchar)MYSQL_TYPE_VIRTUAL) + { + DBUG_ASSERT(vcol_info && vcol_info->expr_item); + stored_in_db= vcol_info->is_stored(); + /* + Walk through the Item tree checking if all items are valid + to be part of the virtual column + */ + if (vcol_info->expr_item->walk(&Item::check_vcol_func_processor, 0, NULL)) + { + my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name); + DBUG_RETURN(TRUE); + } + + /* + Make a field created for the real type. + Note that regular and computed fields differ from each other only by + Field::vcol_info. It is is always NULL for a column that is not + computed. + */ + sql_type= fld_type= vcol_info->get_real_type(); + } + /* Set NO_DEFAULT_VALUE_FLAG if this field doesn't have a default value and it is NOT NULL, not an AUTO_INCREMENT field and not a TIMESTAMP. @@ -9882,7 +9915,7 @@ bool Create_field::init(THD *thd, char *fld_name, enum_field_types fld_type, } case MYSQL_TYPE_DECIMAL: DBUG_ASSERT(0); /* Was obsolete */ - } + } /* Remember the value of length */ char_length= length; @@ -9992,7 +10025,6 @@ uint pack_length_to_packflag(uint type) return 0; // This shouldn't happen } - Field *make_field(TABLE_SHARE *share, uchar *ptr, uint32 field_length, uchar *null_pos, uchar null_bit, uint pack_flag, @@ -10184,6 +10216,10 @@ Create_field::Create_field(Field *old_field,Field *orig_field) charset= old_field->charset(); // May be NULL ptr comment= old_field->comment; decimals= old_field->decimals(); + vcol_info= old_field->vcol_info; + stored_in_db= old_field->stored_in_db; + option_list= old_field->option_list; + option_struct= old_field->option_struct; /* Fix if the original table had 4 byte pointer blobs */ if (flags & BLOB_FLAG) @@ -10258,6 +10294,19 @@ Create_field::Create_field(Field *old_field,Field *orig_field) /** + Makes a clone of this object for ALTER/CREATE TABLE + + @param mem_root MEM_ROOT where to clone the field +*/ + +Create_field *Create_field::clone(MEM_ROOT *mem_root) const +{ + Create_field *res= new (mem_root) Create_field(*this); + return res; +} + + +/** maximum possible display length for blob. @return diff --git a/sql/field.h b/sql/field.h index 04d534114e6..d74e2d163ee 100644 --- a/sql/field.h +++ b/sql/field.h @@ -45,6 +45,80 @@ inline uint get_set_pack_length(int elements) return len > 4 ? 8 : len; } +/* + Virtual_column_info is the class to contain additional + characteristics that is specific for a virtual/computed + field such as: + - the defining expression that is evaluated to compute the value + of the field + - whether the field is to be stored in the database + - whether the field is used in a partitioning expression +*/ + +class Virtual_column_info: public Sql_alloc +{ +private: + /* + The following data is only updated by the parser and read + when a Create_field object is created/initialized. + */ + enum_field_types field_type; /* Real field type*/ + /* Flag indicating that the field is physically stored in the database */ + my_bool stored_in_db; + /* Flag indicating that the field used in a partitioning expression */ + my_bool in_partitioning_expr; + +public: + /* The expression to compute the value of the virtual column */ + Item *expr_item; + /* Text representation of the defining expression */ + LEX_STRING expr_str; + /* + The list of items created when the defining expression for the virtual + column is being parsed and validated. These items are freed in the closefrm + function when the table containing this virtual column is removed from + the TABLE cache. + TODO. Items for all different virtual columns of a table should be put into + one list attached to the TABLE structure. + */ + Item *item_free_list; + + Virtual_column_info() + : field_type((enum enum_field_types)MYSQL_TYPE_VIRTUAL), + stored_in_db(FALSE), in_partitioning_expr(FALSE), + expr_item(NULL), item_free_list(NULL) + { + expr_str.str= NULL; + expr_str.length= 0; + }; + ~Virtual_column_info() {} + enum_field_types get_real_type() + { + return field_type; + } + void set_field_type(enum_field_types fld_type) + { + /* Calling this function can only be done once. */ + field_type= fld_type; + } + bool is_stored() + { + return stored_in_db; + } + void set_stored_in_db_flag(bool stored) + { + stored_in_db= stored; + } + bool is_in_partitioning_expr() + { + return in_partitioning_expr; + } + void mark_as_in_partitioning_expr() + { + in_partitioning_expr= TRUE; + } +}; + class Field { Field(const Item &); /* Prevent use of these */ @@ -57,20 +131,23 @@ public: uchar *ptr; // Position to field in record uchar *null_ptr; // Byte where null_bit is /* - Note that you can use table->in_use as replacement for current_thd member + Note that you can use table->in_use as replacement for current_thd member only inside of val_*() and store() members (e.g. you can't use it in cons) */ struct st_table *table; // Pointer for table struct st_table *orig_table; // Pointer to original table const char **table_name, *field_name; + /** reference to the list of options or NULL */ + engine_option_value *option_list; + void *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; key_map part_of_sortkey; - /* - We use three additional unireg types for TIMESTAMP to overcome limitation - of current binary format of .frm file. We'd like to be able to support - NOW() as default and on update value for such fields but unable to hold + /* + We use three additional unireg types for TIMESTAMP to overcome limitation + of current binary format of .frm file. We'd like to be able to support + NOW() as default and on update value for such fields but unable to hold this info anywhere except unireg_check field. This issue will be resolved in more clean way with transition to new text based .frm format. See also comment for Field_timestamp::Field_timestamp(). @@ -103,6 +180,19 @@ public: */ bool is_created_from_null_item; + /* + This is additional data provided for any computed(virtual) field. + In particular it includes a pointer to the item by which this field + can be computed from other fields. + */ + Virtual_column_info *vcol_info; + /* + Flag indicating that the field is physically stored in tables + rather than just computed from other fields. + As of now, FALSE can be set only for computed virtual columns. + */ + bool stored_in_db; + Field(uchar *ptr_arg,uint32 length_arg,uchar *null_ptr_arg, uchar null_bit_arg, utype unireg_check_arg, const char *field_name_arg); @@ -2058,14 +2148,31 @@ public: CHARSET_INFO *charset; Field::geometry_type geom_type; Field *field; // For alter table + engine_option_value *option_list; + /** structure with parsed options (for comparing fields in ALTER TABLE) */ + void *option_struct; uint8 row,col,sc_length,interval_id; // For rea_create_table uint offset,pack_flag; - Create_field() :after(0) {} + + /* + This is additinal data provided for any computed(virtual) field. + In particular it includes a pointer to the item by which this field + can be computed from other fields. + */ + Virtual_column_info *vcol_info; + /* + Flag indicating that the field is physically stored in tables + rather than just computed from other fields. + As of now, FALSE can be set only for computed virtual columns. + */ + bool stored_in_db; + + Create_field() :after(0), option_list(NULL), option_struct(NULL) + {} Create_field(Field *field, Field *orig_field); /* Used to make a clone of this object for ALTER/CREATE TABLE */ - Create_field *clone(MEM_ROOT *mem_root) const - { return new (mem_root) Create_field(*this); } + Create_field *clone(MEM_ROOT *mem_root) const; void create_length_to_internal_length(void); /* Init for a tmp table field. To be extended if need be. */ @@ -2077,7 +2184,8 @@ public: char *decimals, uint type_modifier, Item *default_value, Item *on_update_value, LEX_STRING *comment, char *change, List<String> *interval_list, CHARSET_INFO *cs, - uint uint_geom_type); + uint uint_geom_type, Virtual_column_info *vcol_info, + engine_option_value *option_list); bool field_flags_are_binary() { diff --git a/sql/filesort.cc b/sql/filesort.cc index 76777d78085..357f935c0eb 100644 --- a/sql/filesort.cc +++ b/sql/filesort.cc @@ -567,6 +567,8 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, { if ((error= select->quick->get_next())) break; + if (!error) + update_virtual_fields(sort_form); file->position(sort_form->record[0]); DBUG_EXECUTE_IF("debug_filesort", dbug_print_record(sort_form, TRUE);); } @@ -579,11 +581,13 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select, error= my_errno ? my_errno : -1; /* Abort */ break; } - error=file->rnd_pos(sort_form->record[0],next_pos); + error=file->ha_rnd_pos(sort_form->record[0],next_pos); } else { - error=file->rnd_next(sort_form->record[0]); + error=file->ha_rnd_next(sort_form->record[0]); + if (!error) + update_virtual_fields(sort_form); if (!flag) { my_store_ptr(ref_pos,ref_length,record); // Position to row diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc index 35ca65683d6..9176faa3078 100644 --- a/sql/ha_ndbcluster.cc +++ b/sql/ha_ndbcluster.cc @@ -9185,7 +9185,7 @@ pthread_handler_t ndb_util_thread_func(void *arg __attribute__((unused))) thd->client_capabilities = 0; my_net_init(&thd->net, 0); thd->main_security_ctx.master_access= ~0; - thd->main_security_ctx.priv_user = 0; + thd->main_security_ctx.priv_user[0] = 0; CHARSET_INFO *charset_connection; charset_connection= get_charset_by_csname("utf8", @@ -10564,6 +10564,23 @@ mysql_declare_plugin(ndbcluster) NULL /* config options */ } mysql_declare_plugin_end; +maria_declare_plugin(ndbcluster) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &ndbcluster_storage_engine, + ndbcluster_hton_name, + "MySQL AB", + "Clustered, fault-tolerant tables", + PLUGIN_LICENSE_GPL, + ndbcluster_init, /* Plugin Init */ + NULL, /* Plugin Deinit */ + 0x0100 /* 1.0 */, + ndb_status_variables_export,/* status variables */ + NULL, /* system variables */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_GAMMA /* maturity */ +} +maria_declare_plugin_end; #else int Sun_ar_require_a_symbol_here= 0; diff --git a/sql/ha_ndbcluster_binlog.cc b/sql/ha_ndbcluster_binlog.cc index b24b17106c6..77851119e34 100644 --- a/sql/ha_ndbcluster_binlog.cc +++ b/sql/ha_ndbcluster_binlog.cc @@ -3681,7 +3681,7 @@ pthread_handler_t ndb_binlog_thread_func(void *arg) thd->client_capabilities= 0; my_net_init(&thd->net, 0); thd->main_security_ctx.master_access= ~0; - thd->main_security_ctx.priv_user= 0; + thd->main_security_ctx.priv_user[0]= 0; /* Set up ndb binlog diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc index 22b01fe38fa..c065a35b8e3 100644 --- a/sql/ha_partition.cc +++ b/sql/ha_partition.cc @@ -52,6 +52,7 @@ #endif #include "mysql_priv.h" +#include "create_options.h" #ifdef WITH_PARTITION_STORAGE_ENGINE #include "ha_partition.h" @@ -1218,7 +1219,9 @@ int ha_partition::prepare_new_partition(TABLE *tbl, DBUG_ENTER("prepare_new_partition"); if ((error= set_up_table_before_create(tbl, part_name, create_info, - 0, p_elem))) + 0, p_elem)) || + parse_engine_table_options(ha_thd(), file->ht, + file->table_share)) goto error_create; if ((error= file->ha_create(part_name, tbl, create_info))) { @@ -1664,7 +1667,7 @@ int ha_partition::copy_partitions(ulonglong * const copied, goto error; while (TRUE) { - if ((result= file->rnd_next(m_rec0))) + if ((result= file->ha_rnd_next(m_rec0))) { if (result == HA_ERR_RECORD_DELETED) continue; //Probably MyISAM @@ -1869,6 +1872,8 @@ uint ha_partition::del_ren_cre_table(const char *from, { if ((error= set_up_table_before_create(table_arg, from_buff, create_info, i, NULL)) || + parse_engine_table_options(ha_thd(), (*file)->ht, + (*file)->table_share) || ((error= (*file)->ha_create(from_buff, table_arg, create_info)))) goto create_error; } @@ -2465,7 +2470,7 @@ int ha_partition::open(const char *name, int mode, uint test_if_locked) DBUG_RETURN(1); m_start_key.length= 0; m_rec0= table->record[0]; - m_rec_length= table_share->reclength; + m_rec_length= table_share->stored_rec_length; alloc_len= m_tot_parts * (m_rec_length + PARTITION_BYTES_IN_POS); alloc_len+= table_share->max_key_length; if (!m_ordered_rec_buffer) @@ -3638,7 +3643,7 @@ int ha_partition::rnd_next(uchar *buf) while (TRUE) { - result= file->rnd_next(buf); + result= file->ha_rnd_next(buf); if (!result) { m_last_part= part_id; @@ -4488,8 +4493,8 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same) } else if (is_next_same) { - if (!(error= file->index_next_same(buf, m_start_key.key, - m_start_key.length))) + if (!(error= file->ha_index_next_same(buf, m_start_key.key, + m_start_key.length))) { m_last_part= m_part_spec.start_part; DBUG_RETURN(0); @@ -4497,7 +4502,7 @@ int ha_partition::handle_unordered_next(uchar *buf, bool is_next_same) } else { - if (!(error= file->index_next(buf))) + if (!(error= file->ha_index_next(buf))) { m_last_part= m_part_spec.start_part; DBUG_RETURN(0); // Row was in range @@ -4552,13 +4557,26 @@ int ha_partition::handle_unordered_scan_next_partition(uchar * buf) break; case partition_index_read: DBUG_PRINT("info", ("index_read on partition %d", i)); - error= file->index_read_map(buf, m_start_key.key, - m_start_key.keypart_map, - m_start_key.flag); + error= file->ha_index_read_map(buf, m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag); break; case partition_index_first: DBUG_PRINT("info", ("index_first on partition %d", i)); - error= file->index_first(buf); + /* + MyISAM engine can fail if we call index_first() when indexes disabled + that happens if the table is empty. + Here we use file->stats.records instead of file->records() because + file->records() is supposed to return an EXACT count, and it can be + possibly slow. We don't need an exact number, an approximate one- from + the last ::info() call - is sufficient. + */ + if (file->stats.records == 0) + { + error= HA_ERR_END_OF_FILE; + break; + } + error= file->ha_index_first(buf); break; case partition_index_first_unordered: /* @@ -4639,23 +4657,49 @@ int ha_partition::handle_ordered_index_scan(uchar *buf, bool reverse_order) switch (m_index_scan_type) { case partition_index_read: - error= file->index_read_map(rec_buf_ptr, - m_start_key.key, - m_start_key.keypart_map, - m_start_key.flag); + error= file->ha_index_read_map(rec_buf_ptr, + m_start_key.key, + m_start_key.keypart_map, + m_start_key.flag); break; case partition_index_first: - error= file->index_first(rec_buf_ptr); + /* + MyISAM engine can fail if we call index_first() when indexes disabled + that happens if the table is empty. + Here we use file->stats.records instead of file->records() because + file->records() is supposed to return an EXACT count, and it can be + possibly slow. We don't need an exact number, an approximate one- from + the last ::info() call - is sufficient. + */ + if (file->stats.records == 0) + { + error= HA_ERR_END_OF_FILE; + break; + } + error= file->ha_index_first(rec_buf_ptr); reverse_order= FALSE; break; case partition_index_last: - error= file->index_last(rec_buf_ptr); + /* + MyISAM engine can fail if we call index_last() when indexes disabled + that happens if the table is empty. + Here we use file->stats.records instead of file->records() because + file->records() is supposed to return an EXACT count, and it can be + possibly slow. We don't need an exact number, an approximate one- from + the last ::info() call - is sufficient. + */ + if (file->stats.records == 0) + { + error= HA_ERR_END_OF_FILE; + break; + } + error= file->ha_index_last(rec_buf_ptr); reverse_order= TRUE; break; case partition_index_read_last: - error= file->index_read_last_map(rec_buf_ptr, - m_start_key.key, - m_start_key.keypart_map); + error= file->ha_index_read_last_map(rec_buf_ptr, + m_start_key.key, + m_start_key.keypart_map); reverse_order= TRUE; break; case partition_read_range: @@ -4757,10 +4801,10 @@ int ha_partition::handle_ordered_next(uchar *buf, bool is_next_same) memcpy(rec_buf(part_id), table->record[0], m_rec_length); } else if (!is_next_same) - error= file->index_next(rec_buf(part_id)); + error= file->ha_index_next(rec_buf(part_id)); else - error= file->index_next_same(rec_buf(part_id), m_start_key.key, - m_start_key.length); + error= file->ha_index_next_same(rec_buf(part_id), m_start_key.key, + m_start_key.length); if (error) { if (error == HA_ERR_END_OF_FILE) @@ -4805,7 +4849,7 @@ int ha_partition::handle_ordered_prev(uchar *buf) handler *file= m_file[part_id]; DBUG_ENTER("ha_partition::handle_ordered_prev"); - if ((error= file->index_prev(rec_buf(part_id)))) + if ((error= file->ha_index_prev(rec_buf(part_id)))) { if (error == HA_ERR_END_OF_FILE) { @@ -6676,5 +6720,22 @@ mysql_declare_plugin(partition) NULL /* config options */ } mysql_declare_plugin_end; +maria_declare_plugin(partition) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &partition_storage_engine, + "partition", + "Mikael Ronstrom, MySQL AB", + "Partition Storage Engine Helper", + PLUGIN_LICENSE_GPL, + partition_initialize, /* Plugin Init */ + NULL, /* Plugin Deinit */ + 0x0100, /* 1.0 */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_STABLE /* maturity */ +} +maria_declare_plugin_end; #endif diff --git a/sql/ha_partition.h b/sql/ha_partition.h index f37587f2b71..98af55bc038 100644 --- a/sql/ha_partition.h +++ b/sql/ha_partition.h @@ -250,6 +250,7 @@ public: DBUG_RETURN(0); } virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share); + bool check_if_supported_virtual_columns(void) { return TRUE;} virtual bool check_if_incompatible_data(HA_CREATE_INFO *create_info, uint table_changes); private: diff --git a/sql/handler.cc b/sql/handler.cc index b817673ed23..90884daaae4 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -24,6 +24,7 @@ #endif #include "mysql_priv.h" +#include "create_options.h" #include "rpl_filter.h" #include <myisampack.h> #include "myisam.h" @@ -1231,6 +1232,7 @@ int ha_commit_one_phase(THD *thd, bool all) my_error(ER_ERROR_DURING_COMMIT, MYF(0), err); error=1; } + /* Should this be done only if is_real_trans is set ? */ status_var_increment(thd->status_var.ha_commit_count); ha_info_next= ha_info->next(); ha_info->reset(); /* keep it conveniently zero-filled */ @@ -2108,6 +2110,8 @@ int handler::ha_open(TABLE *table_arg, const char *name, int mode, dup_ref=ref+ALIGN_SIZE(ref_length); cached_table_flags= table_flags(); } + rows_read= rows_changed= 0; + memset(index_rows_read, 0, sizeof(index_rows_read)); DBUG_RETURN(error); } @@ -2529,9 +2533,10 @@ void handler::get_auto_increment(ulonglong offset, ulonglong increment, key_copy(key, table->record[0], table->key_info + table->s->next_number_index, table->s->next_number_key_offset); - error= index_read_map(table->record[1], key, - make_prev_keypart_map(table->s->next_number_keypart), - HA_READ_PREFIX_LAST); + error= ha_index_read_map(table->record[1], key, + make_prev_keypart_map(table->s-> + next_number_keypart), + HA_READ_PREFIX_LAST); /* MySQL needs to call us for next row: assume we are inserting ("a",null) here, we return 3, and next this statement will want to insert @@ -3106,11 +3111,14 @@ int handler::ha_check(THD *thd, HA_CHECK_OPT *check_opt) if it is started. */ -inline void -handler::mark_trx_read_write() +handler::mark_trx_read_write_part2() { Ha_trx_info *ha_info= &ha_thd()->ha_data[ht->slot].ha_info[0]; + + /* Don't call this function again for this statement */ + mark_trx_done= TRUE; + /* When a storage engine method is called, the transaction must have been started, unless it's a DDL call, for which the @@ -3561,6 +3569,122 @@ void handler::get_dynamic_partition_info(PARTITION_INFO *stat_info, } +/* + Updates the global table stats with the TABLE this handler represents +*/ + +void handler::update_global_table_stats() +{ + TABLE_STATS * table_stats; + + status_var_add(table->in_use->status_var.rows_read, rows_read); + + if (!table->in_use->userstat_running) + { + rows_read= rows_changed= 0; + return; + } + + if (rows_read + rows_changed == 0) + return; // Nothing to update. + + DBUG_ASSERT(table->s && table->s->table_cache_key.str); + + pthread_mutex_lock(&LOCK_global_table_stats); + /* Gets the global table stats, creating one if necessary. */ + if (!(table_stats= (TABLE_STATS*) + hash_search(&global_table_stats, + (uchar*) table->s->table_cache_key.str, + table->s->table_cache_key.length))) + { + if (!(table_stats = ((TABLE_STATS*) + my_malloc(sizeof(TABLE_STATS), + MYF(MY_WME | MY_ZEROFILL))))) + { + /* Out of memory error already given */ + goto end; + } + memcpy(table_stats->table, table->s->table_cache_key.str, + table->s->table_cache_key.length); + table_stats->table_name_length= table->s->table_cache_key.length; + table_stats->engine_type= ht->db_type; + /* No need to set variables to 0, as we use MY_ZEROFILL above */ + + if (my_hash_insert(&global_table_stats, (uchar*) table_stats)) + { + /* Out of memory error is already given */ + my_free(table_stats, 0); + goto end; + } + } + // Updates the global table stats. + table_stats->rows_read+= rows_read; + table_stats->rows_changed+= rows_changed; + table_stats->rows_changed_x_indexes+= (rows_changed * + (table->s->keys ? table->s->keys : + 1)); + rows_read= rows_changed= 0; +end: + pthread_mutex_unlock(&LOCK_global_table_stats); +} + + +/* + Updates the global index stats with this handler's accumulated index reads. +*/ + +void handler::update_global_index_stats() +{ + DBUG_ASSERT(table->s); + + if (!table->in_use->userstat_running) + { + /* Reset all index read values */ + bzero(index_rows_read, sizeof(index_rows_read[0]) * table->s->keys); + return; + } + + for (uint index = 0; index < table->s->keys; index++) + { + if (index_rows_read[index]) + { + INDEX_STATS* index_stats; + uint key_length; + KEY *key_info = &table->key_info[index]; // Rows were read using this + + DBUG_ASSERT(key_info->cache_name); + if (!key_info->cache_name) + continue; + key_length= table->s->table_cache_key.length + key_info->name_length + 1; + pthread_mutex_lock(&LOCK_global_index_stats); + // Gets the global index stats, creating one if necessary. + if (!(index_stats= (INDEX_STATS*) hash_search(&global_index_stats, + key_info->cache_name, + key_length))) + { + if (!(index_stats = ((INDEX_STATS*) + my_malloc(sizeof(INDEX_STATS), + MYF(MY_WME | MY_ZEROFILL))))) + goto end; // Error is already given + + memcpy(index_stats->index, key_info->cache_name, key_length); + index_stats->index_name_length= key_length; + if (my_hash_insert(&global_index_stats, (uchar*) index_stats)) + { + my_free(index_stats, 0); + goto end; + } + } + /* Updates the global index stats. */ + index_stats->rows_read+= index_rows_read[index]; + index_rows_read[index]= 0; +end: + pthread_mutex_unlock(&LOCK_global_index_stats); + } + } +} + + /**************************************************************************** ** Some general functions that isn't in the handler class ****************************************************************************/ @@ -3596,7 +3720,11 @@ int ha_create_table(THD *thd, const char *path, name= get_canonical_filename(table.file, share.path.str, name_buff); + if (parse_engine_table_options(thd, table.file->ht, &share)) + goto err; + error= table.file->ha_create(name, &table, create_info); + VOID(closefrm(&table, 0)); if (error) { @@ -3703,11 +3831,13 @@ int ha_init_key_cache(const char *name, KEY_CACHE *key_cache) uint tmp_block_size= (uint) key_cache->param_block_size; uint division_limit= key_cache->param_division_limit; uint age_threshold= key_cache->param_age_threshold; + uint partitions= key_cache->param_partitions; pthread_mutex_unlock(&LOCK_global_system_variables); DBUG_RETURN(!init_key_cache(key_cache, tmp_block_size, tmp_buff_size, - division_limit, age_threshold)); + division_limit, age_threshold, + partitions)); } DBUG_RETURN(0); } @@ -3737,10 +3867,12 @@ int ha_resize_key_cache(KEY_CACHE *key_cache) /** - Change parameters for key cache (like size) + Change parameters for key cache (like division_limit) */ int ha_change_key_cache_param(KEY_CACHE *key_cache) { + DBUG_ENTER("ha_change_key_cache_param"); + if (key_cache->key_cache_inited) { pthread_mutex_lock(&LOCK_global_system_variables); @@ -3749,9 +3881,35 @@ int ha_change_key_cache_param(KEY_CACHE *key_cache) pthread_mutex_unlock(&LOCK_global_system_variables); change_key_cache_param(key_cache, division_limit, age_threshold); } - return 0; + DBUG_RETURN(0); +} + + +/** + Repartition key cache +*/ +int ha_repartition_key_cache(KEY_CACHE *key_cache) +{ + DBUG_ENTER("ha_repartition_key_cache"); + + if (key_cache->key_cache_inited) + { + pthread_mutex_lock(&LOCK_global_system_variables); + size_t tmp_buff_size= (size_t) key_cache->param_buff_size; + long tmp_block_size= (long) key_cache->param_block_size; + uint division_limit= key_cache->param_division_limit; + uint age_threshold= key_cache->param_age_threshold; + uint partitions= key_cache->param_partitions; + pthread_mutex_unlock(&LOCK_global_system_variables); + DBUG_RETURN(!repartition_key_cache(key_cache, tmp_block_size, + tmp_buff_size, + division_limit, age_threshold, + partitions)); + } + DBUG_RETURN(0); } + /** Free memory allocated by a key cache. */ @@ -4219,17 +4377,16 @@ int handler::read_range_first(const key_range *start_key, range_key_part= table->key_info[active_index].key_part; if (!start_key) // Read first record - result= index_first(table->record[0]); + result= ha_index_first(table->record[0]); else - result= index_read_map(table->record[0], - start_key->key, - start_key->keypart_map, - start_key->flag); + result= ha_index_read_map(table->record[0], + start_key->key, + start_key->keypart_map, + start_key->flag); if (result) DBUG_RETURN((result == HA_ERR_KEY_NOT_FOUND) ? HA_ERR_END_OF_FILE : result); - DBUG_RETURN (compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE); } @@ -4255,11 +4412,11 @@ int handler::read_range_next() if (eq_range) { /* We trust that index_next_same always gives a row in range */ - DBUG_RETURN(index_next_same(table->record[0], - end_range->key, - end_range->length)); + DBUG_RETURN(ha_index_next_same(table->record[0], + end_range->key, + end_range->length)); } - result= index_next(table->record[0]); + result= ha_index_next(table->record[0]); if (result) DBUG_RETURN(result); DBUG_RETURN(compare_key(end_range) <= 0 ? 0 : HA_ERR_END_OF_FILE); @@ -4643,6 +4800,7 @@ int handler::ha_write_row(uchar *buf) if (unlikely(error= write_row(buf))) DBUG_RETURN(error); + rows_changed++; if (unlikely(error= binlog_log_row(table, 0, buf, log_func))) DBUG_RETURN(error); /* purecov: inspected */ DBUG_RETURN(0); @@ -4664,6 +4822,7 @@ int handler::ha_update_row(const uchar *old_data, uchar *new_data) if (unlikely(error= update_row(old_data, new_data))) return error; + rows_changed++; if (unlikely(error= binlog_log_row(table, old_data, new_data, log_func))) return error; return 0; @@ -4678,6 +4837,7 @@ int handler::ha_delete_row(const uchar *buf) if (unlikely(error= delete_row(buf))) return error; + rows_changed++; if (unlikely(error= binlog_log_row(table, buf, 0, log_func))) return error; return 0; diff --git a/sql/handler.h b/sql/handler.h index d03264a23db..ef69c18e846 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -16,6 +16,9 @@ /* Definitions for parameters to do with handler-routines */ +#ifndef SQL_HANDLER_INCLUDED +#define SQL_HANDLER_INCLUDED + #ifdef USE_PRAGMA_INTERFACE #pragma interface /* gcc class implementation */ #endif @@ -30,6 +33,10 @@ #define USING_TRANSACTIONS +#if MAX_KEY > 128 +#error MAX_KEY is too large. Values up to 128 are supported. +#endif + // the following is for checking tables #define HA_ADMIN_ALREADY_DONE 1 @@ -545,6 +552,103 @@ struct handler_log_file_data { enum log_status status; }; +/* + Definitions for engine-specific table/field/index options in the CREATE TABLE. + + Options are declared with HA_*OPTION_* macros (HA_TOPTION_ULL, HA_FOPTION_ENUM, + HA_KOPTION_STRING, etc). + + Every macros takes the option name, and the name of the underlying field of + the appropriate C structure. The "appropriate C structure" is + ha_table_option_struct for table level options, + ha_field_option_struct for field level options, + ha_key_option_struct for key level options. The engine either + defines a structure of this name, or uses #define's to map + these "appropriate" names to the actual structure type name. + + ULL options use a ulonglong as the backing store. + HA_*OPTION_ULL() takes the option name, the structure field name, + the default value for the option, min, max, and blk_siz values. + + STRING options use a char* as a backing store. + HA_*OPTION_STRING takes the option name and the structure field name. + The default value will be 0. + + ENUM options use a uint as a backing store (not enum!!!). + HA_*OPTION_ENUM takes the option name, the structure field name, + the default value for the option as a number, and a string with the + permitted values for this enum - one string with comma separated values, + for example: "gzip,bzip2,lzma" + + BOOL options use a bool as a backing store. + HA_*OPTION_BOOL takes the option name, the structure field name, + and the default value for the option. + From the SQL, BOOL options accept YES/NO, ON/OFF, and 1/0. + + The name of the option is limited to 255 bytes, + the value (for string options) - to the 32767 bytes. + + See ha_example.cc for an example. +*/ +enum ha_option_type { HA_OPTION_TYPE_ULL, /* unsigned long long */ + HA_OPTION_TYPE_STRING, /* char * */ + HA_OPTION_TYPE_ENUM, /* uint */ + HA_OPTION_TYPE_BOOL}; /* bool */ + +#define HA_xOPTION_ULL(name, struc, field, def, min, max, blk_siz) \ + { HA_OPTION_TYPE_ULL, name, sizeof(name)-1, \ + offsetof(struc, field), def, min, max, blk_siz, 0 } +#define HA_xOPTION_STRING(name, struc, field) \ + { HA_OPTION_TYPE_STRING, name, sizeof(name)-1, \ + offsetof(struc, field), 0, 0, 0, 0, 0 } +#define HA_xOPTION_ENUM(name, struc, field, values, def) \ + { HA_OPTION_TYPE_ENUM, name, sizeof(name)-1, \ + offsetof(struc, field), def, 0, \ + sizeof(values)-1, 0, values } +#define HA_xOPTION_BOOL(name, struc, field, def) \ + { HA_OPTION_TYPE_BOOL, name, sizeof(name)-1, \ + offsetof(struc, field), def, 0, 1, 0, 0 } +#define HA_xOPTION_END { HA_OPTION_TYPE_ULL, 0, 0, 0, 0, 0, 0, 0, 0 } + +#define HA_TOPTION_ULL(name, field, def, min, max, blk_siz) \ + HA_xOPTION_ULL(name, ha_table_option_struct, field, def, min, max, blk_siz) +#define HA_TOPTION_STRING(name, field) \ + HA_xOPTION_STRING(name, ha_table_option_struct, field) +#define HA_TOPTION_ENUM(name, field, values, def) \ + HA_xOPTION_ENUM(name, ha_table_option_struct, field, values, def) +#define HA_TOPTION_BOOL(name, field, def) \ + HA_xOPTION_BOOL(name, ha_table_option_struct, field, def) +#define HA_TOPTION_END HA_xOPTION_END + +#define HA_FOPTION_ULL(name, field, def, min, max, blk_siz) \ + HA_xOPTION_ULL(name, ha_field_option_struct, field, def, min, max, blk_siz) +#define HA_FOPTION_STRING(name, field) \ + HA_xOPTION_STRING(name, ha_field_option_struct, field) +#define HA_FOPTION_ENUM(name, field, values, def) \ + HA_xOPTION_ENUM(name, ha_field_option_struct, field, values, def) +#define HA_FOPTION_BOOL(name, field, def) \ + HA_xOPTION_BOOL(name, ha_field_option_struct, field, def) +#define HA_FOPTION_END HA_xOPTION_END + +#define HA_KOPTION_ULL(name, field, def, min, max, blk_siz) \ + HA_xOPTION_ULL(name, ha_key_option_struct, field, def, min, max, blk_siz) +#define HA_KOPTION_STRING(name, field) \ + HA_xOPTION_STRING(name, ha_key_option_struct, field) +#define HA_KOPTION_ENUM(name, field, values, def) \ + HA_xOPTION_ENUM(name, ha_key_option_struct, field, values, def) +#define HA_KOPTION_BOOL(name, field, values, def) \ + HA_xOPTION_BOOL(name, ha_key_option_struct, field, values, def) +#define HA_KOPTION_END HA_xOPTION_END + +typedef struct st_ha_create_table_option { + enum ha_option_type type; + const char *name; + size_t name_length; + ptrdiff_t offset; + ulonglong def_value; + ulonglong min_value, max_value, block_size; + const char *values; +} ha_create_table_option; enum handler_iterator_type { @@ -605,8 +709,9 @@ struct handlerton SHOW_COMP_OPTION state; /* - Historical number used for frm file to determine the correct storage engine. - This is going away and new engines will just use "name" for this. + Historical number used for frm file to determine the correct + storage engine. This is going away and new engines will just use + "name" for this. */ enum legacy_db_type db_type; /* @@ -716,7 +821,13 @@ struct handlerton int (*table_exists_in_engine)(handlerton *hton, THD* thd, const char *db, const char *name); uint32 license; /* Flag for Engine License */ - void *data; /* Location for engines to keep personal structures */ + /* + Optional clauses in the CREATE/ALTER TABLE + */ + ha_create_table_option *table_options; // table level options + ha_create_table_option *field_options; // these are specified per field + ha_create_table_option *index_options; // these are specified per index + }; @@ -940,11 +1051,16 @@ typedef struct st_ha_create_information uint extra_size; /* length of extra data segment */ /** Transactional or not. Unused; reserved for future versions. */ enum ha_choice transactional; - bool table_existed; /* 1 in create if table existed */ - bool frm_only; /* 1 if no ha_create_table() */ - bool varchar; /* 1 if table has a VARCHAR */ - enum ha_storage_media storage_media; /* DEFAULT, DISK or MEMORY */ - enum ha_choice page_checksum; /* If we have page_checksums */ + bool table_existed; ///< 1 in create if table existed + bool frm_only; ///< 1 if no ha_create_table() + bool varchar; ///< 1 if table has a VARCHAR + enum ha_storage_media storage_media; ///< DEFAULT, DISK or MEMORY + enum ha_choice page_checksum; ///< If we have page_checksums + engine_option_value *option_list; ///< list of table create options + /* the following three are only for ALTER TABLE, check_if_incompatible_data() */ + void *option_struct; ///< structure with parsed table options + void **fileds_option_struct; ///< array of field option structures + void **keys_option_struct; ///< array of key option structures } HA_CREATE_INFO; @@ -1129,6 +1245,7 @@ public: enum {NONE=0, INDEX, RND} inited; bool locked; bool implicit_emptied; /* Can be !=0 only if HEAP */ + bool mark_trx_done; const COND *pushed_cond; /** next_insert_id is the next value which should be inserted into the @@ -1151,6 +1268,12 @@ public: Interval returned by get_auto_increment() and being consumed by the inserter. */ + /* Statistics variables */ + ulonglong rows_read; + ulonglong rows_changed; + /* One bigger than needed to avoid to test if key == MAX_KEY */ + ulonglong index_rows_read[MAX_KEY+1]; + Discrete_interval auto_inc_interval_for_cur_row; /** Number of reserved auto-increment intervals. Serves as a heuristic @@ -1166,10 +1289,13 @@ public: ref(0), key_used_on_scan(MAX_KEY), active_index(MAX_KEY), ref_length(sizeof(my_off_t)), ft_handler(0), inited(NONE), - locked(FALSE), implicit_emptied(0), + locked(FALSE), implicit_emptied(FALSE), mark_trx_done(FALSE), pushed_cond(0), next_insert_id(0), insert_id_for_cur_row(0), auto_inc_intervals_count(0) - {} + { + reset_statistics(); + } + virtual ~handler(void) { DBUG_ASSERT(locked == FALSE); @@ -1218,6 +1344,13 @@ public: DBUG_RETURN(rnd_end()); } int ha_reset(); + /* Tell handler (not storage engine) this is start of a new statement */ + void ha_start_of_new_statement() + { + ft_handler= 0; + mark_trx_done= FALSE; + } + /* this is necessary in many places, e.g. in HANDLER command */ int ha_index_or_rnd_end() { @@ -1291,10 +1424,16 @@ public: virtual void print_error(int error, myf errflag); virtual bool get_error_message(int error, String *buf); uint get_dup_key(int error); + void reset_statistics() + { + rows_read= rows_changed= 0; + bzero(index_rows_read, sizeof(index_rows_read)); + } virtual void change_table_ptr(TABLE *table_arg, TABLE_SHARE *share) { table= table_arg; table_share= share; + reset_statistics(); } virtual double scan_time() { return ulonglong2double(stats.data_file_length) / IO_SIZE + 2; } @@ -1403,22 +1542,23 @@ public: } /** @brief - Positions an index cursor to the index specified in the handle. Fetches the - row if available. If the key value is null, begin at the first key of the - index. + Positions an index cursor to the index specified in the + handle. Fetches the row if available. If the key value is null, + begin at the first key of the index. */ +protected: virtual int index_read_map(uchar * buf, const uchar * key, key_part_map keypart_map, enum ha_rkey_function find_flag) { uint key_len= calculate_key_len(table, active_index, key, keypart_map); - return index_read(buf, key, key_len, find_flag); + return index_read(buf, key, key_len, find_flag); } /** @brief - Positions an index cursor to the index specified in the handle. Fetches the - row if available. If the key value is null, begin at the first key of the - index. + Positions an index cursor to the index specified in the + handle. Fetches the row if available. If the key value is null, + begin at the first key of the index. */ virtual int index_read_idx_map(uchar * buf, uint index, const uchar * key, key_part_map keypart_map, @@ -1443,6 +1583,79 @@ public: uint key_len= calculate_key_len(table, active_index, key, keypart_map); return index_read_last(buf, key, key_len); } + inline void update_index_statistics() + { + index_rows_read[active_index]++; + rows_read++; + } +public: + + /* Similar functions like the above, but does statistics counting */ + inline int ha_index_read_map(uchar * buf, const uchar * key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) + { + int error= index_read_map(buf, key, keypart_map, find_flag); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_read_idx_map(uchar * buf, uint index, const uchar * key, + key_part_map keypart_map, + enum ha_rkey_function find_flag) + { + int error= index_read_idx_map(buf, index, key, keypart_map, find_flag); + if (!error) + { + rows_read++; + index_rows_read[index]++; + } + return error; + } + inline int ha_index_next(uchar * buf) + { + int error= index_next(buf); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_prev(uchar * buf) + { + int error= index_prev(buf); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_first(uchar * buf) + { + int error= index_first(buf); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_last(uchar * buf) + { + int error= index_last(buf); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_next_same(uchar *buf, const uchar *key, uint keylen) + { + int error= index_next_same(buf, key, keylen); + if (!error) + update_index_statistics(); + return error; + } + inline int ha_index_read_last_map(uchar * buf, const uchar * key, + key_part_map keypart_map) + { + int error= index_read_last_map(buf, key, keypart_map); + if (!error) + update_index_statistics(); + return error; + } + virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p, KEY_MULTI_RANGE *ranges, uint range_count, bool sorted, HANDLER_BUFFER *buffer); @@ -1456,6 +1669,7 @@ public: void ft_end() { ft_handler=NULL; } virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key) { return NULL; } +private: virtual int ft_read(uchar *buf) { return HA_ERR_WRONG_COMMAND; } virtual int rnd_next(uchar *buf)=0; virtual int rnd_pos(uchar * buf, uchar *pos)=0; @@ -1466,11 +1680,50 @@ public: handlers for random position. */ virtual int rnd_pos_by_record(uchar *record) - { - position(record); - return rnd_pos(record, ref); - } + { + position(record); + return rnd_pos(record, ref); + } virtual int read_first_row(uchar *buf, uint primary_key); +public: + + /* Same as above, but with statistics */ + inline int ha_ft_read(uchar *buf) + { + int error= ft_read(buf); + if (!error) + rows_read++; + return error; + } + inline int ha_rnd_next(uchar *buf) + { + int error= rnd_next(buf); + if (!error) + rows_read++; + return error; + } + inline int ha_rnd_pos(uchar *buf, uchar *pos) + { + int error= rnd_pos(buf, pos); + if (!error) + rows_read++; + return error; + } + inline int ha_rnd_pos_by_record(uchar *buf) + { + int error= rnd_pos_by_record(buf); + if (!error) + rows_read++; + return error; + } + inline int ha_read_first_row(uchar *buf, uint primary_key) + { + int error= read_first_row(buf, primary_key); + if (!error) + rows_read++; + return error; + } + /** The following 3 function is only needed for tables that may be internal temporary tables during joins. @@ -1639,6 +1892,9 @@ public: virtual bool is_crashed() const { return 0; } virtual bool auto_repair() const { return 0; } + void update_global_table_stats(); + void update_global_index_stats(); + #define CHF_CREATE_FLAG 0 #define CHF_DELETE_FLAG 1 #define CHF_RENAME_FLAG 2 @@ -1773,6 +2029,17 @@ public: LEX_STRING *engine_name() { return hton_name(ht); } + /* + @brief + Check whether the engine supports virtual columns + + @retval + FALSE if the engine does not support virtual columns + @retval + TRUE if the engine supports virtual columns + */ + virtual bool check_if_supported_virtual_columns(void) { return FALSE;} + protected: /* Service methods for use by storage engines. */ void ha_statistic_increment(ulong SSV::*offset) const; @@ -1795,8 +2062,13 @@ protected: private: /* Private helpers */ - inline void mark_trx_read_write(); -private: + void mark_trx_read_write_part2(); + inline void mark_trx_read_write() + { + if (!mark_trx_done) + mark_trx_read_write_part2(); + } + /* Low-level primitives for storage engines. These should be overridden by the storage engine class. To call these methods, use @@ -1957,6 +2229,7 @@ private: { return HA_ERR_WRONG_COMMAND; } virtual int rename_partitions(const char *path) { return HA_ERR_WRONG_COMMAND; } + friend class ha_partition; }; @@ -2039,6 +2312,7 @@ int ha_table_exists_in_engine(THD* thd, const char* db, const char* name); extern "C" int ha_init_key_cache(const char *name, KEY_CACHE *key_cache); int ha_resize_key_cache(KEY_CACHE *key_cache); int ha_change_key_cache_param(KEY_CACHE *key_cache); +int ha_repartition_key_cache(KEY_CACHE *key_cache); int ha_change_key_cache(KEY_CACHE *old_key_cache, KEY_CACHE *new_key_cache); int ha_end_key_cache(KEY_CACHE *key_cache); @@ -2092,3 +2366,5 @@ int ha_binlog_end(THD *thd); #define ha_binlog_wait(a) do {} while (0) #define ha_binlog_end(a) do {} while (0) #endif + +#endif diff --git a/sql/item.cc b/sql/item.cc index cf0d6615999..22803269e7b 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -681,9 +681,24 @@ bool Item_field::register_field_in_read_map(uchar *arg) TABLE *table= (TABLE *) arg; if (field->table == table || !table) bitmap_set_bit(field->table->read_set, field->field_index); + if (field->vcol_info && field->vcol_info->expr_item) + return field->vcol_info->expr_item->walk(&Item::register_field_in_read_map, + 1, arg); return 0; } +/* + @brief + Mark field in bitmap supplied as *arg +*/ + +bool Item_field::register_field_in_bitmap(uchar *arg) +{ + MY_BITMAP *bitmap= (MY_BITMAP *) arg; + DBUG_ASSERT(bitmap); + bitmap_set_bit(bitmap, field->field_index); + return 0; +} bool Item::check_cols(uint c) { @@ -4450,6 +4465,21 @@ error: return TRUE; } +/* + @brief + Mark virtual columns as used in a partitioning expression +*/ + +bool Item_field::vcol_in_partition_func_processor(uchar *int_arg) +{ + DBUG_ASSERT(fixed); + if (field->vcol_info) + { + field->vcol_info->mark_as_in_partitioning_expr(); + } + return FALSE; +} + Item *Item_field::safe_charset_converter(CHARSET_INFO *tocs) { diff --git a/sql/item.h b/sql/item.h index 8bcfaa286c7..d86dc4255ab 100644 --- a/sql/item.h +++ b/sql/item.h @@ -18,6 +18,24 @@ #pragma interface /* gcc class implementation */ #endif +inline +bool trace_unsupported_func(const char *where, const char *processor_name) +{ + char buff[64]; + sprintf(buff, "%s::%s", where, processor_name); + DBUG_ENTER(buff); + sprintf(buff, "%s returns TRUE: unsupported function", processor_name); + DBUG_PRINT("info", ("%s", buff)); + DBUG_RETURN(TRUE); +} + +inline +bool trace_unsupported_by_check_vcol_func_processor(const char *where) +{ + return trace_unsupported_func(where, "check_vcol_func_processor"); +} + + class Protocol; struct TABLE_LIST; void item_init(void); /* Init item functions */ @@ -909,6 +927,11 @@ public: virtual bool enumerate_field_refs_processor(uchar *arg) { return 0; } virtual bool mark_as_eliminated_processor(uchar *arg) { return 0; } /* + The next function differs from the previous one that a bitmap to be updated + is passed as uchar *arg. + */ + virtual bool register_field_in_bitmap(uchar *arg) { return 0; } + /* Check if a partition function is allowed SYNOPSIS check_partition_func_processor() @@ -960,11 +983,43 @@ public: fields. */ virtual bool check_partition_func_processor(uchar *bool_arg) { return TRUE;} + /* + @brief + Processor used to mark virtual columns used in partitioning expression + + @param + arg always ignored + + @retval + FALSE always + */ + virtual bool vcol_in_partition_func_processor(uchar *arg) + { + return FALSE; + } + virtual bool subst_argument_checker(uchar **arg) - { + { if (*arg) - *arg= NULL; - return TRUE; + *arg= NULL; + return TRUE; + } + /* + @brief + Processor used to check acceptability of an item in the defining + expression for a virtual column + + @param + arg always ignored + + @retval + FALSE the item is accepted in the definition of a virtual column + @retval + TRUE otherwise + */ + virtual bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor(full_name()); } virtual Item *equal_fields_propagator(uchar * arg) { return this; } @@ -1371,6 +1426,10 @@ public: { return value_item->send(protocol, str); } + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("name_const"); + } }; bool agg_item_collations(DTCollation &c, const char *name, @@ -1390,6 +1449,7 @@ public: virtual Item_num *neg()= 0; Item *safe_charset_converter(CHARSET_INFO *tocs); bool check_partition_func_processor(uchar *int_arg) { return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; #define NO_CACHED_FIELD_INDEX ((uint)(-1)) @@ -1549,7 +1609,10 @@ public: bool collect_item_field_processor(uchar * arg); bool find_item_in_field_list_processor(uchar *arg); bool register_field_in_read_map(uchar *arg); + bool register_field_in_bitmap(uchar *arg); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool vcol_in_partition_func_processor(uchar *bool_arg); + bool check_vcol_func_processor(uchar *arg) { return FALSE;} bool enumerate_field_refs_processor(uchar *arg); void cleanup(); bool result_as_longlong() @@ -1610,6 +1673,7 @@ public: Item *safe_charset_converter(CHARSET_INFO *tocs); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; class Item_null_result :public Item_null @@ -1623,7 +1687,11 @@ public: save_in_field(result_field, no_conversions); } bool check_partition_func_processor(uchar *int_arg) {return TRUE;} -}; + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor(full_name()); + } +}; /* Item represents one placeholder ('?') of prepared statement */ @@ -1764,6 +1832,7 @@ public: /** Item is a argument to a limit clause. */ bool limit_clause_param; void set_param_type_and_swap_value(Item_param *from); + }; @@ -1799,6 +1868,7 @@ public: { return (uint)(max_length - test(value < 0)); } bool eq(const Item *, bool binary_cmp) const; bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} + bool check_vcol_func_processor(uchar arg) { return FALSE;} }; @@ -1817,6 +1887,7 @@ public: Item_num *neg (); uint decimal_precision() const { return max_length; } bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -1858,6 +1929,7 @@ public: bool eq(const Item *, bool binary_cmp) const; void set_decimal_value(my_decimal *value_par); bool check_partition_func_processor(uchar *bool_arg) { return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -2015,6 +2087,7 @@ public: } virtual void print(String *str, enum_query_type query_type); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} /** Return TRUE if character-set-introducer was explicitly specified in the @@ -2068,7 +2141,7 @@ double_from_string_with_check (CHARSET_INFO *cs, const char *cptr, char *end); class Item_static_string_func :public Item_string { const char *func_name; -public: + public: Item_static_string_func(const char *name_par, const char *str, uint length, CHARSET_INFO *cs, Derivation dv= DERIVATION_COERCIBLE) @@ -2082,6 +2155,10 @@ public: } bool check_partition_func_processor(uchar *int_arg) {return TRUE;} + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name); + } }; @@ -2093,6 +2170,10 @@ public: CHARSET_INFO *cs= NULL): Item_string(name_arg, length, cs) {} + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("safe_string"); + } }; @@ -2172,6 +2253,7 @@ public: bool eq(const Item *item, bool binary_cmp) const; virtual Item *safe_charset_converter(CHARSET_INFO *tocs); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *arg) { return FALSE;} }; @@ -2202,6 +2284,7 @@ public: save_in_field(result_field, no_conversions); } void cleanup(); + bool check_vcol_func_processor(uchar *arg) { return FALSE;} /* This method is used for debug purposes to print the name of an item to the debug log. The second use of this method is as @@ -2348,7 +2431,10 @@ public: if (ref && result_type() == ROW_RESULT) (*ref)->bring_value(); } - + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("ref"); + } }; @@ -2624,6 +2710,10 @@ public: table_map used_tables() const { return (table_map) 1L; } bool const_item() const { return 0; } bool is_null() { return null_value; } + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("copy"); + } /* Override the methods below as pure virtual to make sure all the @@ -2866,6 +2956,10 @@ public: return arg->walk(processor, walk_subquery, args) || (this->*processor)(args); } + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("values"); + } }; @@ -2962,6 +3056,10 @@ private: BEFORE INSERT of BEFORE UPDATE trigger. */ bool read_only; + virtual bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("trigger"); + } }; @@ -3031,6 +3129,11 @@ public: { return this == item; } + bool check_vcol_func_processor(uchar *arg) + { + return trace_unsupported_by_check_vcol_func_processor("cache"); + } + virtual void store(Item *item); virtual bool cache_value()= 0; }; diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 01d4ae67a3f..76e73837e59 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -772,7 +772,7 @@ public: virtual uchar *get_value(Item *item)=0; void sort() { - my_qsort2(base,used_count,size,compare,collation); + my_qsort2(base,used_count,size,compare,(void*)collation); } int find(Item *item); diff --git a/sql/item_func.cc b/sql/item_func.cc index 6c295635d6c..bb058f0277a 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -3899,10 +3899,30 @@ bool Item_func_set_user_var::register_field_in_read_map(uchar *arg) TABLE *table= (TABLE *) arg; if (result_field->table == table || !table) bitmap_set_bit(result_field->table->read_set, result_field->field_index); + if (result_field->vcol_info) + return result_field->vcol_info-> + expr_item->walk(&Item::register_field_in_read_map, 1, arg); } return 0; } +/* + Mark field in bitmap supplied as *arg + +*/ + +bool Item_func_set_user_var::register_field_in_bitmap(uchar *arg) +{ + MY_BITMAP *bitmap = (MY_BITMAP *) arg; + DBUG_ASSERT(bitmap); + if (result_field) + { + if (!bitmap) + return 1; + bitmap_set_bit(bitmap, result_field->field_index); + } + return 0; +} /** Set value to user variable. diff --git a/sql/item_func.h b/sql/item_func.h index b7994bd941e..507f5d21a3e 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -356,6 +356,7 @@ public: void fix_length_and_dec(); bool fix_fields(THD *thd, Item **ref); longlong val_int() { DBUG_ASSERT(fixed == 1); return value; } + bool check_vcol_func_processor(uchar *int_arg) { return TRUE;} }; @@ -416,6 +417,7 @@ public: Item_func_additive_op(Item *a,Item *b) :Item_num_op(a,b) {} void result_precision(); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -451,6 +453,7 @@ public: my_decimal *decimal_op(my_decimal *); void result_precision(); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -483,6 +486,7 @@ public: } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -497,6 +501,7 @@ public: void result_precision(); void fix_length_and_dec(); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -513,6 +518,7 @@ public: void fix_num_length_and_dec(); uint decimal_precision() const { return args[0]->decimal_precision(); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -526,6 +532,7 @@ public: const char *func_name() const { return "abs"; } void fix_length_and_dec(); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; // A class to handle logarithmic and trigonometric functions @@ -681,6 +688,7 @@ public: double real_op(); my_decimal *decimal_op(my_decimal *); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -693,6 +701,7 @@ public: double real_op(); my_decimal *decimal_op(my_decimal *); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; /* This handles round and truncate */ @@ -724,6 +733,10 @@ public: void update_used_tables(); bool fix_fields(THD *thd, Item **ref); void cleanup() { first_eval= TRUE; Item_real_func::cleanup(); } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } private: void seed_random (Item * val); }; @@ -1006,6 +1019,10 @@ public: max_length= args[0]->max_length; } bool fix_fields(THD *thd, Item **ref); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1019,6 +1036,10 @@ public: const char *func_name() const { return "benchmark"; } void fix_length_and_dec() { max_length=1; maybe_null=0; } virtual void print(String *str, enum_query_type query_type); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1034,6 +1055,10 @@ public: used_tables_cache|= RAND_TABLE_BIT; } longlong val_int(); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1283,6 +1308,10 @@ class Item_func_get_lock :public Item_int_func longlong val_int(); const char *func_name() const { return "get_lock"; } void fix_length_and_dec() { max_length=1; maybe_null=1;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; class Item_func_release_lock :public Item_int_func @@ -1293,6 +1322,10 @@ public: longlong val_int(); const char *func_name() const { return "release_lock"; } void fix_length_and_dec() { max_length=1; maybe_null=1;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; /* replication functions */ @@ -1306,6 +1339,10 @@ public: longlong val_int(); const char *func_name() const { return "master_pos_wait"; } void fix_length_and_dec() { max_length=21; maybe_null=1;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1377,6 +1414,7 @@ public: } void save_org_in_field(Field *field) { (void)save_in_field(field, 1, 0); } bool register_field_in_read_map(uchar *arg); + bool register_field_in_bitmap(uchar *arg); bool set_entry(THD *thd, bool create_if_not_exists); void cleanup(); }; @@ -1551,6 +1589,11 @@ public: bool fix_index(); void init_search(bool no_order); + bool check_vcol_func_processor(uchar *int_arg) + { + /* TODO: consider adding in support for the MATCH-based virtual columns */ + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1570,6 +1613,17 @@ public: 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;} + bool check_vcol_func_processor(uchar *int_arg) + { +#if 0 + DBUG_ENTER("Item_func_is_free_lock::check_vcol_func_processor"); + DBUG_PRINT("info", + ("check_vcol_func_processor returns TRUE: unsupported function")); + DBUG_RETURN(TRUE); +#else + return trace_unsupported_by_check_vcol_func_processor(func_name()); +#endif + } }; class Item_func_is_used_lock :public Item_int_func @@ -1580,6 +1634,10 @@ public: 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;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; /* For type casts */ @@ -1599,6 +1657,11 @@ public: longlong val_int(); const char *func_name() const { return "row_count"; } void fix_length_and_dec() { decimals= 0; maybe_null=0; } + bool check_vcol_func_processor(uchar *int_arg) + { + + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1707,6 +1770,10 @@ public: { return sp_result_field; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1717,6 +1784,10 @@ public: longlong val_int(); const char *func_name() const { return "found_rows"; } void fix_length_and_dec() { decimals= 0; maybe_null=0; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -1731,5 +1802,9 @@ public: void fix_length_and_dec() { max_length= 21; unsigned_flag=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; diff --git a/sql/item_row.h b/sql/item_row.h index 67441f49603..71679100448 100644 --- a/sql/item_row.h +++ b/sql/item_row.h @@ -76,4 +76,5 @@ public: bool check_cols(uint c); bool null_inside() { return with_null; }; void bring_value(); -}; + bool check_vcol_func_processor(uchar *int_arg) {return FALSE; } + }; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 59e7c0df5b6..85cc4b07231 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -344,6 +344,10 @@ public: String *val_str(String *); void fix_length_and_dec() { maybe_null=1; max_length = 13; } const char *func_name() const { return "encrypt"; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; #include "sql_crypt.h" @@ -392,6 +396,11 @@ public: call */ virtual const char *fully_qualified_func_name() const = 0; + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor( + fully_qualified_func_name()); + } }; @@ -655,6 +664,10 @@ public: maybe_null=1; max_length=MAX_BLOB_WIDTH; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -851,5 +864,9 @@ public: } const char *func_name() const{ return "uuid"; } String *val_str(String *); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc index dad05981af4..b550d6e8e8e 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -2057,7 +2057,7 @@ int subselect_uniquesubquery_engine::scan_table() table->null_row= 0; for (;;) { - error=table->file->rnd_next(table->record[0]); + error=table->file->ha_rnd_next(table->record[0]); if (error && error != HA_ERR_END_OF_FILE) { error= report_error(table, error); @@ -2231,10 +2231,11 @@ int subselect_uniquesubquery_engine::exec() if (!table->file->inited) table->file->ha_index_init(tab->ref.key, 0); - error= table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab-> + ref.key_parts), + HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) error= report_error(table, error); @@ -2352,10 +2353,11 @@ int subselect_indexsubquery_engine::exec() if (!table->file->inited) table->file->ha_index_init(tab->ref.key, 1); - error= table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab-> + ref.key_parts), + HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) error= report_error(table, error); @@ -2376,9 +2378,9 @@ int subselect_indexsubquery_engine::exec() ((Item_in_subselect *) item)->value= 1; break; } - error= table->file->index_next_same(table->record[0], - tab->ref.key_buff, - tab->ref.key_length); + error= table->file->ha_index_next_same(table->record[0], + tab->ref.key_buff, + tab->ref.key_length); if (error && error != HA_ERR_END_OF_FILE) { error= report_error(table, error); diff --git a/sql/item_subselect.h b/sql/item_subselect.h index 8d43f2bd383..d19f23bff71 100644 --- a/sql/item_subselect.h +++ b/sql/item_subselect.h @@ -137,6 +137,10 @@ public: bool walk(Item_processor processor, bool walk_subquery, uchar *arg); bool mark_as_eliminated_processor(uchar *arg); bool enumerate_field_refs_processor(uchar *arg); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor("subselect"); + } /** Get the SELECT_LEX structure associated with this Item. diff --git a/sql/item_sum.h b/sql/item_sum.h index 110265fd8b4..74b39674132 100644 --- a/sql/item_sum.h +++ b/sql/item_sum.h @@ -383,6 +383,10 @@ public: Item *get_arg(int i) { return args[i]; } Item *set_arg(int i, THD *thd, Item *new_val); uint get_arg_count() { return arg_count; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -663,6 +667,10 @@ public: } void fix_length_and_dec() {} enum Item_result result_type () const { return hybrid_type; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor("avg_field"); + } const char *func_name() const { DBUG_ASSERT(0); return "avg_field"; } }; @@ -732,6 +740,10 @@ public: } void fix_length_and_dec() {} enum Item_result result_type () const { return hybrid_type; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor("var_field"); + } const char *func_name() const { DBUG_ASSERT(0); return "variance_field"; } }; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index a7a64090f6c..e11449c200e 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -70,6 +70,7 @@ public: enum_monotonicity_info get_monotonicity_info() const; longlong val_int_endpoint(bool left_endp, bool *incl_endp); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -86,6 +87,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -111,6 +113,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -124,6 +127,7 @@ public: enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec(); bool check_partition_func_processor(uchar *int_arg) {return TRUE;} + bool check_vcol_func_processor(uchar *int_arg) {return FALSE;} }; @@ -140,6 +144,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -156,6 +161,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -172,6 +178,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -188,6 +195,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -204,6 +212,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -234,6 +243,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -252,6 +262,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -282,6 +293,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; class Item_func_dayname :public Item_func_weekday @@ -294,6 +306,7 @@ class Item_func_dayname :public Item_func_weekday enum Item_result result_type () const { return STRING_RESULT; } void fix_length_and_dec(); bool check_partition_func_processor(uchar *int_arg) {return TRUE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -320,6 +333,14 @@ public: decimals=0; max_length=10*MY_CHARSET_BIN_MB_MAXLEN; } + bool check_vcol_func_processor(uchar *int_arg) + { + /* + TODO: Allow UNIX_TIMESTAMP called with an argument to be a part + of the expression for a virtual column + */ + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -335,6 +356,7 @@ public: max_length=10*MY_CHARSET_BIN_MB_MAXLEN; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -455,6 +477,10 @@ public: */ virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; bool result_as_longlong() { return TRUE; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -491,6 +517,10 @@ public: void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -531,6 +561,10 @@ public: void fix_length_and_dec(); bool get_date(MYSQL_TIME *res, uint fuzzy_date); virtual void store_now_in_TIME(MYSQL_TIME *now_time)=0; + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; @@ -588,6 +622,7 @@ public: const char *func_name() const { return "from_days"; } bool get_date(MYSQL_TIME *res, uint fuzzy_date); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -715,6 +750,7 @@ class Item_extract :public Item_int_func bool eq(const Item *item, bool binary_cmp) const; virtual void print(String *str, enum_query_type query_type); bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; @@ -963,6 +999,7 @@ public: maybe_null=1; } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} + bool check_vcol_func_processor(uchar *int_arg) { return FALSE;} }; diff --git a/sql/item_xmlfunc.cc b/sql/item_xmlfunc.cc index 3e178dd8938..537965b60cc 100644 --- a/sql/item_xmlfunc.cc +++ b/sql/item_xmlfunc.cc @@ -220,6 +220,11 @@ public: collation.collation= pxml->charset(); } const char *func_name() const { return "nodeset"; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } + }; @@ -526,6 +531,10 @@ public: enum Type type() const { return XPATH_NODESET_CMP; }; const char *func_name() const { return "xpath_nodeset_to_const_comparator"; } bool is_bool_func() { return 1; } + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } longlong val_int() { diff --git a/sql/item_xmlfunc.h b/sql/item_xmlfunc.h index dadbb5ccf42..ce33d161c79 100644 --- a/sql/item_xmlfunc.h +++ b/sql/item_xmlfunc.h @@ -40,6 +40,10 @@ public: } void fix_length_and_dec(); String *parse_xml(String *raw_xml, String *parsed_xml_buf); + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor(func_name()); + } }; diff --git a/sql/lex.h b/sql/lex.h index d9c382347c9..7671f23682d 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -62,6 +62,7 @@ static SYMBOL symbols[] = { { "ALL", SYM(ALL)}, { "ALGORITHM", SYM(ALGORITHM_SYM)}, { "ALTER", SYM(ALTER)}, + { "ALWAYS", SYM(ALWAYS_SYM)}, { "ANALYZE", SYM(ANALYZE_SYM)}, { "AND", SYM(AND_SYM)}, { "ANY", SYM(ANY_SYM)}, @@ -106,6 +107,7 @@ static SYMBOL symbols[] = { { "CHECKSUM", SYM(CHECKSUM_SYM)}, { "CIPHER", SYM(CIPHER_SYM)}, { "CLIENT", SYM(CLIENT_SYM)}, + { "CLIENT_STATISTICS", SYM(CLIENT_STATS_SYM)}, { "CLOSE", SYM(CLOSE_SYM)}, { "COALESCE", SYM(COALESCE)}, { "CODE", SYM(CODE_SYM)}, @@ -220,6 +222,7 @@ static SYMBOL symbols[] = { { "FULL", SYM(FULL)}, { "FULLTEXT", SYM(FULLTEXT_SYM)}, { "FUNCTION", SYM(FUNCTION_SYM)}, + { "GENERATED", SYM(GENERATED_SYM)}, { "GEOMETRY", SYM(GEOMETRY_SYM)}, { "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION)}, { "GET_FORMAT", SYM(GET_FORMAT)}, @@ -245,6 +248,7 @@ static SYMBOL symbols[] = { { "IN", SYM(IN_SYM)}, { "INDEX", SYM(INDEX_SYM)}, { "INDEXES", SYM(INDEXES)}, + { "INDEX_STATISTICS", SYM(INDEX_STATS_SYM)}, { "INFILE", SYM(INFILE)}, { "INITIAL_SIZE", SYM(INITIAL_SIZE_SYM)}, { "INNER", SYM(INNER_SYM)}, @@ -385,14 +389,16 @@ static SYMBOL symbols[] = { { "OUTFILE", SYM(OUTFILE)}, { "OWNER", SYM(OWNER_SYM)}, { "PACK_KEYS", SYM(PACK_KEYS_SYM)}, - { "PARSER", SYM(PARSER_SYM)}, { "PAGE", SYM(PAGE_SYM)}, { "PAGE_CHECKSUM", SYM(PAGE_CHECKSUM_SYM)}, + { "PARSER", SYM(PARSER_SYM)}, + { "PARSE_VCOL_EXPR", SYM(PARSE_VCOL_EXPR_SYM)}, { "PARTIAL", SYM(PARTIAL)}, { "PARTITION", SYM(PARTITION_SYM)}, { "PARTITIONING", SYM(PARTITIONING_SYM)}, { "PARTITIONS", SYM(PARTITIONS_SYM)}, { "PASSWORD", SYM(PASSWORD)}, + { "PERSISTENT", SYM(PERSISTENT_SYM)}, { "PHASE", SYM(PHASE_SYM)}, { "PLUGIN", SYM(PLUGIN_SYM)}, { "PLUGINS", SYM(PLUGINS_SYM)}, @@ -478,6 +484,7 @@ static SYMBOL symbols[] = { { "SIGNED", SYM(SIGNED_SYM)}, { "SIMPLE", SYM(SIMPLE_SYM)}, { "SLAVE", SYM(SLAVE)}, + { "SLOW", SYM(SLOW_SYM)}, { "SNAPSHOT", SYM(SNAPSHOT_SYM)}, { "SMALLINT", SYM(SMALLINT)}, { "SOCKET", SYM(SOCKET_SYM)}, @@ -526,6 +533,7 @@ static SYMBOL symbols[] = { { "TABLE", SYM(TABLE_SYM)}, { "TABLES", SYM(TABLES)}, { "TABLESPACE", SYM(TABLESPACE)}, + { "TABLE_STATISTICS", SYM(TABLE_STATS_SYM)}, { "TABLE_CHECKSUM", SYM(TABLE_CHECKSUM_SYM)}, { "TEMPORARY", SYM(TEMPORARY)}, { "TEMPTABLE", SYM(TEMPTABLE_SYM)}, @@ -569,6 +577,7 @@ static SYMBOL symbols[] = { { "USE", SYM(USE_SYM)}, { "USER", SYM(USER)}, { "USER_RESOURCES", SYM(RESOURCES)}, + { "USER_STATISTICS", SYM(USER_STATS_SYM)}, { "USE_FRM", SYM(USE_FRM)}, { "USING", SYM(USING)}, { "UTC_DATE", SYM(UTC_DATE_SYM)}, @@ -581,13 +590,15 @@ static SYMBOL symbols[] = { { "VARCHARACTER", SYM(VARCHAR)}, { "VARIABLES", SYM(VARIABLES)}, { "VARYING", SYM(VARYING)}, + { "VIA", SYM(VIA_SYM)}, + { "VIEW", SYM(VIEW_SYM)}, + { "VIRTUAL", SYM(VIRTUAL_SYM)}, { "WAIT", SYM(WAIT_SYM)}, { "WARNINGS", SYM(WARNINGS)}, { "WEEK", SYM(WEEK_SYM)}, { "WHEN", SYM(WHEN_SYM)}, { "WHERE", SYM(WHERE)}, { "WHILE", SYM(WHILE_SYM)}, - { "VIEW", SYM(VIEW_SYM)}, { "WITH", SYM(WITH)}, { "WORK", SYM(WORK_SYM)}, { "WRAPPER", SYM(WRAPPER_SYM)}, diff --git a/sql/log.cc b/sql/log.cc index f52e68dd1b9..bfe194172ba 100644 --- a/sql/log.cc +++ b/sql/log.cc @@ -824,6 +824,13 @@ void Log_to_file_event_handler::flush() mysql_slow_log.reopen_file(); } +void Log_to_file_event_handler::flush_slow_log() +{ + /* reopen slow log file */ + if (opt_slow_log) + mysql_slow_log.reopen_file(); +} + /* Log error with all enabled log event handlers @@ -919,8 +926,6 @@ void LOGGER::init_log_tables() bool LOGGER::flush_logs(THD *thd) { - int rc= 0; - /* Now we lock logger, as nobody should be able to use logging routines while log tables are closed @@ -932,7 +937,24 @@ bool LOGGER::flush_logs(THD *thd) /* end of log flush */ logger.unlock(); - return rc; + return 0; +} + + +bool LOGGER::flush_slow_log(THD *thd) +{ + /* + Now we lock logger, as nobody should be able to use logging routines while + log tables are closed + */ + logger.lock_exclusive(); + + /* reopen log files */ + file_log_handler->flush_slow_log(); + + /* end of log flush */ + logger.unlock(); + return 0; } @@ -4305,6 +4327,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) if (likely(is_open())) { IO_CACHE *file= &log_file; + my_off_t my_org_b_tell; #ifdef HAVE_REPLICATION /* In the future we need to add to the following if tests like @@ -4320,6 +4343,8 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) } #endif /* HAVE_REPLICATION */ + my_org_b_tell= my_b_tell(file); + #if defined(USING_TRANSACTIONS) /* Should we write to the binlog cache or to the binlog on disk? @@ -4338,7 +4363,7 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) 2 - --binlog-direct-non-transactional-updates is set and we are about to use the statement format. When using the row format (cache_stmt == TRUE). */ - if (opt_using_transactions && thd) + if (opt_using_transactions) { if (thd->binlog_setup_trx_data()) goto err; @@ -4381,7 +4406,6 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) If row-based binlogging, Insert_id, Rand and other kind of "setting context" events are not needed. */ - if (thd) { if (!thd->current_stmt_binlog_row_based) { @@ -4428,16 +4452,16 @@ bool MYSQL_BIN_LOG::write(Log_event *event_info) } } - /* - Write the SQL command - */ - + /* Write the SQL command */ if (event_info->write(file) || DBUG_EVALUATE_IF("injecting_fault_writing", 1, 0)) goto err; if (file == &log_file) // we are writing to the real log (disk) { + ulonglong data_written= (my_b_tell(file) - my_org_b_tell); + status_var_add(thd->status_var.binlog_bytes_written, data_written); + if (flush_and_sync()) goto err; signal_update(); @@ -4574,6 +4598,7 @@ uint MYSQL_BIN_LOG::next_file_id() SYNOPSIS write_cache() + thd Current_thread cache Cache to write to the binary log lock_log True if the LOCK_log mutex should be aquired, false otherwise sync_log True if the log should be flushed and sync:ed @@ -4583,7 +4608,8 @@ uint MYSQL_BIN_LOG::next_file_id() be reset as a READ_CACHE to be able to read the contents from it. */ -int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log) +int MYSQL_BIN_LOG::write_cache(THD *thd, IO_CACHE *cache, bool lock_log, + bool sync_log) { Mutex_sentry sentry(lock_log ? &LOCK_log : NULL); @@ -4631,6 +4657,7 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log) /* write the first half of the split header */ if (my_b_write(&log_file, header, carry)) return ER_ERROR_ON_WRITE; + status_var_add(thd->status_var.binlog_bytes_written, carry); /* copy fixed second half of header to cache so the correct @@ -4699,6 +4726,8 @@ int MYSQL_BIN_LOG::write_cache(IO_CACHE *cache, bool lock_log, bool sync_log) /* Write data to the binary log file */ if (my_b_write(&log_file, cache->read_pos, length)) return ER_ERROR_ON_WRITE; + status_var_add(thd->status_var.binlog_bytes_written, length); + cache->read_pos=cache->read_end; // Mark buffer used up } while ((length= my_b_fill(cache))); @@ -4750,6 +4779,7 @@ bool MYSQL_BIN_LOG::write_incident(THD *thd, bool lock) if (lock) pthread_mutex_lock(&LOCK_log); error= ev.write(&log_file); + status_var_add(thd->status_var.binlog_bytes_written, ev.data_written); if (lock) { if (!error && !(error= flush_and_sync())) @@ -4821,21 +4851,28 @@ bool MYSQL_BIN_LOG::write(THD *thd, IO_CACHE *cache, Log_event *commit_event, */ if (qinfo.write(&log_file)) goto err; + status_var_add(thd->status_var.binlog_bytes_written, qinfo.data_written); DBUG_EXECUTE_IF("crash_before_writing_xid", { - if ((write_error= write_cache(cache, false, true))) + if ((write_error= write_cache(thd, cache, FALSE, + TRUE))) DBUG_PRINT("info", ("error writing binlog cache: %d", write_error)); DBUG_PRINT("info", ("crashing before writing xid")); abort(); }); - if ((write_error= write_cache(cache, false, false))) + if ((write_error= write_cache(thd, cache, FALSE, FALSE))) goto err; - if (commit_event && commit_event->write(&log_file)) - goto err; + if (commit_event) + { + if (commit_event->write(&log_file)) + goto err; + status_var_add(thd->status_var.binlog_bytes_written, + commit_event->data_written); + } if (incident && write_incident(thd, FALSE)) goto err; @@ -6000,3 +6037,20 @@ mysql_declare_plugin(binlog) NULL /* config options */ } mysql_declare_plugin_end; +maria_declare_plugin(binlog) +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &binlog_storage_engine, + "binlog", + "MySQL AB", + "This is a pseudo storage engine to represent the binlog in a transaction", + PLUGIN_LICENSE_GPL, + binlog_init, /* Plugin Init */ + NULL, /* Plugin Deinit */ + 0x0100 /* 1.0 */, + NULL, /* status variables */ + NULL, /* system variables */ + "1.0", /* string version */ + MariaDB_PLUGIN_MATURITY_STABLE /* maturity */ +} +maria_declare_plugin_end; diff --git a/sql/log.h b/sql/log.h index 8b5dfcb3935..c6cf8780c04 100644 --- a/sql/log.h +++ b/sql/log.h @@ -365,7 +365,8 @@ public: bool write(THD *thd, IO_CACHE *cache, Log_event *commit_event, bool incident); bool write_incident(THD *thd, bool lock); - int write_cache(IO_CACHE *cache, bool lock_log, bool flush_and_sync); + int write_cache(THD *thd, IO_CACHE *cache, bool lock_log, + bool flush_and_sync); void set_write_error(THD *thd); bool check_write_error(THD *thd); @@ -503,6 +504,7 @@ public: const char *sql_text, uint sql_text_len, CHARSET_INFO *client_cs); void flush(); + void flush_slow_log(); void init_pthread_objects(); MYSQL_QUERY_LOG *get_mysql_slow_log() { return &mysql_slow_log; } MYSQL_QUERY_LOG *get_mysql_log() { return &mysql_log; } @@ -547,6 +549,7 @@ public: void init_base(); void init_log_tables(); bool flush_logs(THD *thd); + bool flush_slow_log(THD *thd); /* Perform basic logger cleanup. this will leave e.g. error log open. */ void cleanup_base(); /* Free memory. Nothing could be logged after this function is called */ diff --git a/sql/log_event.cc b/sql/log_event.cc index 94b6b35ec8c..fccaefaab0c 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -1121,7 +1121,7 @@ failed my_b_read")); goto err; } if ((res= read_log_event(buf, data_len, &error, description_event))) - res->register_temp_buf(buf); + res->register_temp_buf(buf, TRUE); err: UNLOCK_MUTEX; @@ -1716,10 +1716,11 @@ beg: case MYSQL_TYPE_DATETIME: { + /* these must be size_t, because it's what my_b_printf expects for %d */ size_t d, t; uint64 i64= uint8korr(ptr); /* YYYYMMDDhhmmss */ - d= i64 / 1000000; - t= i64 % 1000000; + d= (size_t)(i64 / 1000000); + t= (size_t)(i64 % 1000000); my_b_printf(file, "%04d-%02d-%02d %02d:%02d:%02d", d / 10000, (d % 10000) / 100, d % 100, t / 10000, (t % 10000) / 100, t % 100); @@ -4556,7 +4557,7 @@ int Load_log_event::do_apply_event(NET* net, Relay_log_info const *rli, */ lex_start(thd); thd->lex->local_file= local_fname; - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); if (!use_rli_only_for_errors) { @@ -6352,7 +6353,7 @@ int Append_block_log_event::do_apply_event(Relay_log_info const *rli) as the present method does not call mysql_parse(). */ lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); my_delete(fname, MYF(0)); // old copy may exist already if ((fd= my_create(fname, CREATE_MODE, O_WRONLY | O_BINARY | O_EXCL | O_NOFOLLOW, @@ -7292,7 +7293,7 @@ int Rows_log_event::do_apply_event(Relay_log_info const *rli) we need to do any changes to that value after this function. */ lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); /* The current statement is just about to begin and has not yet modified anything. Note, all.modified is reset @@ -8090,6 +8091,111 @@ Table_map_log_event::~Table_map_log_event() my_free(m_memory, MYF(MY_ALLOW_ZERO_PTR)); } + +#ifdef MYSQL_CLIENT + +/* + Rewrite database name for the event to name specified by new_db + SYNOPSIS + new_db Database name to change to + new_len Length + desc Event describing binlog that we're writing to. + + DESCRIPTION + Reset db name. This function assumes that temp_buf member contains event + representation taken from a binary log. It resets m_dbnam and m_dblen and + rewrites temp_buf with new db name. + + RETURN + 0 - Success + other - Error +*/ + +int Table_map_log_event::rewrite_db(const char* new_db, size_t new_len, + const Format_description_log_event* desc) +{ + DBUG_ENTER("Table_map_log_event::rewrite_db"); + DBUG_ASSERT(temp_buf); + + uint header_len= min(desc->common_header_len, + LOG_EVENT_MINIMAL_HEADER_LEN) + TABLE_MAP_HEADER_LEN; + int len_diff; + + if (!(len_diff= new_len - m_dblen)) + { + memcpy((void*) (temp_buf + header_len + 1), new_db, m_dblen + 1); + memcpy((void*) m_dbnam, new_db, m_dblen + 1); + DBUG_RETURN(0); + } + + // Create new temp_buf + ulong event_cur_len= uint4korr(temp_buf + EVENT_LEN_OFFSET); + ulong event_new_len= event_cur_len + len_diff; + char* new_temp_buf= (char*) my_malloc(event_new_len, MYF(MY_WME)); + + if (!new_temp_buf) + { + sql_print_error("Table_map_log_event::rewrite_db: " + "failed to allocate new temp_buf (%d bytes required)", + event_new_len); + DBUG_RETURN(-1); + } + + // Rewrite temp_buf + char* ptr= new_temp_buf; + ulong cnt= 0; + + // Copy header and change event length + memcpy(ptr, temp_buf, header_len); + int4store(ptr + EVENT_LEN_OFFSET, event_new_len); + ptr += header_len; + cnt += header_len; + + // Write new db name length and new name + *ptr++ = new_len; + memcpy(ptr, new_db, new_len + 1); + ptr += new_len + 1; + cnt += m_dblen + 2; + + // Copy rest part + memcpy(ptr, temp_buf + cnt, event_cur_len - cnt); + + // Reregister temp buf + free_temp_buf(); + register_temp_buf(new_temp_buf, TRUE); + + // Reset m_dbnam and m_dblen members + m_dblen= new_len; + + // m_dbnam resides in m_memory together with m_tblnam and m_coltype + uchar* memory= m_memory; + char const* tblnam= m_tblnam; + uchar* coltype= m_coltype; + + m_memory= (uchar*) my_multi_malloc(MYF(MY_WME), + &m_dbnam, (uint) m_dblen + 1, + &m_tblnam, (uint) m_tbllen + 1, + &m_coltype, (uint) m_colcnt, + NullS); + + if (!m_memory) + { + sql_print_error("Table_map_log_event::rewrite_db: " + "failed to allocate new m_memory (%d + %d + %d bytes required)", + m_dblen + 1, m_tbllen + 1, m_colcnt); + DBUG_RETURN(-1); + } + + memcpy((void*)m_dbnam, new_db, m_dblen + 1); + memcpy((void*)m_tblnam, tblnam, m_tbllen + 1); + memcpy(m_coltype, coltype, m_colcnt); + + my_free(memory, MYF(MY_WME)); + DBUG_RETURN(0); +} +#endif /* MYSQL_CLIENT */ + + /* Return value is an error code, one of: @@ -8565,7 +8671,7 @@ Rows_log_event::write_row(const Relay_log_info *const rli, if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { DBUG_PRINT("info",("Locating offending record using rnd_pos()")); - error= table->file->rnd_pos(table->record[1], table->file->dup_ref); + error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref); if (error) { DBUG_PRINT("info",("rnd_pos() returns error %d",error)); @@ -8597,10 +8703,10 @@ Rows_log_event::write_row(const Relay_log_info *const rli, key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum, 0); - error= table->file->index_read_idx_map(table->record[1], keynum, - (const uchar*)key.get(), - HA_WHOLE_KEY, - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_idx_map(table->record[1], keynum, + (const uchar*)key.get(), + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); if (error) { DBUG_PRINT("info",("index_read_idx() returns %s", HA_ERR(error))); @@ -8868,13 +8974,14 @@ int Rows_log_event::find_row(const Relay_log_info *rli) length. Something along these lines should work: ADD>>> store_record(table,record[1]); - int error= table->file->rnd_pos(table->record[0], table->file->ref); + int error= table->file->ha_rnd_pos(table->record[0], + table->file->ref); ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0], table->s->reclength) == 0); */ DBUG_PRINT("info",("locating record using primary key (position)")); - int error= table->file->rnd_pos_by_record(table->record[0]); + int error= table->file->ha_rnd_pos_by_record(table->record[0]); if (error) { DBUG_PRINT("info",("rnd_pos returns error %d",error)); @@ -8934,9 +9041,9 @@ int Rows_log_event::find_row(const Relay_log_info *rli) table->record[0][table->s->null_bytes - 1]|= 256U - (1U << table->s->last_null_bit_pos); - if ((error= table->file->index_read_map(table->record[0], m_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_map(table->record[0], m_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { DBUG_PRINT("info",("no record matching the key found in the table")); if (error == HA_ERR_RECORD_DELETED) @@ -8998,7 +9105,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli) 256U - (1U << table->s->last_null_bit_pos); } - while ((error= table->file->index_next(table->record[0]))) + while ((error= table->file->ha_index_next(table->record[0]))) { /* We just skip records that has already been deleted */ if (error == HA_ERR_RECORD_DELETED) @@ -9034,7 +9141,7 @@ int Rows_log_event::find_row(const Relay_log_info *rli) do { restart_rnd_next: - error= table->file->rnd_next(table->record[0]); + error= table->file->ha_rnd_next(table->record[0]); DBUG_PRINT("info", ("error: %s", HA_ERR(error))); switch (error) { diff --git a/sql/log_event.h b/sql/log_event.h index 36715b1d151..d88f53fc864 100644 --- a/sql/log_event.h +++ b/sql/log_event.h @@ -878,6 +878,13 @@ public: event's type, and its content is distributed in the event-specific fields. */ char *temp_buf; + + /* + TRUE <=> this event 'owns' temp_buf and should call my_free() when done + with it + */ + bool event_owns_temp_buf; + /* Timestamp on the master(for debugging and replication of NOW()/TIMESTAMP). It is important for queries and LOAD DATA @@ -1019,12 +1026,17 @@ public: Log_event(const char* buf, const Format_description_log_event *description_event); virtual ~Log_event() { free_temp_buf();} - void register_temp_buf(char* buf) { temp_buf = buf; } + void register_temp_buf(char* buf, bool must_free) + { + temp_buf= buf; + event_owns_temp_buf= must_free; + } void free_temp_buf() { if (temp_buf) { - my_free(temp_buf, MYF(0)); + if (event_owns_temp_buf) + my_free(temp_buf, MYF(0)); temp_buf = 0; } } @@ -1359,7 +1371,7 @@ protected: MODE_PIPES_AS_CONCAT==0x2 MODE_ANSI_QUOTES==0x4 MODE_IGNORE_SPACE==0x8 - MODE_NOT_USED==0x10 + MODE_IGNORE_BAD_TABLE_OPTIONS==0x10 MODE_ONLY_FULL_GROUP_BY==0x20 MODE_NO_UNSIGNED_SUBTRACTION==0x40 MODE_NO_DIR_IN_CREATE==0x80 @@ -3325,6 +3337,8 @@ public: ulong get_table_id() const { return m_table_id; } const char *get_table_name() const { return m_tblnam; } const char *get_db_name() const { return m_dbnam; } + int rewrite_db(const char* new_name, size_t new_name_len, + const Format_description_log_event*); #endif virtual Log_event_type get_type_code() { return TABLE_MAP_EVENT; } diff --git a/sql/log_event_old.cc b/sql/log_event_old.cc index cda550f3c92..3e090b8a6ca 100644 --- a/sql/log_event_old.cc +++ b/sql/log_event_old.cc @@ -63,7 +63,7 @@ Old_rows_log_event::do_apply_event(Old_rows_log_event *ev, const Relay_log_info we need to do any changes to that value after this function. */ lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); /* Check if the slave is set to use SBR. If so, it should switch @@ -553,7 +553,7 @@ replace_record(THD *thd, TABLE *table, */ if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { - error= table->file->rnd_pos(table->record[1], table->file->dup_ref); + error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref); if (error) { DBUG_PRINT("info",("rnd_pos() returns error %d",error)); @@ -579,10 +579,10 @@ replace_record(THD *thd, TABLE *table, key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum, 0); - error= table->file->index_read_idx_map(table->record[1], keynum, - (const uchar*)key.get(), - HA_WHOLE_KEY, - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_idx_map(table->record[1], keynum, + (const uchar*)key.get(), + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); if (error) { DBUG_PRINT("info", ("index_read_idx() returns error %d", error)); @@ -694,13 +694,13 @@ static int find_and_fetch_row(TABLE *table, uchar *key) length. Something along these lines should work: ADD>>> store_record(table,record[1]); - int error= table->file->rnd_pos(table->record[0], table->file->ref); + int error= table->file->ha_rnd_pos(table->record[0], table->file->ref); ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0], table->s->reclength) == 0); */ table->file->position(table->record[0]); - int error= table->file->rnd_pos(table->record[0], table->file->ref); + int error= table->file->ha_rnd_pos(table->record[0], table->file->ref); /* rnd_pos() returns the record in table->record[0], so we have to move it to table->record[1]. @@ -738,8 +738,9 @@ static int find_and_fetch_row(TABLE *table, uchar *key) my_ptrdiff_t const pos= table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0; table->record[1][pos]= 0xFF; - if ((error= table->file->index_read_map(table->record[1], key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_map(table->record[1], key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { table->file->print_error(error, MYF(0)); table->file->ha_index_end(); @@ -793,7 +794,7 @@ static int find_and_fetch_row(TABLE *table, uchar *key) 256U - (1U << table->s->last_null_bit_pos); } - while ((error= table->file->index_next(table->record[1]))) + while ((error= table->file->ha_index_next(table->record[1]))) { /* We just skip records that has already been deleted */ if (error == HA_ERR_RECORD_DELETED) @@ -822,7 +823,7 @@ static int find_and_fetch_row(TABLE *table, uchar *key) do { restart_rnd_next: - error= table->file->rnd_next(table->record[1]); + error= table->file->ha_rnd_next(table->record[1]); DBUG_DUMP("record[0]", table->record[0], table->s->reclength); DBUG_DUMP("record[1]", table->record[1], table->s->reclength); @@ -2120,7 +2121,7 @@ Old_rows_log_event::write_row(const Relay_log_info *const rli, if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { DBUG_PRINT("info",("Locating offending record using rnd_pos()")); - error= table->file->rnd_pos(table->record[1], table->file->dup_ref); + error= table->file->ha_rnd_pos(table->record[1], table->file->dup_ref); if (error) { DBUG_PRINT("info",("rnd_pos() returns error %d",error)); @@ -2152,10 +2153,10 @@ Old_rows_log_event::write_row(const Relay_log_info *const rli, key_copy((uchar*)key.get(), table->record[0], table->key_info + keynum, 0); - error= table->file->index_read_idx_map(table->record[1], keynum, - (const uchar*)key.get(), - HA_WHOLE_KEY, - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_idx_map(table->record[1], keynum, + (const uchar*)key.get(), + HA_WHOLE_KEY, + HA_READ_KEY_EXACT); if (error) { DBUG_PRINT("info",("index_read_idx() returns error %d", error)); @@ -2306,13 +2307,13 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli) length. Something along these lines should work: ADD>>> store_record(table,record[1]); - int error= table->file->rnd_pos(table->record[0], table->file->ref); + int error= table->file->ha_rnd_pos(table->record[0], table->file->ref); ADD>>> DBUG_ASSERT(memcmp(table->record[1], table->record[0], table->s->reclength) == 0); */ DBUG_PRINT("info",("locating record using primary key (position)")); - int error= table->file->rnd_pos_by_record(table->record[0]); + int error= table->file->ha_rnd_pos_by_record(table->record[0]); if (error) { DBUG_PRINT("info",("rnd_pos returns error %d",error)); @@ -2372,9 +2373,9 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli) table->s->null_bytes > 0 ? table->s->null_bytes - 1 : 0; table->record[0][pos]= 0xFF; - if ((error= table->file->index_read_map(table->record[0], m_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_map(table->record[0], m_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { DBUG_PRINT("info",("no record matching the key found in the table")); if (error == HA_ERR_RECORD_DELETED) @@ -2436,7 +2437,7 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli) 256U - (1U << table->s->last_null_bit_pos); } - while ((error= table->file->index_next(table->record[0]))) + while ((error= table->file->ha_index_next(table->record[0]))) { /* We just skip records that has already been deleted */ if (error == HA_ERR_RECORD_DELETED) @@ -2472,7 +2473,7 @@ int Old_rows_log_event::find_row(const Relay_log_info *rli) do { restart_rnd_next: - error= table->file->rnd_next(table->record[0]); + error= table->file->ha_rnd_next(table->record[0]); switch (error) { diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 499d7d2fc24..c178f348206 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -28,6 +28,16 @@ #ifndef MYSQL_CLIENT +/* + the following #define adds server-only members to enum_mysql_show_type, + that is defined in mysql/plugin.h + it has to be before mysql/plugin.h is included. +*/ +#define SHOW_always_last SHOW_KEY_CACHE_LONG, \ + SHOW_KEY_CACHE_LONGLONG, SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, \ + SHOW_HAVE, SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, \ + SHOW_LONG_NOFLUSH, SHOW_LONGLONG_STATUS + #include <my_global.h> #include <mysql_version.h> #include <mysql_embed.h> @@ -92,12 +102,16 @@ extern MYSQL_PLUGIN_IMPORT const char *primary_key_name; #include "unireg.h" void init_sql_alloc(MEM_ROOT *root, uint block_size, uint pre_alloc_size); +#endif // MYSQL_CLIENT + void *sql_alloc(size_t); void *sql_calloc(size_t); char *sql_strdup(const char *str); char *sql_strmake(const char *str, size_t len); void *sql_memdup(const void * ptr, size_t size); void sql_element_free(void *ptr); + +#ifndef MYSQL_CLIENT char *sql_strmake_with_convert(const char *str, size_t arg_length, CHARSET_INFO *from_cs, size_t max_res_length, @@ -506,7 +520,7 @@ protected: #define MODE_PIPES_AS_CONCAT 2 #define MODE_ANSI_QUOTES 4 #define MODE_IGNORE_SPACE 8 -#define MODE_NOT_USED 16 +#define MODE_IGNORE_BAD_TABLE_OPTIONS 16 #define MODE_ONLY_FULL_GROUP_BY 32 #define MODE_NO_UNSIGNED_SUBTRACTION 64 #define MODE_NO_DIR_IN_CREATE 128 @@ -1053,9 +1067,6 @@ int write_bin_log(THD *thd, bool clear_error, char const *query, ulong query_length); /* sql_connect.cc */ -int check_user(THD *thd, enum enum_server_command command, - const char *passwd, uint passwd_len, const char *db, - bool check_count); pthread_handler_t handle_one_connection(void *arg); bool init_new_connection_handler_thread(); void reset_mqh(LEX_USER *lu, bool get_them); @@ -1067,6 +1078,10 @@ bool setup_connection_thread_globals(THD *thd); bool login_connection(THD *thd); void end_connection(THD *thd); void prepare_new_connection_state(THD* thd); +void update_global_user_stats(THD* thd, bool create_user, time_t now); +int get_or_create_user_conn(THD *thd, const char *user, + const char *host, USER_RESOURCES *mqh); +int check_for_max_user_connections(THD *thd, USER_CONN *uc); int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create, bool silent); bool mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create); @@ -1103,14 +1118,22 @@ bool is_update_query(enum enum_sql_command command); bool is_log_table_write_query(enum enum_sql_command command); bool alloc_query(THD *thd, const char *packet, uint packet_length); void mysql_init_select(LEX *lex); -void mysql_reset_thd_for_next_command(THD *thd); +void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat); bool mysql_new_select(LEX *lex, bool move_down); void create_select_for_variable(const char *var_name); void mysql_init_multi_delete(LEX *lex); bool multi_delete_set_locks_and_link_aux_tables(LEX *lex); void init_max_user_conn(void); void init_update_queries(void); +void init_global_user_stats(void); +void init_global_table_stats(void); +void init_global_index_stats(void); +void init_global_client_stats(void); void free_max_user_conn(void); +void free_global_user_stats(void); +void free_global_table_stats(void); +void free_global_index_stats(void); +void free_global_client_stats(void); pthread_handler_t handle_bootstrap(void *arg); int mysql_execute_command(THD *thd); bool do_command(THD *thd); @@ -1353,6 +1376,7 @@ find_field_in_table(THD *thd, TABLE *table, const char *name, uint length, bool allow_rowid, uint *cached_field_index_ptr); Field * find_field_in_table_sef(TABLE *table, const char *name); +int update_virtual_fields(TABLE *table, bool ignore_stored= FALSE); #endif /* MYSQL_SERVER */ @@ -1483,14 +1507,17 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum enum_field_types t LEX_STRING *comment, char *change, List<String> *interval_list, CHARSET_INFO *cs, - uint uint_geom_type); + uint uint_geom_type, + Virtual_column_info *vcol_info, + engine_option_value *create_options); Create_field * new_create_field(THD *thd, char *field_name, enum_field_types type, char *length, char *decimals, uint type_modifier, Item *default_value, Item *on_update_value, LEX_STRING *comment, char *change, List<String> *interval_list, CHARSET_INFO *cs, - uint uint_geom_type); + uint uint_geom_type, + Virtual_column_info *vcol_info); void store_position_for_column(const char *name); bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc); bool push_new_name_resolution_context(THD *thd, @@ -1981,6 +2008,7 @@ extern ulong max_connect_errors, connect_timeout; extern ulong extra_max_connections; extern ulong slave_net_timeout, slave_trans_retries; extern uint max_user_connections; +extern ulonglong denied_connections; extern ulong what_to_log,flush_time; extern ulong query_buff_size; extern ulong max_prepared_stmt_count, prepared_stmt_count; @@ -2034,6 +2062,7 @@ extern my_bool opt_safe_show_db, opt_local_infile, opt_myisam_use_mmap; extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern ulong slave_exec_mode_options; extern my_bool opt_readonly, lower_case_file_system; +extern my_bool opt_userstat_running; extern my_bool opt_enable_named_pipe, opt_sync_frm, opt_allow_suspicious_udfs; extern my_bool opt_secure_auth; extern char* opt_secure_file_priv; @@ -2074,6 +2103,11 @@ extern pthread_mutex_t LOCK_des_key_file; #endif extern pthread_mutex_t LOCK_server_started; extern pthread_cond_t COND_server_started; +extern pthread_mutex_t LOCK_global_user_client_stats; +extern pthread_mutex_t LOCK_global_table_stats; +extern pthread_mutex_t LOCK_global_index_stats; +extern pthread_mutex_t LOCK_stats; + extern int mysqld_server_started; extern rw_lock_t LOCK_grant, LOCK_sys_init_connect, LOCK_sys_init_slave; extern rw_lock_t LOCK_system_variables_hash; @@ -2100,6 +2134,11 @@ extern KNOWN_DATE_TIME_FORMAT known_date_time_formats[]; extern String null_string; extern HASH open_cache, lock_db_cache; +extern HASH global_user_stats; +extern HASH global_client_stats; +extern HASH global_table_stats; +extern HASH global_index_stats; + extern TABLE *unused_tables; extern const char* any_db; extern struct my_option my_long_options[]; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 55a889b55af..e0d2c4291e3 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -23,6 +23,7 @@ #include "rpl_mi.h" #include "sql_repl.h" #include "rpl_filter.h" +#include "client_settings.h" #include "repl_failsafe.h" #include <my_stacktrace.h> #include "mysqld_suffix.h" @@ -244,7 +245,7 @@ const char *show_comp_option_name[]= {"YES", "NO", "DISABLED"}; static const char *sql_mode_names[]= { "REAL_AS_FLOAT", "PIPES_AS_CONCAT", "ANSI_QUOTES", "IGNORE_SPACE", - "?", "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION", + "IGNORE_BAD_TABLE_OPTIONS", "ONLY_FULL_GROUP_BY", "NO_UNSIGNED_SUBTRACTION", "NO_DIR_IN_CREATE", "POSTGRESQL", "ORACLE", "MSSQL", "DB2", "MAXDB", "NO_KEY_OPTIONS", "NO_TABLE_OPTIONS", "NO_FIELD_OPTIONS", "MYSQL323", "MYSQL40", "ANSI", @@ -264,7 +265,7 @@ static const unsigned int sql_mode_names_len[]= /*PIPES_AS_CONCAT*/ 15, /*ANSI_QUOTES*/ 11, /*IGNORE_SPACE*/ 12, - /*?*/ 1, + /*IGNORE_BAD_TABLE_OPTIONS*/ 24, /*ONLY_FULL_GROUP_BY*/ 18, /*NO_UNSIGNED_SUBTRACTION*/ 23, /*NO_DIR_IN_CREATE*/ 16, @@ -418,6 +419,7 @@ static pthread_cond_t COND_thread_cache, COND_flush_thread_cache; bool opt_update_log, opt_bin_log, opt_ignore_builtin_innodb= 0; my_bool opt_log, opt_slow_log; +my_bool opt_userstat_running; ulong log_output_options; my_bool opt_log_queries_not_using_indexes= 0; bool opt_error_log= IF_WIN(1,0); @@ -553,6 +555,7 @@ ulong binlog_cache_use= 0, binlog_cache_disk_use= 0; ulong max_connections, max_connect_errors; ulong extra_max_connections; uint max_user_connections= 0; +ulonglong denied_connections; /** Limit of the total number of prepared statements in the server. Is necessary to protect the server against out-of-memory attacks. @@ -655,6 +658,9 @@ pthread_mutex_t LOCK_mysql_create_db, LOCK_Acl, LOCK_open, LOCK_thread_count, LOCK_global_system_variables, LOCK_user_conn, LOCK_slave_list, LOCK_active_mi, LOCK_connection_count, LOCK_uuid_generator; +pthread_mutex_t LOCK_stats, LOCK_global_user_client_stats; +pthread_mutex_t LOCK_global_table_stats, LOCK_global_index_stats; + /** The below lock protects access to two global server variables: max_prepared_stmt_count and prepared_stmt_count. These variables @@ -1362,6 +1368,10 @@ void clean_up(bool print_message) x_free(opt_secure_file_priv); bitmap_free(&temp_pool); free_max_user_conn(); + free_global_user_stats(); + free_global_client_stats(); + free_global_table_stats(); + free_global_index_stats(); #ifdef HAVE_REPLICATION end_slave_list(); #endif @@ -1386,6 +1396,7 @@ void clean_up(bool print_message) if (print_message && errmesg && server_start_time) sql_print_information(ER(ER_SHUTDOWN_COMPLETE),my_progname); thread_scheduler.end(); + mysql_library_end(); finish_client_errs(); my_free((uchar*) my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST), MYF(MY_WME | MY_FAE | MY_ALLOW_ZERO_PTR)); @@ -1452,6 +1463,11 @@ static void clean_up_mutexes() (void) pthread_mutex_destroy(&LOCK_bytes_received); (void) pthread_mutex_destroy(&LOCK_user_conn); (void) pthread_mutex_destroy(&LOCK_connection_count); + (void) pthread_mutex_destroy(&LOCK_stats); + (void) pthread_mutex_destroy(&LOCK_global_user_client_stats); + (void) pthread_mutex_destroy(&LOCK_global_table_stats); + (void) pthread_mutex_destroy(&LOCK_global_index_stats); + Events::destroy_mutexes(); #ifdef HAVE_OPENSSL (void) pthread_mutex_destroy(&LOCK_des_key_file); @@ -1494,20 +1510,23 @@ static void clean_up_mutexes() mysys/thr_mutex.c, will give a warning on first wrong mutex usage! */ +#ifdef SAFE_MUTEX +#define always_in_that_order(A,B) \ + pthread_mutex_lock(A); pthread_mutex_lock(B); \ + pthread_mutex_unlock(B); pthread_mutex_unlock(A) +#else +#define always_in_that_order(A,B) +#endif + static void register_mutex_order() { -#ifdef SAFE_MUTEX /* We must have LOCK_open before LOCK_global_system_variables because LOCK_open is hold while sql_plugin.c::intern_sys_var_ptr() is called. */ - pthread_mutex_lock(&LOCK_open); - pthread_mutex_lock(&LOCK_global_system_variables); - - pthread_mutex_unlock(&LOCK_global_system_variables); - pthread_mutex_unlock(&LOCK_open); -#endif + always_in_that_order(&LOCK_open, &LOCK_global_system_variables); } +#undef always_in_that_order /**************************************************************************** @@ -2664,8 +2683,9 @@ bugs.\n"); end: #ifndef __WIN__ - /* On Windows, do not terminate, but pass control to exception filter */ exit(1); +#else + /* On Windows, do not terminate, but pass control to the exception filter */; #endif } @@ -3239,6 +3259,7 @@ SHOW_VAR com_status_vars[]= { {"show_binlog_events", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOG_EVENTS]), SHOW_LONG_STATUS}, {"show_binlogs", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_BINLOGS]), SHOW_LONG_STATUS}, {"show_charsets", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CHARSETS]), SHOW_LONG_STATUS}, + {"show_client_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CLIENT_STATS]), SHOW_LONG_STATUS}, {"show_collations", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLLATIONS]), SHOW_LONG_STATUS}, {"show_column_types", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_COLUMN_TYPES]), SHOW_LONG_STATUS}, {"show_contributors", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_CONTRIBUTORS]), SHOW_LONG_STATUS}, @@ -3261,6 +3282,7 @@ SHOW_VAR com_status_vars[]= { {"show_function_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS_FUNC]), SHOW_LONG_STATUS}, {"show_grants", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_GRANTS]), SHOW_LONG_STATUS}, {"show_keys", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_KEYS]), SHOW_LONG_STATUS}, + {"show_index_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_INDEX_STATS]), SHOW_LONG_STATUS}, {"show_master_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_MASTER_STAT]), SHOW_LONG_STATUS}, {"show_new_master", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_NEW_MASTER]), SHOW_LONG_STATUS}, {"show_open_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_OPEN_TABLES]), SHOW_LONG_STATUS}, @@ -3277,9 +3299,11 @@ SHOW_VAR com_status_vars[]= { {"show_slave_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_SLAVE_STAT]), SHOW_LONG_STATUS}, {"show_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STATUS]), SHOW_LONG_STATUS}, {"show_storage_engines", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_STORAGE_ENGINES]), SHOW_LONG_STATUS}, + {"show_table_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATS]), SHOW_LONG_STATUS}, {"show_table_status", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLE_STATUS]), SHOW_LONG_STATUS}, {"show_tables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TABLES]), SHOW_LONG_STATUS}, {"show_triggers", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_TRIGGERS]), SHOW_LONG_STATUS}, + {"show_user_statistics", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_USER_STATS]), SHOW_LONG_STATUS}, {"show_variables", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_VARIABLES]), SHOW_LONG_STATUS}, {"show_warnings", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SHOW_WARNS]), SHOW_LONG_STATUS}, {"slave_start", (char*) offsetof(STATUS_VAR, com_stat[(uint) SQLCOM_SLAVE_START]), SHOW_LONG_STATUS}, @@ -3476,6 +3500,7 @@ static int init_common_variables(const char *conf_file_name, int argc, if (init_errmessage()) /* Read error messages from file */ return 1; init_client_errs(); + mysql_library_init(never,never,never); /* for replication */ lex_init(); if (item_create_init()) return 1; @@ -3684,6 +3709,12 @@ static int init_thread_environment() (void) pthread_mutex_init(&LOCK_prepared_stmt_count, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_uuid_generator, MY_MUTEX_INIT_FAST); (void) pthread_mutex_init(&LOCK_connection_count, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_stats, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_user_client_stats, + MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_table_stats, MY_MUTEX_INIT_FAST); + (void) pthread_mutex_init(&LOCK_global_index_stats, MY_MUTEX_INIT_FAST); + #ifdef HAVE_OPENSSL (void) pthread_mutex_init(&LOCK_des_key_file,MY_MUTEX_INIT_FAST); #ifndef HAVE_YASSL @@ -4069,6 +4100,9 @@ a file name for --log-bin-index option", opt_binlog_index_name); /* call ha_init_key_cache() on all key caches to init them */ process_key_caches(&ha_init_key_cache); + init_global_table_stats(); + init_global_index_stats(); + /* Allow storage engine to give real error messages */ if (ha_init_errors()) DBUG_RETURN(1); @@ -4276,6 +4310,8 @@ a file name for --log-bin-index option", opt_binlog_index_name); init_max_user_conn(); init_update_queries(); + init_global_user_stats(); + init_global_client_stats(); DBUG_RETURN(0); } @@ -5094,6 +5130,7 @@ static void create_new_thread(THD *thd) DBUG_PRINT("error",("Too many connections")); close_connection(thd, ER_CON_COUNT_ERROR, 1); + statistic_increment(denied_connections, &LOCK_status); delete thd; DBUG_VOID_RETURN; } @@ -5803,6 +5840,7 @@ enum options_mysqld OPT_INTERACTIVE_TIMEOUT, OPT_JOIN_BUFF_SIZE, OPT_KEY_BUFFER_SIZE, OPT_KEY_CACHE_BLOCK_SIZE, OPT_KEY_CACHE_DIVISION_LIMIT, OPT_KEY_CACHE_AGE_THRESHOLD, + OPT_KEY_CACHE_PARTITIONS, OPT_LONG_QUERY_TIME, OPT_LOWER_CASE_TABLE_NAMES, OPT_MAX_ALLOWED_PACKET, OPT_MAX_BINLOG_CACHE_SIZE, OPT_MAX_BINLOG_SIZE, @@ -5908,6 +5946,7 @@ enum options_mysqld OPT_LOG_SLOW_RATE_LIMIT, OPT_LOG_SLOW_VERBOSITY, OPT_LOG_SLOW_FILTER, + OPT_USERSTAT, OPT_GENERAL_LOG_FILE, OPT_SLOW_QUERY_LOG_FILE, OPT_IGNORE_BUILTIN_INNODB, @@ -6616,8 +6655,6 @@ Can't be set to 1 if --log-slave-updates is used.", {"shared-memory", OPT_ENABLE_SHARED_MEMORY, "Enable the shared memory.",(uchar**) &opt_enable_shared_memory, (uchar**) &opt_enable_shared_memory, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, -#endif -#ifdef HAVE_SMEM {"shared-memory-base-name",OPT_SHARED_MEMORY_BASE_NAME, "Base name of shared memory.", (uchar**) &shared_memory_base_name, (uchar**) &shared_memory_base_name, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, @@ -6905,6 +6942,12 @@ log and this option does nothing anymore.", (uchar**) 0, 0, (GET_ULONG | GET_ASK_ADDR) , REQUIRED_ARG, 100, 1, 100, 0, 1, 0}, + {"key_cache_partitions", OPT_KEY_CACHE_PARTITIONS, + "The number of partitions in key cache", + (uchar**) &dflt_key_cache_var.param_partitions, + (uchar**) 0, + 0, (GET_ULONG | GET_ASK_ADDR), REQUIRED_ARG, DEFAULT_KEY_CACHE_PARTITIONS, + 0, MAX_KEY_CACHE_PARTITIONS, 0, 1, 0}, {"log-slow-filter", OPT_LOG_SLOW_FILTER, "Log only the queries that followed certain execution plan. Multiple flags allowed in a comma-separated string. [admin, filesort, filesort_on_disk, full_join, full_scan, query_cache, query_cache_miss, tmp_table, tmp_table_on_disk]. Sets log-slow-admin-command to ON", 0, 0, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, QPLAN_ALWAYS_SET, 0, 0}, @@ -7330,6 +7373,10 @@ The minimum value for this variable is 4096.", (uchar**) &max_system_variables.net_wait_timeout, 0, GET_ULONG, REQUIRED_ARG, NET_WAIT_TIMEOUT, 1, IF_WIN(INT_MAX32/1000, LONG_TIMEOUT), 0, 1, 0}, + {"userstat", OPT_USERSTAT, + "Control USER_STATISTICS, CLIENT_STATISTICS, INDEX_STATISTICS and TABLE_STATISTICS running", + (uchar**) &opt_userstat_running, (uchar**) &opt_userstat_running, + 0, GET_BOOL, NO_ARG, 0, 0, 1, 0, 1, 0}, {"binlog-direct-non-transactional-updates", OPT_BINLOG_DIRECT_NON_TRANS_UPDATE, "Causes updates to non-transactional engines using statement format to be written directly to binary log. Before using this option make sure that there are no dependencies between transactional and non-transactional tables such as in the statement INSERT INTO t_myisam SELECT * FROM t_innodb; otherwise, slaves may diverge from the master.", (uchar**) &global_system_variables.binlog_direct_non_trans_update, (uchar**) &max_system_variables.binlog_direct_non_trans_update, 0, GET_BOOL, NO_ARG, 0, @@ -7704,19 +7751,24 @@ static int show_ssl_get_cipher_list(THD *thd, SHOW_VAR *var, char *buff) SHOW_VAR status_vars[]= { {"Aborted_clients", (char*) &aborted_threads, SHOW_LONG}, {"Aborted_connects", (char*) &aborted_connects, SHOW_LONG}, + {"Access_denied_errors", (char*) offsetof(STATUS_VAR, access_denied_errors), SHOW_LONG_STATUS}, {"Binlog_cache_disk_use", (char*) &binlog_cache_disk_use, SHOW_LONG}, {"Binlog_cache_use", (char*) &binlog_cache_use, SHOW_LONG}, + {"Busy_time", (char*) offsetof(STATUS_VAR, busy_time), SHOW_DOUBLE_STATUS}, {"Bytes_received", (char*) offsetof(STATUS_VAR, bytes_received), SHOW_LONGLONG_STATUS}, {"Bytes_sent", (char*) offsetof(STATUS_VAR, bytes_sent), SHOW_LONGLONG_STATUS}, + {"Binlog_bytes_written", (char*) offsetof(STATUS_VAR, binlog_bytes_written), SHOW_LONGLONG_STATUS}, {"Com", (char*) com_status_vars, SHOW_ARRAY}, {"Compression", (char*) &show_net_compression, SHOW_FUNC}, {"Connections", (char*) &thread_id, SHOW_LONG_NOFLUSH}, + {"Cpu_time", (char*) offsetof(STATUS_VAR, cpu_time), SHOW_DOUBLE_STATUS}, {"Created_tmp_disk_tables", (char*) offsetof(STATUS_VAR, created_tmp_disk_tables), SHOW_LONG_STATUS}, {"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG}, {"Created_tmp_tables", (char*) offsetof(STATUS_VAR, created_tmp_tables), SHOW_LONG_STATUS}, {"Delayed_errors", (char*) &delayed_insert_errors, SHOW_LONG}, {"Delayed_insert_threads", (char*) &delayed_insert_threads, SHOW_LONG_NOFLUSH}, {"Delayed_writes", (char*) &delayed_insert_writes, SHOW_LONG}, + {"Empty_queries", (char*) offsetof(STATUS_VAR, empty_queries), SHOW_LONG_STATUS}, {"Flush_commands", (char*) &refresh_version, SHOW_LONG_NOFLUSH}, {"Handler_commit", (char*) offsetof(STATUS_VAR, ha_commit_count), SHOW_LONG_STATUS}, {"Handler_delete", (char*) offsetof(STATUS_VAR, ha_delete_count), SHOW_LONG_STATUS}, @@ -7751,6 +7803,8 @@ SHOW_VAR status_vars[]= { {"Opened_tables", (char*) offsetof(STATUS_VAR, opened_tables), SHOW_LONG_STATUS}, {"Opened_table_definitions", (char*) offsetof(STATUS_VAR, opened_shares), SHOW_LONG_STATUS}, {"Prepared_stmt_count", (char*) &show_prepared_stmt_count, SHOW_FUNC}, + {"Rows_sent", (char*) offsetof(STATUS_VAR, rows_sent), SHOW_LONG_STATUS}, + {"Rows_read", (char*) offsetof(STATUS_VAR, rows_read), SHOW_LONG_STATUS}, #ifdef HAVE_QUERY_CACHE {"Qcache_free_blocks", (char*) &query_cache.free_memory_blocks, SHOW_LONG_NOFLUSH}, {"Qcache_free_memory", (char*) &query_cache.free_memory, SHOW_LONG_NOFLUSH}, @@ -8355,6 +8409,7 @@ mysqld_get_one_option(int optid, } case (int)OPT_REPLICATE_REWRITE_DB: { + /* See also OPT_REWRITE_DB handling in client/mysqlbinlog.cc */ char* key = argument,*p, *val; if (!(p= strstr(argument, "->"))) @@ -8875,6 +8930,7 @@ mysql_getopt_value(const char *keyname, uint key_length, case OPT_KEY_CACHE_BLOCK_SIZE: case OPT_KEY_CACHE_DIVISION_LIMIT: case OPT_KEY_CACHE_AGE_THRESHOLD: + case OPT_KEY_CACHE_PARTITIONS: { KEY_CACHE *key_cache; if (!(key_cache= get_or_create_key_cache(keyname, key_length))) @@ -8892,6 +8948,8 @@ mysql_getopt_value(const char *keyname, uint key_length, return (uchar**) &key_cache->param_division_limit; case OPT_KEY_CACHE_AGE_THRESHOLD: return (uchar**) &key_cache->param_age_threshold; + case OPT_KEY_CACHE_PARTITIONS: + return (uchar**) &key_cache->param_partitions; } } } @@ -9321,6 +9379,8 @@ void refresh_status(THD *thd) /* Reset thread's status variables */ bzero((uchar*) &thd->status_var, sizeof(thd->status_var)); + bzero((uchar*) &thd->org_status_var, sizeof(thd->org_status_var)); + thd->start_bytes_received= 0; /* Reset some global variables */ reset_status_vars(); diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 30a6c3bb2fc..7d2bbeda090 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -8370,7 +8370,7 @@ int QUICK_ROR_INTERSECT_SELECT::get_next() /* We get here if we got the same row ref in all scans. */ if (need_to_fetch_row) - error= head->file->rnd_pos(head->record[0], last_rowid); + error= head->file->ha_rnd_pos(head->record[0], last_rowid); } while (error == HA_ERR_RECORD_DELETED); DBUG_RETURN(error); } @@ -8436,7 +8436,7 @@ int QUICK_ROR_UNION_SELECT::get_next() cur_rowid= prev_rowid; prev_rowid= tmp; - error= head->file->rnd_pos(quick->record, prev_rowid); + error= head->file->ha_rnd_pos(quick->record, prev_rowid); } while (error == HA_ERR_RECORD_DELETED); DBUG_RETURN(error); } @@ -8661,10 +8661,12 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, key_range start_key, end_key; if (last_range) { - /* Read the next record in the same range with prefix after cur_prefix. */ + /* + Read the next record in the same range with prefix after cur_prefix. + */ DBUG_ASSERT(cur_prefix != 0); - result= file->index_read_map(record, cur_prefix, keypart_map, - HA_READ_AFTER_KEY); + result= file->ha_index_read_map(record, cur_prefix, keypart_map, + HA_READ_AFTER_KEY); if (result || (file->compare_key(file->end_range) <= 0)) DBUG_RETURN(result); } @@ -8720,8 +8722,8 @@ int QUICK_RANGE_SELECT_GEOM::get_next() if (last_range) { // Already read through key - result= file->index_next_same(record, last_range->min_key, - last_range->min_length); + result= file->ha_index_next_same(record, last_range->min_key, + last_range->min_length); if (result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); } @@ -8735,10 +8737,10 @@ int QUICK_RANGE_SELECT_GEOM::get_next() } last_range= *(cur_range++); - result= file->index_read_map(record, last_range->min_key, - last_range->min_keypart_map, - (ha_rkey_function)(last_range->flag ^ - GEOM_FLAG)); + result= file->ha_index_read_map(record, last_range->min_key, + last_range->min_keypart_map, + (ha_rkey_function)(last_range->flag ^ + GEOM_FLAG)); if (result != HA_ERR_KEY_NOT_FOUND && result != HA_ERR_END_OF_FILE) DBUG_RETURN(result); last_range= 0; // Not found, to next range @@ -8850,9 +8852,9 @@ int QUICK_SELECT_DESC::get_next() { // Already read through key result = ((last_range->flag & EQ_RANGE && used_key_parts <= head->key_info[index].key_parts) ? - file->index_next_same(record, last_range->min_key, + file->ha_index_next_same(record, last_range->min_key, last_range->min_length) : - file->index_prev(record)); + file->ha_index_prev(record)); if (!result) { if (cmp_prev(*rev_it.ref()) == 0) @@ -8868,7 +8870,7 @@ int QUICK_SELECT_DESC::get_next() if (last_range->flag & NO_MAX_RANGE) // Read last record { int local_error; - if ((local_error=file->index_last(record))) + if ((local_error= file->ha_index_last(record))) DBUG_RETURN(local_error); // Empty table if (cmp_prev(last_range) == 0) DBUG_RETURN(0); @@ -8880,9 +8882,9 @@ int QUICK_SELECT_DESC::get_next() used_key_parts <= head->key_info[index].key_parts) { - result = file->index_read_map(record, last_range->max_key, - last_range->max_keypart_map, - HA_READ_KEY_EXACT); + result= file->ha_index_read_map(record, last_range->max_key, + last_range->max_keypart_map, + HA_READ_KEY_EXACT); } else { @@ -8890,11 +8892,11 @@ int QUICK_SELECT_DESC::get_next() (last_range->flag & EQ_RANGE && used_key_parts > head->key_info[index].key_parts) || range_reads_after_key(last_range)); - result=file->index_read_map(record, last_range->max_key, - last_range->max_keypart_map, - ((last_range->flag & NEAR_MAX) ? - HA_READ_BEFORE_KEY : - HA_READ_PREFIX_LAST_OR_PREV)); + result= file->ha_index_read_map(record, last_range->max_key, + last_range->max_keypart_map, + ((last_range->flag & NEAR_MAX) ? + HA_READ_BEFORE_KEY : + HA_READ_PREFIX_LAST_OR_PREV)); } if (result) { @@ -10634,7 +10636,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::reset(void) DBUG_RETURN(result); if (quick_prefix_select && quick_prefix_select->reset()) DBUG_RETURN(1); - result= file->index_last(record); + result= file->ha_index_last(record); if (result == HA_ERR_END_OF_FILE) DBUG_RETURN(0); /* Save the prefix of the last group. */ @@ -10736,9 +10738,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::get_next() first sub-group with the extended prefix. */ if (!have_min && !have_max && key_infix_len > 0) - result= file->index_read_map(record, group_prefix, - make_prev_keypart_map(real_key_parts), - HA_READ_KEY_EXACT); + result= file->ha_index_read_map(record, group_prefix, + make_prev_keypart_map(real_key_parts), + HA_READ_KEY_EXACT); result= have_min ? min_res : have_max ? max_res : result; } while ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) && @@ -10800,9 +10802,10 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min() /* Apply the constant equality conditions to the non-group select fields */ if (key_infix_len > 0) { - if ((result= file->index_read_map(record, group_prefix, - make_prev_keypart_map(real_key_parts), - HA_READ_KEY_EXACT))) + if ((result= + file->ha_index_read_map(record, group_prefix, + make_prev_keypart_map(real_key_parts), + HA_READ_KEY_EXACT))) DBUG_RETURN(result); } @@ -10817,9 +10820,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min() { /* Find the first subsequent record without NULL in the MIN/MAX field. */ key_copy(tmp_record, record, index_info, 0); - result= file->index_read_map(record, tmp_record, - make_keypart_map(real_key_parts), - HA_READ_AFTER_KEY); + result= file->ha_index_read_map(record, tmp_record, + make_keypart_map(real_key_parts), + HA_READ_AFTER_KEY); /* Check if the new record belongs to the current group by comparing its prefix with the group's prefix. If it is from the next group, then the @@ -10874,9 +10877,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max() if (min_max_ranges.elements > 0) result= next_max_in_range(); else - result= file->index_read_map(record, group_prefix, - make_prev_keypart_map(real_key_parts), - HA_READ_PREFIX_LAST); + result= file->ha_index_read_map(record, group_prefix, + make_prev_keypart_map(real_key_parts), + HA_READ_PREFIX_LAST); DBUG_RETURN(result); } @@ -10919,7 +10922,7 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix() { if (!seen_first_key) { - result= file->index_first(record); + result= file->ha_index_first(record); if (result) DBUG_RETURN(result); seen_first_key= TRUE; @@ -10927,9 +10930,9 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_prefix() else { /* Load the first key in this group into record. */ - result= file->index_read_map(record, group_prefix, - make_prev_keypart_map(group_key_parts), - HA_READ_AFTER_KEY); + result= file->ha_index_read_map(record, group_prefix, + make_prev_keypart_map(group_key_parts), + HA_READ_AFTER_KEY); if (result) DBUG_RETURN(result); } @@ -11006,7 +11009,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_min_in_range() HA_READ_AFTER_KEY : HA_READ_KEY_OR_NEXT; } - result= file->index_read_map(record, group_prefix, keypart_map, find_flag); + result= file->ha_index_read_map(record, group_prefix, keypart_map, + find_flag); if (result) { if ((result == HA_ERR_KEY_NOT_FOUND || result == HA_ERR_END_OF_FILE) && @@ -11145,7 +11149,8 @@ int QUICK_GROUP_MIN_MAX_SELECT::next_max_in_range() HA_READ_BEFORE_KEY : HA_READ_PREFIX_LAST_OR_PREV; } - result= file->index_read_map(record, group_prefix, keypart_map, find_flag); + result= file->ha_index_read_map(record, group_prefix, keypart_map, + find_flag); if (result) { diff --git a/sql/opt_range.h b/sql/opt_range.h index 6a0234e62fe..6f7ef7842e7 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -727,7 +727,7 @@ public: ~FT_SELECT() { file->ft_end(); } int init() { return error=file->ft_init(); } int reset() { return 0; } - int get_next() { return error=file->ft_read(record); } + int get_next() { return error= file->ha_ft_read(record); } int get_type() { return QS_TYPE_FULLTEXT; } }; diff --git a/sql/opt_sum.cc b/sql/opt_sum.cc index 87ef3af6e44..cefb507a61e 100644 --- a/sql/opt_sum.cc +++ b/sql/opt_sum.cc @@ -255,7 +255,7 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) error= table->file->ha_index_init((uint) ref.key, 1); if (!ref.key_length) - error= table->file->index_first(table->record[0]); + error= table->file->ha_index_first(table->record[0]); else { /* @@ -277,10 +277,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) Closed interval: Either The MIN argument is non-nullable, or we have a >= predicate for the MIN argument. */ - error= table->file->index_read_map(table->record[0], - ref.key_buff, - make_prev_keypart_map(ref.key_parts), - HA_READ_KEY_OR_NEXT); + error= table->file->ha_index_read_map(table->record[0], + ref.key_buff, + make_prev_keypart_map(ref.key_parts), + HA_READ_KEY_OR_NEXT); else { /* @@ -289,10 +289,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) 2) there is a > predicate on it, nullability is irrelevant. We need to scan the next bigger record first. */ - error= table->file->index_read_map(table->record[0], - ref.key_buff, - make_prev_keypart_map(ref.key_parts), - HA_READ_AFTER_KEY); + error= table->file->ha_index_read_map(table->record[0], + ref.key_buff, + make_prev_keypart_map(ref.key_parts), + HA_READ_AFTER_KEY); /* If the found record is outside the group formed by the search prefix, or there is no such record at all, check if all @@ -315,10 +315,10 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) key_cmp_if_same(table, ref.key_buff, ref.key, prefix_len))) { DBUG_ASSERT(item_field->field->real_maybe_null()); - error= table->file->index_read_map(table->record[0], - ref.key_buff, - make_prev_keypart_map(ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_map(table->record[0], + ref.key_buff, + make_prev_keypart_map(ref.key_parts), + HA_READ_KEY_EXACT); } } } @@ -403,13 +403,13 @@ int opt_sum_query(TABLE_LIST *tables, List<Item> &all_fields,COND *conds) error= table->file->ha_index_init((uint) ref.key, 1); if (!ref.key_length) - error= table->file->index_last(table->record[0]); + error= table->file->ha_index_last(table->record[0]); else - error= table->file->index_read_map(table->record[0], key_buff, - make_prev_keypart_map(ref.key_parts), - range_fl & NEAR_MAX ? - HA_READ_BEFORE_KEY : - HA_READ_PREFIX_LAST_OR_PREV); + error= table->file->ha_index_read_map(table->record[0], key_buff, + make_prev_keypart_map(ref.key_parts), + range_fl & NEAR_MAX ? + HA_READ_BEFORE_KEY : + HA_READ_PREFIX_LAST_OR_PREV); if (!error && reckey_in_range(1, &ref, item_field->field, conds, range_fl, prefix_len)) error= HA_ERR_KEY_NOT_FOUND; diff --git a/sql/password.c b/sql/password.c index 3c662e0c8f3..0cfa15b15d6 100644 --- a/sql/password.c +++ b/sql/password.c @@ -193,13 +193,13 @@ void scramble_323(char *to, const char *message, const char *password) */ my_bool -check_scramble_323(const char *scrambled, const char *message, +check_scramble_323(const unsigned char *scrambled, const char *message, ulong *hash_pass) { struct my_rnd_struct rand_st; ulong hash_message[2]; - char buff[16],*to,extra; /* Big enough for check */ - const char *pos; + uchar buff[16],*to,extra; /* Big enough for check */ + const uchar *pos; hash_password(hash_message, message, SCRAMBLE_LENGTH_323); my_rnd_init(&rand_st,hash_pass[0] ^ hash_message[0], @@ -214,7 +214,7 @@ check_scramble_323(const char *scrambled, const char *message, to=buff; while (*scrambled) { - if (*scrambled++ != (char) (*to++ ^ extra)) + if (*scrambled++ != (uchar) (*to++ ^ extra)) return 1; /* Wrong password */ } return 0; @@ -481,7 +481,7 @@ scramble(char *to, const char *message, const char *password) */ my_bool -check_scramble(const char *scramble_arg, const char *message, +check_scramble(const uchar *scramble_arg, const char *message, const uint8 *hash_stage2) { SHA1_CONTEXT sha1_context; @@ -494,7 +494,7 @@ check_scramble(const char *scramble_arg, const char *message, mysql_sha1_input(&sha1_context, hash_stage2, SHA1_HASH_SIZE); mysql_sha1_result(&sha1_context, buf); /* encrypt scramble */ - my_crypt((char *) buf, buf, (const uchar *) scramble_arg, SCRAMBLE_LENGTH); + my_crypt((char *) buf, buf, scramble_arg, SCRAMBLE_LENGTH); /* now buf supposedly contains hash_stage1: so we can get hash_stage2 */ mysql_sha1_reset(&sha1_context); mysql_sha1_input(&sha1_context, buf, SHA1_HASH_SIZE); diff --git a/sql/procedure.h b/sql/procedure.h index ceb586766b1..30a8a0efccb 100644 --- a/sql/procedure.h +++ b/sql/procedure.h @@ -42,7 +42,11 @@ public: { init_make_field(tmp_field,field_type()); } - unsigned int size_of() { return sizeof(*this);} + unsigned int size_of() { return sizeof(*this);} + bool check_vcol_func_processor(uchar *int_arg) + { + return trace_unsupported_by_check_vcol_func_processor("proc"); + } }; class Item_proc_real :public Item_proc diff --git a/sql/protocol.cc b/sql/protocol.cc index 11b4c085505..f9bdec1b2ac 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -341,24 +341,6 @@ static bool write_eof_packet(THD *thd, NET *net, } /** - Please client to send scrambled_password in old format. - - @param thd thread handle - - @retval - 0 ok - @retval - !0 error -*/ - -bool send_old_password_request(THD *thd) -{ - NET *net= &thd->net; - return my_net_write(net, eof_buff, 1) || net_flush(net); -} - - -/** @param thd Thread handler @param sql_errno The error code to send @param err A pointer to the error message diff --git a/sql/protocol.h b/sql/protocol.h index 5a043d9c482..3eb5091a7c7 100644 --- a/sql/protocol.h +++ b/sql/protocol.h @@ -177,7 +177,6 @@ public: void send_warning(THD *thd, uint sql_errno, const char *err=0); bool net_send_error(THD *thd, uint sql_errno=0, const char *err=0); void net_end_statement(THD *thd); -bool send_old_password_request(THD *thd); uchar *net_store_data(uchar *to,const uchar *from, size_t length); uchar *net_store_data(uchar *to,int32 from); uchar *net_store_data(uchar *to,longlong from); diff --git a/sql/records.cc b/sql/records.cc index 2fc5a26a210..e2a1ea9b4af 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -327,6 +327,7 @@ static int rr_quick(READ_RECORD *info) break; } } + update_virtual_fields(info->table); return tmp; } @@ -346,7 +347,7 @@ static int rr_quick(READ_RECORD *info) static int rr_index_first(READ_RECORD *info) { - int tmp= info->file->index_first(info->record); + int tmp= info->file->ha_index_first(info->record); info->read_record= rr_index; if (tmp) tmp= rr_handle_error(info, tmp); @@ -372,7 +373,7 @@ static int rr_index_first(READ_RECORD *info) static int rr_index(READ_RECORD *info) { - int tmp= info->file->index_next(info->record); + int tmp= info->file->ha_index_next(info->record); if (tmp) tmp= rr_handle_error(info, tmp); return tmp; @@ -382,7 +383,7 @@ static int rr_index(READ_RECORD *info) int rr_sequential(READ_RECORD *info) { int tmp; - while ((tmp=info->file->rnd_next(info->record))) + while ((tmp= info->file->ha_rnd_next(info->record))) { /* rnd_next can return RECORD_DELETED for MyISAM when one thread is @@ -394,6 +395,8 @@ int rr_sequential(READ_RECORD *info) break; } } + if (!tmp) + update_virtual_fields(info->table); return tmp; } @@ -405,7 +408,7 @@ static int rr_from_tempfile(READ_RECORD *info) { if (my_b_read(info->io_cache,info->ref_pos,info->ref_length)) return -1; /* End of file */ - if (!(tmp=info->file->rnd_pos(info->record,info->ref_pos))) + if (!(tmp= info->file->ha_rnd_pos(info->record,info->ref_pos))) break; /* The following is extremely unlikely to happen */ if (tmp == HA_ERR_RECORD_DELETED || @@ -456,7 +459,7 @@ static int rr_from_pointers(READ_RECORD *info) cache_pos= info->cache_pos; info->cache_pos+= info->ref_length; - if (!(tmp=info->file->rnd_pos(info->record,cache_pos))) + if (!(tmp= info->file->ha_rnd_pos(info->record,cache_pos))) break; /* The following is extremely unlikely to happen */ @@ -589,7 +592,7 @@ static int rr_from_cache(READ_RECORD *info) record=uint3korr(position); position+=3; record_pos=info->cache+record*info->reclength; - if ((error=(int16) info->file->rnd_pos(record_pos,info->ref_pos))) + if ((error=(int16) info->file->ha_rnd_pos(record_pos,info->ref_pos))) { record_pos[info->error_offset]=1; shortstore(record_pos,error); diff --git a/sql/rpl_filter.cc b/sql/rpl_filter.cc index ee3fc358b60..ffae94f733a 100644 --- a/sql/rpl_filter.cc +++ b/sql/rpl_filter.cc @@ -45,6 +45,7 @@ Rpl_filter::~Rpl_filter() } +#ifndef MYSQL_CLIENT /* Returns true if table should be logged/replicated @@ -129,6 +130,7 @@ Rpl_filter::tables_ok(const char* db, TABLE_LIST* tables) !do_table_inited && !wild_do_table_inited); } +#endif /* Checks whether a db matches some do_db and ignore_db rules @@ -520,6 +522,13 @@ Rpl_filter::get_wild_ignore_table(String* str) } +bool +Rpl_filter::rewrite_db_is_empty() +{ + return rewrite_db.is_empty(); +} + + const char* Rpl_filter::get_rewrite_db(const char* db, size_t *new_len) { diff --git a/sql/rpl_filter.h b/sql/rpl_filter.h index ff7e4081bb1..88951600e56 100644 --- a/sql/rpl_filter.h +++ b/sql/rpl_filter.h @@ -42,7 +42,9 @@ public: /* Checks - returns true if ok to replicate/log */ - bool tables_ok(const char* db, TABLE_LIST* tables); +#ifndef MYSQL_CLIENT + bool tables_ok(const char* db, TABLE_LIST *tables); +#endif bool db_ok(const char* db); bool db_ok_with_wild_table(const char *db); @@ -69,6 +71,7 @@ public: void get_wild_do_table(String* str); void get_wild_ignore_table(String* str); + bool rewrite_db_is_empty(); const char* get_rewrite_db(const char* db, size_t *new_len); I_List<i_string>* get_do_db(); diff --git a/sql/set_var.cc b/sql/set_var.cc index bf126fb09e4..8a512b8947b 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -320,15 +320,18 @@ static sys_var_thd_ulong sys_interactive_timeout(&vars, "interactive_timeout", static sys_var_thd_ulong sys_join_buffer_size(&vars, "join_buffer_size", &SV::join_buff_size); static sys_var_key_buffer_size sys_key_buffer_size(&vars, "key_buffer_size"); -static sys_var_key_cache_long sys_key_cache_block_size(&vars, "key_cache_block_size", - offsetof(KEY_CACHE, - param_block_size)); -static sys_var_key_cache_long sys_key_cache_division_limit(&vars, "key_cache_division_limit", - offsetof(KEY_CACHE, - param_division_limit)); -static sys_var_key_cache_long sys_key_cache_age_threshold(&vars, "key_cache_age_threshold", - offsetof(KEY_CACHE, - param_age_threshold)); +static sys_var_key_cache_long sys_key_cache_block_size(&vars, + "key_cache_block_size", + offsetof(KEY_CACHE,param_block_size)); +static sys_var_key_cache_long sys_key_cache_division_limit(&vars, + "key_cache_division_limit", + offsetof(KEY_CACHE, param_division_limit)); +static sys_var_key_cache_long sys_key_cache_age_threshold(&vars, + "key_cache_age_threshold", + offsetof(KEY_CACHE, param_age_threshold)); +static sys_var_key_cache_long sys_key_cache_partitions(&vars, + "key_cache_partitions", + offsetof(KEY_CACHE, param_partitions)); static sys_var_const sys_language(&vars, "language", OPT_GLOBAL, SHOW_CHAR, (uchar*) language); @@ -517,6 +520,9 @@ static sys_var_const sys_protocol_version(&vars, "protocol_version", static sys_var_thd_ulong sys_read_buff_size(&vars, "read_buffer_size", &SV::read_buff_size); static sys_var_opt_readonly sys_readonly(&vars, "read_only", &opt_readonly); +static sys_var_bool_ptr sys_userstat(&vars, "userstat", + &opt_userstat_running); + static sys_var_thd_ulong sys_read_rnd_buff_size(&vars, "read_rnd_buffer_size", &SV::read_rnd_buff_size); static sys_var_thd_ulong sys_div_precincrement(&vars, "div_precision_increment", @@ -2545,7 +2551,15 @@ bool sys_var_key_cache_long::update(THD *thd, set_var *var) pthread_mutex_unlock(&LOCK_global_system_variables); - error= (bool) (ha_resize_key_cache(key_cache)); + if (offset == offsetof(KEY_CACHE, param_block_size)) + error= (bool) (ha_resize_key_cache(key_cache)); + else + if (offset == offsetof(KEY_CACHE, param_division_limit) || + offset == offsetof(KEY_CACHE, param_age_threshold)) + error= (bool) (ha_change_key_cache_param(key_cache)); + else + if (offset == offsetof(KEY_CACHE, param_partitions)) + error= (bool) (ha_repartition_key_cache(key_cache)); pthread_mutex_lock(&LOCK_global_system_variables); key_cache->in_init= 0; @@ -4153,6 +4167,7 @@ static KEY_CACHE *create_key_cache(const char *name, uint length) key_cache->param_block_size= dflt_key_cache_var.param_block_size; key_cache->param_division_limit= dflt_key_cache_var.param_division_limit; key_cache->param_age_threshold= dflt_key_cache_var.param_age_threshold; + key_cache->param_partitions= dflt_key_cache_var.param_partitions; } } DBUG_RETURN(key_cache); diff --git a/sql/set_var.h b/sql/set_var.h index 5b4760589af..5b8fa1358cb 100644 --- a/sql/set_var.h +++ b/sql/set_var.h @@ -1427,6 +1427,7 @@ public: my_free((uchar*) name, MYF(0)); } friend bool process_key_caches(process_key_cache_t func); + friend int fill_key_cache_tables(THD *thd, TABLE_LIST *tables, COND *cond); friend void delete_elements(I_List<NAMED_LIST> *list, void (*free_element)(const char*, uchar*)); }; diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index 093d345ba38..9e8360487a4 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -6207,9 +6207,41 @@ ER_TOO_MANY_CONCURRENT_TRXS WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED eng "Non-ASCII separator arguments are not fully supported" +ER_VCOL_BASED_ON_VCOL + eng "A computed column cannot be based on a computed column" + +ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED + eng "Function or expression is not allowed for column '%s'." + +ER_DATA_CONVERSION_ERROR_FOR_VIRTUAL_COLUMN + eng "Generated value for computed column '%s' cannot be converted to type '%s'." + +ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN + eng "Primary key cannot be defined upon a computed column." + +ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN + eng "Key/Index cannot be defined on a non-stored computed column." + +ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN + eng "Cannot define foreign key with %s clause on a computed column." + +ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN + eng "The value specified for computed column '%s' in table '%s' ignored." + +ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN + eng "'%s' is not yet supported for computed columns." + +ER_CONST_EXPR_IN_VCOL + eng "Constant expression in computed column function is not allowed." + ER_DEBUG_SYNC_TIMEOUT eng "debug sync point wait timed out" ger "Debug Sync Point Wartezeit überschritten" ER_DEBUG_SYNC_HIT_LIMIT eng "debug sync point hit limit reached" ger "Debug Sync Point Hit Limit erreicht" + +ER_UNKNOWN_OPTION + eng "Unknown option '%-.64s'" +ER_BAD_OPTION_VALUE + eng "Incorrect value '%-.64s' for option '%-.64s'" diff --git a/sql/sp.cc b/sql/sp.cc index f0508142557..66a2f703df3 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -147,7 +147,8 @@ TABLE_FIELD_TYPE proc_table_fields[MYSQL_PROC_FIELD_COUNT] = { { C_STRING_WITH_LEN("sql_mode") }, { C_STRING_WITH_LEN("set('REAL_AS_FLOAT','PIPES_AS_CONCAT','ANSI_QUOTES'," - "'IGNORE_SPACE','NOT_USED','ONLY_FULL_GROUP_BY','NO_UNSIGNED_SUBTRACTION'," + "'IGNORE_SPACE','IGNORE_BAD_TABLE_OPTIONS','ONLY_FULL_GROUP_BY'," + "'NO_UNSIGNED_SUBTRACTION'," "'NO_DIR_IN_CREATE','POSTGRESQL','ORACLE','MSSQL','DB2','MAXDB'," "'NO_KEY_OPTIONS','NO_TABLE_OPTIONS','NO_FIELD_OPTIONS','MYSQL323','MYSQL40'," "'ANSI','NO_AUTO_VALUE_ON_ZERO','NO_BACKSLASH_ESCAPES','STRICT_TRANS_TABLES'," @@ -512,8 +513,9 @@ db_find_routine_aux(THD *thd, int type, sp_name *name, TABLE *table) key_copy(key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) DBUG_RETURN(SP_KEY_NOT_FOUND); DBUG_RETURN(SP_OK); @@ -1286,9 +1288,9 @@ sp_drop_db_routines(THD *thd, char *db) ret= SP_OK; table->file->ha_index_init(0, 1); - if (! table->file->index_read_map(table->record[0], - (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr, - (key_part_map)1, HA_READ_KEY_EXACT)) + if (!table->file->ha_index_read_map(table->record[0], + (uchar *) table->field[MYSQL_PROC_FIELD_DB]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT)) { int nxtres; bool deleted= FALSE; @@ -1303,9 +1305,11 @@ sp_drop_db_routines(THD *thd, char *db) nxtres= 0; break; } - } while (! (nxtres= table->file->index_next_same(table->record[0], - (uchar *)table->field[MYSQL_PROC_FIELD_DB]->ptr, - key_len))); + } while (!(nxtres= table->file-> + ha_index_next_same(table->record[0], + (uchar *)table->field[MYSQL_PROC_FIELD_DB]-> + ptr, + key_len))); if (nxtres != HA_ERR_END_OF_FILE) ret= SP_KEY_NOT_FOUND; if (deleted) diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 25cd1d8a9b4..84689b2efb2 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -819,6 +819,8 @@ sp_head::create_result_field(uint field_max_length, const char *field_name, m_return_field_def.interval, field_name ? field_name : (const char *) m_name.str); + field->vcol_info= m_return_field_def.vcol_info; + field->stored_in_db= m_return_field_def.stored_in_db; if (field) field->init(table); @@ -2087,6 +2089,8 @@ sp_head::reset_lex(THD *thd) sublex->dec= NULL; sublex->interval_list.empty(); sublex->type= 0; + sublex->uint_geom_type= 0; + sublex->vcol_info= 0; DBUG_RETURN(FALSE); } @@ -2213,7 +2217,8 @@ sp_head::fill_field_definition(THD *thd, LEX *lex, &lex->interval_list, lex->charset ? lex->charset : thd->variables.collation_database, - lex->uint_geom_type)) + lex->uint_geom_type, + lex->vcol_info, NULL)) return TRUE; if (field_def->interval_list.elements) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index ce91f83ce85..95cee8ab029 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -30,6 +30,8 @@ #include <stdarg.h> #include "sp_head.h" #include "sp.h" +#include <sql_common.h> +#include <mysql/plugin_auth.h> static const TABLE_FIELD_TYPE mysql_db_table_fields[MYSQL_DB_FIELD_COUNT] = { @@ -148,6 +150,87 @@ 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}; +static LEX_STRING native_password_plugin_name= { + C_STRING_WITH_LEN("mysql_native_password") +}; + +static LEX_STRING old_password_plugin_name= { + C_STRING_WITH_LEN("mysql_old_password") +}; + +/// @todo make it configurable +LEX_STRING *default_auth_plugin_name= &native_password_plugin_name; + +static plugin_ref native_password_plugin; +#ifndef EMBEDDED_LIBRARY +static plugin_ref old_password_plugin; +#endif + +/* Classes */ + +struct acl_host_and_ip +{ + char *hostname; + long ip,ip_mask; // Used with masked ip:s +}; + +class ACL_ACCESS { +public: + ulong sort; + ulong access; +}; + +/* ACL_HOST is used if no host is specified */ + +class ACL_HOST :public ACL_ACCESS +{ +public: + acl_host_and_ip host; + char *db; +}; + +class ACL_USER :public ACL_ACCESS +{ +public: + acl_host_and_ip host; + uint hostname_length; + USER_RESOURCES user_resource; + char *user; + uint8 salt[SCRAMBLE_LENGTH+1]; // scrambled password in binary form + 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; + + ACL_USER *copy(MEM_ROOT *root) + { + ACL_USER *dst= (ACL_USER *)alloc_root(root, sizeof(ACL_USER)); + if (!dst) + return 0; + *dst= *this; + dst->user= safe_strdup_root(root, user); + dst->ssl_cipher= safe_strdup_root(root, ssl_cipher); + dst->x509_issuer= safe_strdup_root(root, x509_issuer); + dst->x509_subject= safe_strdup_root(root, x509_subject); + if (plugin.str == native_password_plugin_name.str || + plugin.str == old_password_plugin_name.str) + dst->plugin= plugin; + else + dst->plugin.str= strmake_root(root, plugin.str, plugin.length); + dst->auth_string.str = safe_strdup_root(root, auth_string.str); + dst->host.hostname= safe_strdup_root(root, host.hostname); + return dst; + } +}; + +class ACL_DB :public ACL_ACCESS +{ +public: + acl_host_and_ip host; + char *user,*db; +}; + #ifndef NO_EMBEDDED_ACCESS_CHECKS #define FIRST_NON_YN_FIELD 26 @@ -171,6 +254,24 @@ static uchar* acl_entry_get_key(acl_entry *entry, size_t *length, #define IP_ADDR_STRLEN (3+1+3+1+3+1+3) #define ACL_KEY_LENGTH (IP_ADDR_STRLEN+1+NAME_LEN+1+USERNAME_LENGTH+1) +#if defined(HAVE_OPENSSL) +/* + Without SSL the handshake consists of one packet. This packet + has both client capabilites and scrambled password. + With SSL the handshake might consist of two packets. If the first + packet (client capabilities) has CLIENT_SSL flag set, we have to + switch to SSL and read the second packet. The scrambled password + is in the second packet and client_capabilites field will be ignored. + Maybe it is better to accept flags other than CLIENT_SSL from the + second packet? +*/ +#define SSL_HANDSHAKE_SIZE 2 +#define NORMAL_HANDSHAKE_SIZE 6 +#define MIN_HANDSHAKE_SIZE 2 +#else +#define MIN_HANDSHAKE_SIZE 6 +#endif /* HAVE_OPENSSL && !EMBEDDED_LIBRARY */ + static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs; static MEM_ROOT mem, memex; static bool initialized=0; @@ -186,9 +287,8 @@ static void init_check_host(void); static void rebuild_check_host(void); static ACL_USER *find_acl_user(const char *host, const char *user, my_bool exact); -static bool update_user_table(THD *thd, TABLE *table, - const char *host, const char *user, - const char *new_password, uint new_password_len); +static bool update_user_table(THD *, TABLE *, const char *, const char *, + const char *, uint); static void update_hostname(acl_host_and_ip *host, const char *hostname); static bool compare_hostname(const acl_host_and_ip *host,const char *hostname, const char *ip); @@ -218,6 +318,35 @@ set_user_salt(ACL_USER *acl_user, const char *password, uint password_len) acl_user->salt_len= 0; } +/** + Fix ACL::plugin pointer to point to a hard-coded string, if appropriate + + Make sure that if ACL_USER's plugin is a built-in, then it points + to a hard coded string, not to an allocated copy. Run-time, for + authentication, we want to be able to detect built-ins by comparing + pointers, not strings. + + Additionally - update the salt if the plugin is built-in. + + @retval 0 the pointers were fixed + @retval 1 this ACL_USER uses a not built-in plugin +*/ +static bool fix_user_plugin_ptr(ACL_USER *user) +{ + if (my_strcasecmp(system_charset_info, user->plugin.str, + native_password_plugin_name.str) == 0) + user->plugin= native_password_plugin_name; + else + if (my_strcasecmp(system_charset_info, user->plugin.str, + old_password_plugin_name.str) == 0) + user->plugin= old_password_plugin_name; + else + return true; + + set_user_salt(user, user->auth_string.str, user->auth_string.length); + return false; +} + /* This after_update function is used when user.password is less than SCRAMBLE_LENGTH bytes. @@ -265,6 +394,19 @@ my_bool acl_init(bool dont_read_acl_tables) (hash_get_key) acl_entry_get_key, (hash_free_key) free, &my_charset_utf8_bin); + + /* + cache built-in native authentication plugins, + to avoid hash searches and a global mutex lock on every connect + */ + native_password_plugin= my_plugin_lock_by_name(0, + &native_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN); + old_password_plugin= my_plugin_lock_by_name(0, + &old_password_plugin_name, MYSQL_AUTHENTICATION_PLUGIN); + + if (!native_password_plugin || !old_password_plugin) + DBUG_RETURN(1); + if (dont_read_acl_tables) { DBUG_RETURN(0); /* purecov: tested */ @@ -422,6 +564,10 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) while (!(read_record_info.read_record(&read_record_info))) { ACL_USER user; + char *password; + uint password_len; + + bzero(&user, sizeof(user)); update_hostname(&user.host, get_field(&mem, table->field[0])); user.user= get_field(&mem, table->field[1]); if (check_no_resolve && hostname_requires_resolving(user.host.hostname)) @@ -433,27 +579,34 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) continue; } - const char *password= get_field(thd->mem_root, table->field[2]); - uint password_len= password ? strlen(password) : 0; + password= get_field(&mem, table->field[2]); + password_len= password ? strlen(password) : 0; + user.auth_string.str= password ? password : const_cast<char*>(""); + user.auth_string.length= password_len; set_user_salt(&user, password, password_len); - if (user.salt_len == 0 && password_len != 0) - { - switch (password_len) { - case 45: /* 4.1: to be removed */ - sql_print_warning("Found 4.1 style password for user '%s@%s'. " - "Ignoring user. " - "You should change password for this user.", - user.user ? user.user : "", - user.host.hostname ? user.host.hostname : ""); - break; - default: - sql_print_warning("Found invalid password for user: '%s@%s'; " - "Ignoring user", user.user ? user.user : "", - user.host.hostname ? user.host.hostname : ""); - break; - } + + switch (password_len) { + case 0: /* no password */ + case SCRAMBLED_PASSWORD_CHAR_LENGTH: + user.plugin= native_password_plugin_name; + break; + case SCRAMBLED_PASSWORD_CHAR_LENGTH_323: + user.plugin= old_password_plugin_name; + break; + case 45: /* 4.1: to be removed */ + sql_print_warning("Found 4.1.0 style password for user '%s@%s'. " + "Ignoring user. " + "You should change password for this user.", + user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); + continue; + default: + sql_print_warning("Found invalid password for user: '%s@%s'; " + "Ignoring user", user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); + continue; } - else // password is correct + { uint next_field; user.access= get_access(table,3,&next_field) & GLOBAL_ACLS; @@ -530,13 +683,35 @@ static my_bool acl_load(THD *thd, TABLE_LIST *tables) ptr= get_field(thd->mem_root, table->field[next_field++]); user.user_resource.user_conn= ptr ? atoi(ptr) : 0; } - else - user.user_resource.user_conn= 0; + + if (table->s->fields >= 41) + { + /* We may have plugin & auth_String fields */ + char *tmpstr= get_field(&mem, table->field[next_field++]); + if (tmpstr) + { + user.plugin.str= tmpstr; + user.plugin.length= strlen(user.plugin.str); + if (user.auth_string.length) + { + sql_print_warning("'user' entry '%s@%s' has both a password " + "and an authentication plugin specified. The " + "password will be ignored.", + user.user ? user.user : "", + user.host.hostname ? user.host.hostname : ""); + } + user.auth_string.str= get_field(&mem, table->field[next_field++]); + if (!user.auth_string.str) + user.auth_string.str= const_cast<char*>(""); + user.auth_string.length= strlen(user.auth_string.str); + + fix_user_plugin_ptr(&user); + } + } } else { user.ssl_type=SSL_TYPE_NONE; - bzero((char *)&(user.user_resource),sizeof(user.user_resource)); #ifndef TO_BE_REMOVED if (table->s->fields <= 13) { // Without grant @@ -639,6 +814,8 @@ void acl_free(bool end) delete_dynamic(&acl_dbs); delete_dynamic(&acl_wild_hosts); hash_free(&acl_check_hosts); + plugin_unlock(0, native_password_plugin); + plugin_unlock(0, old_password_plugin); if (!end) acl_cache->clear(1); /* purecov: inspected */ else @@ -841,246 +1018,10 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) /* - Seek ACL entry for a user, check password, SSL cypher, and if - everything is OK, update THD user data and USER_RESOURCES struct. - - IMPLEMENTATION - This function does not check if the user has any sensible privileges: - only user's existence and validity is checked. - Note, that entire operation is protected by acl_cache_lock. + Gets user credentials without authentication and resource limit checks. SYNOPSIS acl_getroot() - thd thread handle. If all checks are OK, - thd->security_ctx->priv_user/master_access are updated. - thd->security_ctx->host/ip/user are used for checks. - mqh user resources; on success mqh is reset, else - unchanged - passwd scrambled & crypted password, received from client - (to check): thd->scramble or thd->scramble_323 is - used to decrypt passwd, so they must contain - original random string, - passwd_len length of passwd, must be one of 0, 8, - SCRAMBLE_LENGTH_323, SCRAMBLE_LENGTH - 'thd' and 'mqh' are updated on success; other params are IN. - - RETURN VALUE - 0 success: thd->priv_user, thd->priv_host, thd->master_access, mqh are - updated - 1 user not found or authentication failure - 2 user found, has long (4.1.1) salt, but passwd is in old (3.23) format. - -1 user found, has short (3.23) salt, but passwd is in new (4.1.1) format. -*/ - -int acl_getroot(THD *thd, USER_RESOURCES *mqh, - const char *passwd, uint passwd_len) -{ - ulong user_access= NO_ACCESS; - int res= 1; - ACL_USER *acl_user= 0; - Security_context *sctx= thd->security_ctx; - DBUG_ENTER("acl_getroot"); - - if (!initialized) - { - /* - here if mysqld's been started with --skip-grant-tables option. - */ - sctx->skip_grants(); - bzero((char*) mqh, sizeof(*mqh)); - DBUG_RETURN(0); - } - - VOID(pthread_mutex_lock(&acl_cache->lock)); - - /* - Find acl entry in user database. Note, that find_acl_user is not the same, - because it doesn't take into account the case when user is not empty, - but acl_user->user is empty - */ - - for (uint i=0 ; i < acl_users.elements ; i++) - { - ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*); - if (!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) - { - if (compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip)) - { - /* check password: it should be empty or valid */ - if (passwd_len == acl_user_tmp->salt_len) - { - if (acl_user_tmp->salt_len == 0 || - (acl_user_tmp->salt_len == SCRAMBLE_LENGTH ? - check_scramble(passwd, thd->scramble, acl_user_tmp->salt) : - check_scramble_323(passwd, thd->scramble, - (ulong *) acl_user_tmp->salt)) == 0) - { - acl_user= acl_user_tmp; - res= 0; - } - } - else if (passwd_len == SCRAMBLE_LENGTH && - acl_user_tmp->salt_len == SCRAMBLE_LENGTH_323) - res= -1; - else if (passwd_len == SCRAMBLE_LENGTH_323 && - acl_user_tmp->salt_len == SCRAMBLE_LENGTH) - res= 2; - /* linear search complete: */ - break; - } - } - } - /* - This was moved to separate tree because of heavy HAVE_OPENSSL case. - If acl_user is not null, res is 0. - */ - - if (acl_user) - { - /* OK. User found and password checked continue validation */ -#ifdef HAVE_OPENSSL - Vio *vio=thd->net.vio; - SSL *ssl= (SSL*) vio->ssl_arg; - X509 *cert; -#endif - - /* - At this point we know that user is allowed to connect - from given host by given username/password pair. Now - we check if SSL is required, if user is using SSL and - if X509 certificate attributes are OK - */ - switch (acl_user->ssl_type) { - case SSL_TYPE_NOT_SPECIFIED: // Impossible - case SSL_TYPE_NONE: // SSL is not required - user_access= acl_user->access; - break; -#ifdef HAVE_OPENSSL - case SSL_TYPE_ANY: // Any kind of SSL is ok - if (vio_type(vio) == VIO_TYPE_SSL) - user_access= acl_user->access; - break; - case SSL_TYPE_X509: /* Client should have any valid certificate. */ - /* - Connections with non-valid certificates are dropped already - in sslaccept() anyway, so we do not check validity here. - - We need to check for absence of SSL because without SSL - we should reject connection. - */ - if (vio_type(vio) == VIO_TYPE_SSL && - SSL_get_verify_result(ssl) == X509_V_OK && - (cert= SSL_get_peer_certificate(ssl))) - { - user_access= acl_user->access; - X509_free(cert); - } - break; - case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ - /* - We do not check for absence of SSL because without SSL it does - not pass all checks here anyway. - If cipher name is specified, we compare it to actual cipher in - use. - */ - if (vio_type(vio) != VIO_TYPE_SSL || - SSL_get_verify_result(ssl) != X509_V_OK) - break; - if (acl_user->ssl_cipher) - { - DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", - acl_user->ssl_cipher,SSL_get_cipher(ssl))); - if (!strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl))) - user_access= acl_user->access; - else - { - if (global_system_variables.log_warnings) - sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'", - acl_user->ssl_cipher, - SSL_get_cipher(ssl)); - break; - } - } - /* Prepare certificate (if exists) */ - DBUG_PRINT("info",("checkpoint 1")); - if (!(cert= SSL_get_peer_certificate(ssl))) - { - user_access=NO_ACCESS; - break; - } - DBUG_PRINT("info",("checkpoint 2")); - /* If X509 issuer is specified, we check it... */ - if (acl_user->x509_issuer) - { - DBUG_PRINT("info",("checkpoint 3")); - char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); - DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", - acl_user->x509_issuer, ptr)); - if (strcmp(acl_user->x509_issuer, ptr)) - { - if (global_system_variables.log_warnings) - sql_print_information("X509 issuer mismatch: should be '%s' " - "but is '%s'", acl_user->x509_issuer, ptr); - free(ptr); - X509_free(cert); - user_access=NO_ACCESS; - break; - } - user_access= acl_user->access; - free(ptr); - } - DBUG_PRINT("info",("checkpoint 4")); - /* X509 subject is specified, we check it .. */ - if (acl_user->x509_subject) - { - char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); - DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", - acl_user->x509_subject, ptr)); - if (strcmp(acl_user->x509_subject,ptr)) - { - if (global_system_variables.log_warnings) - sql_print_information("X509 subject mismatch: should be '%s' but is '%s'", - acl_user->x509_subject, ptr); - free(ptr); - X509_free(cert); - user_access=NO_ACCESS; - break; - } - user_access= acl_user->access; - free(ptr); - } - /* Deallocate the X509 certificate. */ - X509_free(cert); - break; -#else /* HAVE_OPENSSL */ - default: - /* - If we don't have SSL but SSL is required for this user the - authentication should fail. - */ - break; -#endif /* HAVE_OPENSSL */ - } - sctx->master_access= user_access; - sctx->priv_user= acl_user->user ? sctx->user : (char *) ""; - *mqh= acl_user->user_resource; - - if (acl_user->host.hostname) - strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1); - else - *sctx->priv_host= 0; - } - VOID(pthread_mutex_unlock(&acl_cache->lock)); - DBUG_RETURN(res); -} - - -/* - This is like acl_getroot() above, but it doesn't check password, - and we don't care about the user resources. - - SYNOPSIS - acl_getroot_no_password() sctx Context which should be initialized user user name host host name @@ -1092,13 +1033,13 @@ int acl_getroot(THD *thd, USER_RESOURCES *mqh, TRUE Error */ -bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, - char *ip, char *db) +bool acl_getroot(Security_context *sctx, char *user, char *host, + char *ip, char *db) { int res= 1; uint i; ACL_USER *acl_user= 0; - DBUG_ENTER("acl_getroot_no_password"); + DBUG_ENTER("acl_getroot"); DBUG_PRINT("enter", ("Host: '%s', Ip: '%s', User: '%s', db: '%s'", (host ? host : "(NULL)"), (ip ? ip : "(NULL)"), @@ -1121,8 +1062,7 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, sctx->master_access= 0; sctx->db_access= 0; - sctx->priv_user= (char *) ""; - *sctx->priv_host= 0; + *sctx->priv_user= *sctx->priv_host= 0; /* Find acl entry in user database. @@ -1164,7 +1104,11 @@ bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, } } sctx->master_access= acl_user->access; - sctx->priv_user= acl_user->user ? user : (char *) ""; + + if (acl_user->user) + strmake(sctx->priv_user, user, USERNAME_LENGTH); + else + *sctx->priv_user= 0; if (acl_user->host.hostname) strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME - 1); @@ -1190,7 +1134,9 @@ static void acl_update_user(const char *user, const char *host, const char *x509_issuer, const char *x509_subject, USER_RESOURCES *mqh, - ulong privileges) + ulong privileges, + const LEX_STRING *plugin, + const LEX_STRING *auth) { safe_mutex_assert_owner(&acl_cache->lock); @@ -1204,6 +1150,22 @@ static void acl_update_user(const char *user, const char *host, (acl_user->host.hostname && !my_strcasecmp(system_charset_info, host, acl_user->host.hostname))) { + if (plugin->str[0]) + { + acl_user->plugin= *plugin; + acl_user->auth_string.str= auth->str ? + strmake_root(&mem, auth->str, auth->length) : const_cast<char*>(""); + acl_user->auth_string.length= auth->length; + if (fix_user_plugin_ptr(acl_user)) + acl_user->plugin.str= strmake_root(&mem, plugin->str, plugin->length); + } + else + if (password) + { + acl_user->auth_string.str= strmake_root(&mem, password, password_len); + acl_user->auth_string.length= password_len; + set_user_salt(acl_user, password, password_len); + } acl_user->access=privileges; if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR) acl_user->user_resource.questions=mqh->questions; @@ -1223,8 +1185,6 @@ static void acl_update_user(const char *user, const char *host, acl_user->x509_subject= (x509_subject ? strdup_root(&mem,x509_subject) : 0); } - if (password) - set_user_salt(acl_user, password, password_len); /* search complete: */ break; } @@ -1240,7 +1200,9 @@ static void acl_insert_user(const char *user, const char *host, const char *x509_issuer, const char *x509_subject, USER_RESOURCES *mqh, - ulong privileges) + ulong privileges, + const LEX_STRING *plugin, + const LEX_STRING *auth) { ACL_USER acl_user; @@ -1248,6 +1210,24 @@ static void acl_insert_user(const char *user, const char *host, acl_user.user=*user ? strdup_root(&mem,user) : 0; update_hostname(&acl_user.host, *host ? strdup_root(&mem, host): 0); + if (plugin->str[0]) + { + acl_user.plugin= *plugin; + acl_user.auth_string.str= auth->str ? + strmake_root(&mem, auth->str, auth->length) : const_cast<char*>(""); + acl_user.auth_string.length= auth->length; + if (fix_user_plugin_ptr(&acl_user)) + acl_user.plugin.str= strmake_root(&mem, plugin->str, plugin->length); + } + else + { + acl_user.plugin= password_len == SCRAMBLED_PASSWORD_CHAR_LENGTH_323 ? + old_password_plugin_name : native_password_plugin_name; + acl_user.auth_string.str= strmake_root(&mem, password, password_len); + acl_user.auth_string.length= password_len; + set_user_salt(&acl_user, password, password_len); + } + acl_user.access=privileges; acl_user.user_resource = *mqh; acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user); @@ -1258,8 +1238,6 @@ static void acl_insert_user(const char *user, const char *host, acl_user.x509_issuer= x509_issuer ? strdup_root(&mem,x509_issuer) : 0; acl_user.x509_subject=x509_subject ? strdup_root(&mem,x509_subject) : 0; - set_user_salt(&acl_user, password, password_len); - VOID(push_dynamic(&acl_users,(uchar*) &acl_user)); if (!acl_user.host.hostname || (acl_user.host.hostname[0] == wild_many && !acl_user.host.hostname[1])) @@ -1637,7 +1615,13 @@ bool change_password(THD *thd, const char *host, const char *user, goto end; } /* update loaded acl entry: */ - set_user_salt(acl_user, new_password, new_password_len); + if (acl_user->plugin.str == native_password_plugin_name.str || + acl_user->plugin.str == old_password_plugin_name.str) + { + acl_user->auth_string.str= strmake_root(&mem, new_password, new_password_len); + acl_user->auth_string.length= new_password_len; + set_user_salt(acl_user, new_password, new_password_len); + } if (update_user_table(thd, table, acl_user->host.hostname ? acl_user->host.hostname : "", @@ -1839,9 +1823,9 @@ static bool update_user_table(THD *thd, TABLE *table, key_copy((uchar *) user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, - (uchar *) user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *) user_key, HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0)); /* purecov: deadcode */ @@ -1932,9 +1916,9 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, user_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* what == 'N' means revoke */ if (what == 'N') @@ -1966,6 +1950,15 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, thd->security_ctx->user, thd->security_ctx->host_or_ip); goto end; } + else if (combo.plugin.str[0]) + { + if (!plugin_is_ready(&combo.plugin, MYSQL_AUTHENTICATION_PLUGIN)) + { + my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), combo.plugin.str); + goto end; + } + } + old_row_exists = 0; restore_record(table,s->default_values); table->field[0]->store(combo.host.str,combo.host.length, @@ -1979,7 +1972,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, { old_row_exists = 1; store_record(table,record[1]); // Save copy for update - if (combo.password.str) // If password given + if (combo.password.str) // If password given table->field[2]->store(password, password_len, system_charset_info); else if (!rights && !revoke_grant && lex->ssl_type == SSL_TYPE_NOT_SPECIFIED && @@ -2060,7 +2053,17 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, (mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS)) table->field[next_field+3]->store((longlong) mqh.user_conn, TRUE); mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour; + + next_field+=4; + if (table->s->fields >= 41 && combo.plugin.str[0]) + { + table->field[next_field]->store(combo.plugin.str, combo.plugin.length, + system_charset_info); + table->field[next_field+1]->store(combo.auth.str, combo.auth.length, + system_charset_info); + } } + if (old_row_exists) { /* @@ -2104,7 +2107,9 @@ end: lex->x509_issuer, lex->x509_subject, &lex->mqh, - rights); + rights, + &combo.plugin, + &combo.auth); else acl_insert_user(combo.user.str, combo.host.str, password, password_len, lex->ssl_type, @@ -2112,7 +2117,9 @@ end: lex->x509_issuer, lex->x509_subject, &lex->mqh, - rights); + rights, + &combo.plugin, + &combo.auth); } DBUG_RETURN(error); } @@ -2156,9 +2163,9 @@ static int replace_db_table(TABLE *table, const char *db, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0],0, user_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0],0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { if (what == 'N') { // no row, no revoke @@ -2390,8 +2397,9 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) col_privs->field[4]->store("",0, &my_charset_latin1); col_privs->file->ha_index_init(0, 1); - if (col_privs->file->index_read_map(col_privs->record[0], (uchar*) key, - (key_part_map)15, HA_READ_KEY_EXACT)) + if (col_privs->file->ha_index_read_map(col_privs->record[0], (uchar*) key, + (key_part_map)15, + HA_READ_KEY_EXACT)) { cols = 0; /* purecov: deadcode */ col_privs->file->ha_index_end(); @@ -2417,7 +2425,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) privs= cols= 0; return; } - } while (!col_privs->file->index_next(col_privs->record[0]) && + } while (!col_privs->file->ha_index_next(col_privs->record[0]) && !key_cmp_if_same(col_privs,key,0,key_prefix_len)); col_privs->file->ha_index_end(); } @@ -2561,8 +2569,8 @@ static int replace_column_table(GRANT_TABLE *g_t, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_map(table->record[0], user_key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_map(table->record[0], user_key, + HA_WHOLE_KEY, HA_READ_KEY_EXACT)) { if (revoke_grant) { @@ -2643,9 +2651,9 @@ static int replace_column_table(GRANT_TABLE *g_t, key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - if (table->file->index_read_map(table->record[0], user_key, - (key_part_map)15, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_map(table->record[0], user_key, + (key_part_map)15, + HA_READ_KEY_EXACT)) goto end; /* Scan through all rows with the same host,db,user and table */ @@ -2696,7 +2704,7 @@ static int replace_column_table(GRANT_TABLE *g_t, hash_delete(&g_t->hash_columns,(uchar*) grant_column); } } - } while (!table->file->index_next(table->record[0]) && + } while (!table->file->ha_index_next(table->record[0]) && !key_cmp_if_same(table, key, 0, key_prefix_length)); } @@ -2746,9 +2754,9 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table, key_copy(user_key, table->record[0], table->key_info, table->key_info->key_length); - if (table->file->index_read_idx_map(table->record[0], 0, user_key, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, user_key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* The following should never happen as we first check the in memory @@ -2873,10 +2881,10 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name, TRUE); store_record(table,record[1]); // store at pos 1 - if (table->file->index_read_idx_map(table->record[0], 0, - (uchar*) table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar*) table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* The following should never happen as we first check the in memory @@ -3620,7 +3628,7 @@ static my_bool grant_load_procs_priv(TABLE *p_table) p_table->file->ha_index_init(0, 1); p_table->use_all_columns(); - if (!p_table->file->index_first(p_table->record[0])) + if (!p_table->file->ha_index_first(p_table->record[0])) { memex_ptr= &memex; my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); @@ -3672,7 +3680,7 @@ static my_bool grant_load_procs_priv(TABLE *p_table) goto end_unlock; } } - while (!p_table->file->index_next(p_table->record[0])); + while (!p_table->file->ha_index_next(p_table->record[0])); } /* Return ok */ return_val= 0; @@ -3722,7 +3730,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables) t_table->use_all_columns(); c_table->use_all_columns(); - if (!t_table->file->index_first(t_table->record[0])) + if (!t_table->file->ha_index_first(t_table->record[0])) { memex_ptr= &memex; my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); @@ -3757,7 +3765,7 @@ static my_bool grant_load(THD *thd, TABLE_LIST *tables) goto end_unlock; } } - while (!t_table->file->index_next(t_table->record[0])); + while (!t_table->file->ha_index_next(t_table->record[0])); } return_val=0; // Return ok @@ -4029,6 +4037,8 @@ err: { char command[128]; get_privilege_desc(command, sizeof(command), want_access); + status_var_increment(thd->status_var.access_denied_errors); + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), command, sctx->priv_user, @@ -4442,14 +4452,14 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, ulong get_table_grant(THD *thd, TABLE_LIST *table) { ulong privilege; - Security_context *sctx= thd->security_ctx; - const char *db = table->db ? table->db : thd->db; GRANT_TABLE *grant_table; rw_rdlock(&LOCK_grant); #ifdef EMBEDDED_LIBRARY grant_table= NULL; #else + Security_context *sctx= thd->security_ctx; + const char *db = table->db ? table->db : thd->db; grant_table= table_hash_search(sctx->host, sctx->ip, db, sctx->priv_user, table->table_name, 0); #endif @@ -4642,16 +4652,27 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) global.append(lex_user->host.str,lex_user->host.length, system_charset_info); global.append ('\''); - if (acl_user->salt_len) + if (acl_user->plugin.str == native_password_plugin_name.str || + acl_user->plugin.str == old_password_plugin_name.str) { - char passwd_buff[SCRAMBLED_PASSWORD_CHAR_LENGTH+1]; - if (acl_user->salt_len == SCRAMBLE_LENGTH) - make_password_from_salt(passwd_buff, acl_user->salt); - else - make_password_from_salt_323(passwd_buff, (ulong *) acl_user->salt); - global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); - global.append(passwd_buff); - global.append('\''); + if (acl_user->auth_string.length) + { + DBUG_ASSERT(acl_user->salt_len); + global.append(STRING_WITH_LEN(" IDENTIFIED BY PASSWORD '")); + global.append(acl_user->auth_string.str, acl_user->auth_string.length); + global.append('\''); + } + } + else + { + global.append(STRING_WITH_LEN(" IDENTIFIED VIA ")); + global.append(acl_user->plugin.str, acl_user->plugin.length); + if (acl_user->auth_string.length) + { + global.append(STRING_WITH_LEN(" USING '")); + global.append(acl_user->auth_string.str, acl_user->auth_string.length); + global.append('\''); + } } /* "show grants" SSL related stuff */ if (acl_user->ssl_type == SSL_TYPE_ANY) @@ -5274,9 +5295,9 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, table->key_info->key_part[1].store_length); key_copy(user_key, table->record[0], table->key_info, key_prefix_length); - if ((error= table->file->index_read_idx_map(table->record[0], 0, - user_key, (key_part_map)3, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_idx_map(table->record[0], 0, + user_key, (key_part_map)3, + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) { @@ -5311,7 +5332,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, DBUG_PRINT("info",("scan table: '%s' search: '%s'@'%s'", table->s->table_name.str, user_str, host_str)); #endif - while ((error= table->file->rnd_next(table->record[0])) != + while ((error= table->file->ha_rnd_next(table->record[0])) != HA_ERR_END_OF_FILE) { if (error) @@ -6325,38 +6346,44 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name, tables->db= (char*)sp_db; tables->table_name= tables->alias= (char*)sp_name; - combo->host.length= strlen(combo->host.str); - combo->user.length= strlen(combo->user.str); - combo->host.str= thd->strmake(combo->host.str,combo->host.length); - combo->user.str= thd->strmake(combo->user.str,combo->user.length); + thd->make_lex_string(&combo->user, + combo->user.str, strlen(combo->user.str), 0); + thd->make_lex_string(&combo->host, + combo->host.str, strlen(combo->host.str), 0); + combo->password= empty_lex_str; + combo->plugin= empty_lex_str; + combo->auth= empty_lex_str; - if(au && au->salt_len) + if(au) { - if (au->salt_len == SCRAMBLE_LENGTH) - { - make_password_from_salt(passwd_buff, au->salt); - combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH; - } - else if (au->salt_len == SCRAMBLE_LENGTH_323) + if (au->salt_len) { - make_password_from_salt_323(passwd_buff, (ulong *) au->salt); - combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + if (au->salt_len == SCRAMBLE_LENGTH) + { + make_password_from_salt(passwd_buff, au->salt); + combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH; + } + else if (au->salt_len == SCRAMBLE_LENGTH_323) + { + make_password_from_salt_323(passwd_buff, (ulong *) au->salt); + combo->password.length= SCRAMBLED_PASSWORD_CHAR_LENGTH_323; + } + else + { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, ER_PASSWD_LENGTH, + ER(ER_PASSWD_LENGTH), SCRAMBLED_PASSWORD_CHAR_LENGTH); + return TRUE; + } + combo->password.str= passwd_buff; } - else + + if (au->plugin.str != native_password_plugin_name.str && + au->plugin.str != old_password_plugin_name.str) { - push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_PASSWD_LENGTH, - ER(ER_PASSWD_LENGTH), - SCRAMBLED_PASSWORD_CHAR_LENGTH); - return TRUE; + combo->plugin= au->plugin; + combo->auth= au->auth_string; } - combo->password.str= passwd_buff; - } - else - { - combo->password.str= (char*)""; - combo->password.length= 0; } if (user_list.push_back(combo)) @@ -6850,3 +6877,1462 @@ bool check_routine_level_acl(THD *thd, const char *db, const char *name, } #endif + +/**************************************************************************** + AUTHENTICATION CODE + including initial connect handshake, invoking appropriate plugins, + client-server plugin negotiation, COM_CHANGE_USER, and native + MySQL authentication plugins. +****************************************************************************/ + +/* few defines to have less ifdef's in the code below */ +#ifdef EMBEDDED_LIBRARY +#undef HAVE_OPENSSL +#ifdef NO_EMBEDDED_ACCESS_CHECKS +#define initialized 0 +#define decrease_user_connections(X) /* nothing */ +#define check_for_max_user_connections(X,Y) 0 +#define get_or_create_user_conn(A,B,C,D) 0 +#endif +#endif +#ifndef HAVE_OPENSSL +#define ssl_acceptor_fd 0 +#define sslaccept(A,B,C,D) 1 +#define NORMAL_HANDSHAKE_SIZE 6 +#endif + +/** + The internal version of what plugins know as MYSQL_PLUGIN_VIO, + basically the context of the authentication session +*/ +struct MPVIO_EXT : public MYSQL_PLUGIN_VIO +{ + MYSQL_SERVER_AUTH_INFO auth_info; + THD *thd; + 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 + /** when restarting a plugin this caches the last client reply */ + struct { + char *plugin, *pkt; ///< pointers into NET::buff + uint pkt_len; + } cached_client_reply; + /** this caches the first plugin packet for restart request on the client */ + struct { + char *pkt; + uint pkt_len; + } cached_server_packet; + int packets_read, packets_written; ///< counters for send/received packets + uint connect_errors; ///< if there were connect errors for this host + /** when plugin returns a failure this tells us what really happened */ + enum { SUCCESS, FAILURE, RESTART } status; +}; + + +/** + a helper function to report an access denied error in all the proper places +*/ + +static void login_failed_error(THD *thd, bool passwd_used) +{ + my_error(ER_ACCESS_DENIED_ERROR, MYF(0), + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, + passwd_used ? ER(ER_YES) : ER(ER_NO)); + general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, + passwd_used ? ER(ER_YES) : ER(ER_NO)); + status_var_increment(thd->status_var.access_denied_errors); + /* + Log access denied messages to the error log when log-warnings = 2 + so that the overhead of the general query log is not required to track + failed connections. + */ + if (global_system_variables.log_warnings > 1) + { + sql_print_warning(ER(ER_ACCESS_DENIED_ERROR), + thd->main_security_ctx.user, + thd->main_security_ctx.host_or_ip, + passwd_used ? ER(ER_YES) : ER(ER_NO)); + } +} + + +/** + sends a server handshake initialization packet, the very first packet + after the connection was established + + Packet format: + + Bytes Content + ----- ---- + 1 protocol version (always 10) + n server version string, \0-terminated + 4 thread id + 8 first 8 bytes of the plugin provided data (scramble) + 1 \0 byte, terminating the first part of a scramble + 2 server capabilities (two lower bytes) + 1 server character set + 2 server status + 2 server capabilities (two upper bytes) + 1 length of the scramble + 10 reserved, always 0 + n rest of the plugin provided data (at least 12 bytes) + 1 \0 byte, terminating the second part of a scramble + + @retval 0 ok + @retval 1 error +*/ + +static bool send_server_handshake_packet(MPVIO_EXT *mpvio, + const char *data, uint data_len) +{ + DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE); + DBUG_ASSERT(data_len <= 255); + + THD *thd= mpvio->thd; + char *buff= (char *)my_alloca(1 + SERVER_VERSION_LENGTH + 1 + data_len + 64); + char scramble_buf[SCRAMBLE_LENGTH]; + char *end= buff; + + *end++= protocol_version; + + thd->client_capabilities= CLIENT_BASIC_FLAGS; + + if (data_len) + { + mpvio->cached_server_packet.pkt= (char*)thd->memdup(data, data_len); + mpvio->cached_server_packet.pkt_len= data_len; + } + + if (data_len < SCRAMBLE_LENGTH) + { + if (data_len) + { /* + the first packet *must* have at least 20 bytes of a scramble. + if a plugin provided less, we pad it to 20 with zeros + */ + memcpy(scramble_buf, data, data_len); + bzero(scramble_buf+data_len, SCRAMBLE_LENGTH-data_len); + data= scramble_buf; + } + else + { + /* + if the default plugin does not provide the data for the scramble at + all, we generate a scramble internally anyway, just in case the + user account (that will be known only later) uses a + native_password_plugin (which needs a scramble). If we don't send a + scramble now - wasting 20 bytes in the packet - + native_password_plugin will have to send it in a separate packet, + adding one more round trip. + */ + create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); + data= thd->scramble; + } + data_len= SCRAMBLE_LENGTH; + } + + if (opt_using_transactions) + thd->client_capabilities|= CLIENT_TRANSACTIONS; + + thd->client_capabilities|= CAN_CLIENT_COMPRESS; + + if (ssl_acceptor_fd) + { + thd->client_capabilities |= CLIENT_SSL; + thd->client_capabilities |= CLIENT_SSL_VERIFY_SERVER_CERT; + } + + end= strnmov(end, server_version, SERVER_VERSION_LENGTH) + 1; + int4store((uchar*) end, mpvio->thd->thread_id); + end+= 4; + + /* + Old clients does not understand long scrambles, but can ignore packet + tail: that's why first part of the scramble is placed here, and second + part at the end of packet. + */ + end= (char*)memcpy(end, data, SCRAMBLE_LENGTH_323); + end+= SCRAMBLE_LENGTH_323; + *end++= 0; + + int2store(end, thd->client_capabilities); + /* write server characteristics: up to 16 bytes allowed */ + end[2]=(char) default_charset_info->number; + int2store(end+3, mpvio->thd->server_status); + int2store(end+5, thd->client_capabilities >> 16); + end[7]= data_len; + bzero(end+8, 10); + end+= 18; + /* write scramble tail */ + end= (char*)memcpy(end, data + SCRAMBLE_LENGTH_323, + data_len - SCRAMBLE_LENGTH_323); + end+= data_len - SCRAMBLE_LENGTH_323; + end= strmake(end, plugin_name(mpvio->plugin)->str, + plugin_name(mpvio->plugin)->length); + + int res= my_net_write(&mpvio->thd->net, (uchar*) buff, (size_t) (end-buff)) || + net_flush(&mpvio->thd->net); + my_afree(buff); + return res; +} + +static bool secure_auth(THD *thd) +{ + if (!opt_secure_auth) + return 0; + + /* + If the server is running in secure auth mode, short scrambles are + forbidden. Extra juggling to report the same error as the old code. + */ + if (thd->client_capabilities & CLIENT_PROTOCOL_41) + { + my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0), + thd->security_ctx->user, + thd->security_ctx->host_or_ip); + general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), + thd->security_ctx->user, + thd->security_ctx->host_or_ip); + } + else + { + my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); + general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); + } + return 1; +} + +/** + sends a "change plugin" packet, requesting a client to restart authentication + using a different authentication plugin + + Packet format: + + Bytes Content + ----- ---- + 1 byte with the value 254 + n client plugin to use, \0-terminated + n plugin provided data + + In a special case of switching from native_password_plugin to + old_password_plugin, the packet contains only one - the first - byte, + plugin name is omitted, plugin data aren't needed as the scramble was + already sent. This one-byte packet is identical to the "use the short + scramble" packet in the protocol before plugins were introduced. + + @retval 0 ok + @retval 1 error +*/ + +static bool send_plugin_request_packet(MPVIO_EXT *mpvio, + const uchar *data, uint data_len) +{ + DBUG_ASSERT(mpvio->packets_written == 1); + DBUG_ASSERT(mpvio->packets_read == 1); + NET *net= &mpvio->thd->net; + static uchar switch_plugin_request_buf[]= { 254 }; + + + mpvio->status= MPVIO_EXT::FAILURE; // the status is no longer RESTART + + const char *client_auth_plugin= + ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin; + + DBUG_ASSERT(client_auth_plugin); + + /* + we send an old "short 4.0 scramble request", if we need to request a + client to use 4.0 auth plugin (short scramble) and the scramble was + already sent to the client + + below, cached_client_reply.plugin is the plugin name that client has used, + client_auth_plugin is derived from mysql.user table, for the given + user account, it's the plugin that the client need to use to login. + */ + bool switch_from_long_to_short_scramble= + native_password_plugin_name.str == mpvio->cached_client_reply.plugin && + client_auth_plugin == old_password_plugin_name.str; + + if (switch_from_long_to_short_scramble) + return secure_auth(mpvio->thd) || + my_net_write(net, switch_plugin_request_buf, 1) || + net_flush(net); + + /* + We never request a client to switch from a short to long scramble. + Plugin-aware clients can do that, but traditionally it meant to + ask an old 4.0 client to use the new 4.1 authentication protocol. + */ + bool switch_from_short_to_long_scramble= + old_password_plugin_name.str == mpvio->cached_client_reply.plugin && + client_auth_plugin == native_password_plugin_name.str; + + if (switch_from_short_to_long_scramble) + { + my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); + general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); + return 1; + } + + return net_write_command(net, switch_plugin_request_buf[0], + (uchar*)client_auth_plugin, + strlen(client_auth_plugin)+1, + (uchar*)data, data_len); +} + + +#ifndef NO_EMBEDDED_ACCESS_CHECKS +/** + Finds acl entry in user database for authentication purposes. + + Finds a user and copies it into mpvio. Reports an authentication + failure if a user is not found. + + @note find_acl_user is not the same, because it doesn't take into + account the case when user is not empty, but acl_user->user is empty + + @retval 0 found + @retval 1 not found +*/ + +static bool find_mpvio_user(MPVIO_EXT *mpvio, Security_context *sctx) +{ + DBUG_ASSERT(mpvio->acl_user == 0); + + pthread_mutex_lock(&acl_cache->lock); + for (uint i=0 ; i < acl_users.elements ; i++) + { + ACL_USER *acl_user_tmp= dynamic_element(&acl_users,i,ACL_USER*); + if ((!acl_user_tmp->user || !strcmp(sctx->user, acl_user_tmp->user)) && + compare_hostname(&acl_user_tmp->host, sctx->host, sctx->ip)) + { + mpvio->acl_user= acl_user_tmp->copy(mpvio->thd->mem_root); + break; + } + } + pthread_mutex_unlock(&acl_cache->lock); + + if (!mpvio->acl_user) + { + login_failed_error(mpvio->thd, 0); + return 1; + } + + /* user account requires non-default plugin and the client is too old */ + if (mpvio->acl_user->plugin.str != native_password_plugin_name.str && + mpvio->acl_user->plugin.str != old_password_plugin_name.str && + !(mpvio->thd->client_capabilities & CLIENT_PLUGIN_AUTH)) + { + DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str, + native_password_plugin_name.str)); + DBUG_ASSERT(my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str, + old_password_plugin_name.str)); + my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); + general_log_print(mpvio->thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); + return 1; + } + + mpvio->auth_info.user_name= sctx->user; + mpvio->auth_info.auth_string= mpvio->acl_user->auth_string.str; + strmake(mpvio->auth_info.authenticated_as, mpvio->acl_user->user ? + mpvio->acl_user->user : "", USERNAME_LENGTH); + + return 0; +} +#endif + + +/* the packet format is described in send_change_user_packet() */ +static bool parse_com_change_user_packet(MPVIO_EXT *mpvio, uint packet_length) +{ + THD *thd= mpvio->thd; + NET *net= &thd->net; + Security_context *sctx= thd->security_ctx; + + char *user= (char*) net->read_pos; + char *end= user + packet_length; + /* Safe because there is always a trailing \0 at the end of the packet */ + char *passwd= strend(user)+1; + uint user_len= passwd - user - 1; + char *db= passwd; + char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 + char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 + uint dummy_errors; + + if (passwd >= end) + { + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); + return 1; + } + + /* + Old clients send null-terminated string as password; new clients send + the size (1 byte) + string (not null-terminated). Hence in case of empty + password both send '\0'. + + This strlen() can't be easily deleted without changing protocol. + + Cast *passwd to an unsigned char, so that it doesn't extend the sign for + *passwd > 127 and become 2**32-127+ after casting to uint. + */ + uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ? + (uchar)(*passwd++) : strlen(passwd)); + + db+= passwd_len + 1; + /* + Database name is always NUL-terminated, so in case of empty database + the packet must contain at least the trailing '\0'. + */ + if (db >= end) + { + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); + return 1; + } + + uint db_len= strlen(db); + + char *ptr= db + db_len + 1; + + if (ptr+1 < end) + { + uint cs_number= uint2korr(ptr); + thd_init_client_charset(thd, cs_number); + thd->update_charset(); + } + + /* Convert database and user names to utf8 */ + db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info, + db, db_len, thd->charset(), &dummy_errors); + + user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, + system_charset_info, user, user_len, + thd->charset(), &dummy_errors); + + if (!(sctx->user= my_strndup(user_buff, user_len, MYF(MY_WME)))) + return 1; + + /* Clear variables that are allocated */ + thd->user_connect= 0; + strmake(sctx->priv_user, sctx->user, USERNAME_LENGTH); + + if (thd->make_lex_string(&mpvio->db, db_buff, db_len, 0) == 0) + return 1; /* The error is set by make_lex_string(). */ + + /* + Clear thd->db as it points to something, that will be freed when + connection is closed. We don't want to accidentally free a wrong + pointer if connect failed. + */ + thd->reset_db(NULL, 0); + + if (!initialized) + { + // if mysqld's been started with --skip-grant-tables option + mpvio->status= MPVIO_EXT::SUCCESS; + return 0; + } + +#ifndef NO_EMBEDDED_ACCESS_CHECKS + if (find_mpvio_user(mpvio, sctx)) + return 1; + + char *client_plugin; + if (thd->client_capabilities & CLIENT_PLUGIN_AUTH) + { + client_plugin= ptr + 2; + if (client_plugin >= end) + { + my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); + return 1; + } + } + else + { + if (thd->client_capabilities & CLIENT_SECURE_CONNECTION) + client_plugin= native_password_plugin_name.str; + else + { + client_plugin= old_password_plugin_name.str; + /* + For a passwordless accounts we use native_password_plugin. + But when an old 4.0 client connects to it, we change it to + old_password_plugin, otherwise MySQL will think that server + and client plugins don't match. + */ + if (mpvio->acl_user->auth_string.length == 0) + mpvio->acl_user->plugin= old_password_plugin_name; + } + } + + /* remember the data part of the packet, to present it to plugin in read_packet() */ + mpvio->cached_client_reply.pkt= passwd; + mpvio->cached_client_reply.pkt_len= passwd_len; + mpvio->cached_client_reply.plugin= client_plugin; + mpvio->status= MPVIO_EXT::RESTART; +#endif + + return 0; +} + + +/* the packet format is described in send_client_reply_packet() */ +static ulong parse_client_handshake_packet(MPVIO_EXT *mpvio, + uchar **buff, ulong pkt_len) +{ +#ifndef EMBEDDED_LIBRARY + THD *thd= mpvio->thd; + NET *net= &thd->net; + char *end; + + DBUG_ASSERT(mpvio->status == MPVIO_EXT::FAILURE); + + if (pkt_len < MIN_HANDSHAKE_SIZE) + return packet_error; + + if (mpvio->connect_errors) + reset_host_errors(&net->vio->remote.sin_addr); + + ulong client_capabilities= uint2korr(net->read_pos); + if (client_capabilities & CLIENT_PROTOCOL_41) + { + client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; + thd->max_client_packet_length= uint4korr(net->read_pos+4); + DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); + thd_init_client_charset(thd, (uint) net->read_pos[8]); + thd->update_charset(); + end= (char*) net->read_pos+32; + } + else + { + thd->max_client_packet_length= uint3korr(net->read_pos+2); + end= (char*) net->read_pos+5; + } + + /* Disable those bits which are not supported by the client. */ + thd->client_capabilities&= client_capabilities; + + if (thd->client_capabilities & CLIENT_IGNORE_SPACE) + thd->variables.sql_mode|= MODE_IGNORE_SPACE; + + DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities)); + if (thd->client_capabilities & CLIENT_SSL) + { + char error_string[1024] __attribute__((unused)); + + /* Do the SSL layering. */ + if (!ssl_acceptor_fd) + return packet_error; + + DBUG_PRINT("info", ("IO layer change in progress...")); + if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, error_string)) + { + DBUG_PRINT("error", ("Failed to accept new SSL connection")); + return packet_error; + } + + DBUG_PRINT("info", ("Reading user information over SSL layer")); + pkt_len= my_net_read(net); + if (pkt_len == packet_error || pkt_len < NORMAL_HANDSHAKE_SIZE) + { + DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", + pkt_len)); + return packet_error; + } + } + + if (end >= (char*) net->read_pos+ pkt_len +2) + return packet_error; + + if (thd->client_capabilities & CLIENT_INTERACTIVE) + thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; + if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && + opt_using_transactions) + net->return_status= &thd->server_status; + + char *user= end; + char *passwd= strend(user)+1; + uint user_len= passwd - user - 1, db_len; + char *db= passwd; + char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 + char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 + uint dummy_errors; + + /* + Old clients send null-terminated string as password; new clients send + the size (1 byte) + string (not null-terminated). Hence in case of empty + password both send '\0'. + + This strlen() can't be easily deleted without changing protocol. + + Cast *passwd to an unsigned char, so that it doesn't extend the sign for + *passwd > 127 and become 2**32-127+ after casting to uint. + */ + uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? + (uchar)(*passwd++) : strlen(passwd); + + if (thd->client_capabilities & CLIENT_CONNECT_WITH_DB) + { + db= db + passwd_len + 1; + /* strlen() can't be easily deleted without changing protocol */ + db_len= strlen(db); + } + else + { + db= 0; + db_len= 0; + } + + if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) + return packet_error; + + char *client_plugin= passwd + passwd_len + (db ? db_len + 1 : 0); + + /* Since 4.1 all database names are stored in utf8 */ + if (db) + { + db_len= copy_and_convert(db_buff, sizeof(db_buff)-1, system_charset_info, + db, db_len, thd->charset(), &dummy_errors); + db= db_buff; + } + + user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, + system_charset_info, user, user_len, + thd->charset(), &dummy_errors); + user= user_buff; + + /* If username starts and ends in "'", chop them off */ + if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'') + { + user++; + user_len-= 2; + } + + Security_context *sctx= thd->security_ctx; + + if (thd->make_lex_string(&mpvio->db, db, db_len, 0) == 0) + return packet_error; /* The error is set by make_lex_string(). */ + if (sctx->user) + x_free(sctx->user); + if (!(sctx->user= my_strndup(user, user_len, MYF(MY_WME)))) + return packet_error; /* The error is set by my_strdup(). */ + + /* + Clear thd->db as it points to something, that will be freed when + connection is closed. We don't want to accidentally free a wrong + pointer if connect failed. + */ + thd->reset_db(NULL, 0); + + if (!initialized) + { + // if mysqld's been started with --skip-grant-tables option + mpvio->status= MPVIO_EXT::SUCCESS; + return packet_error; + } + + if (find_mpvio_user(mpvio, sctx)) + return packet_error; + + if (thd->client_capabilities & CLIENT_PLUGIN_AUTH) + { + if ((client_plugin + strlen(client_plugin)) > + (char *)net->read_pos + pkt_len) + return packet_error; + } + else + { + if (thd->client_capabilities & CLIENT_SECURE_CONNECTION) + client_plugin= native_password_plugin_name.str; + else + { + client_plugin= old_password_plugin_name.str; + /* + For a passwordless accounts we use native_password_plugin. + But when an old 4.0 client connects to it, we change it to + old_password_plugin, otherwise MySQL will think that server + and client plugins don't match. + */ + if (mpvio->acl_user->auth_string.length == 0) + mpvio->acl_user->plugin= old_password_plugin_name; + } + } + + /* + if the acl_user needs a different plugin to authenticate + (specified in GRANT ... AUTHENTICATED VIA plugin_name ..) + we need to restart the authentication in the server. + But perhaps the client has already used the correct plugin - + in that case the authentication on the client may not need to be + restarted and a server auth plugin will read the data that the client + has just send. Cache them to return in the next server_mpvio_read_packet(). + */ + if (my_strcasecmp(system_charset_info, mpvio->acl_user->plugin.str, + plugin_name(mpvio->plugin)->str) != 0) + { + mpvio->cached_client_reply.pkt= passwd; + mpvio->cached_client_reply.pkt_len= passwd_len; + mpvio->cached_client_reply.plugin= client_plugin; + mpvio->status= MPVIO_EXT::RESTART; + return packet_error; + } + + /* + ok, we don't need to restart the authentication on the server. + but if the client used the wrong plugin, we need to restart + the authentication on the client. Do it here, the server plugin + doesn't need to know. + */ + const char *client_auth_plugin= + ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin; + + if (client_auth_plugin && + my_strcasecmp(system_charset_info, client_plugin, client_auth_plugin)) + { + mpvio->cached_client_reply.plugin= client_plugin; + if (send_plugin_request_packet(mpvio, + (uchar*)mpvio->cached_server_packet.pkt, + mpvio->cached_server_packet.pkt_len)) + return packet_error; + + passwd_len= my_net_read(&mpvio->thd->net); + passwd = (char*)mpvio->thd->net.read_pos; + } + + *buff= (uchar*)passwd; + return passwd_len; +#else + return 0; +#endif +} + + +/** + vio->write_packet() callback method for server authentication plugins + + This function is called by a server authentication plugin, when it wants + to send data to the client. + + It transparently wraps the data into a handshake packet, + and handles plugin negotiation with the client. If necessary, + it escapes the plugin data, if it starts with a mysql protocol packet byte. +*/ + +static int server_mpvio_write_packet(MYSQL_PLUGIN_VIO *param, + const uchar *packet, int packet_len) +{ + MPVIO_EXT *mpvio= (MPVIO_EXT*)param; + int res; + + /* reset cached_client_reply */ + mpvio->cached_client_reply.pkt= 0; + /* for the 1st packet we wrap plugin data into the handshake packet */ + if (mpvio->packets_written == 0) + res= send_server_handshake_packet(mpvio, (char*)packet, packet_len); + else if (mpvio->status == MPVIO_EXT::RESTART) + res= send_plugin_request_packet(mpvio, packet, packet_len); + else if (packet_len > 0 && (*packet == 1 || *packet == 255 || *packet == 254)) + { + /* + we cannot allow plugin data packet to start from 255 or 254 - + as the client will treat it as an error or "change plugin" packet. + We'll escape these bytes with \1. Consequently, we + have to escape \1 byte too. + */ + res= net_write_command(&mpvio->thd->net, 1, (uchar*)"", 0, + packet, packet_len); + } + else + { + res= my_net_write(&mpvio->thd->net, packet, packet_len) || + net_flush(&mpvio->thd->net); + } + mpvio->packets_written++; + return res; +} + + +/** + vio->read_packet() callback method for server authentication plugins + + This function is called by a server authentication plugin, when it wants + to read data from the client. + + It transparently extracts the client plugin data, if embedded into + a client authentication handshake packet, and handles plugin negotiation + with the client, if necessary. +*/ + +static int server_mpvio_read_packet(MYSQL_PLUGIN_VIO *param, uchar **buf) +{ + MPVIO_EXT *mpvio= (MPVIO_EXT*)param; + ulong pkt_len; + + if (mpvio->packets_written == 0) + { + /* + plugin wants to read the data without sending anything first. + send an empty packet to force a server handshake packet to be sent + */ + if (server_mpvio_write_packet(mpvio, 0, 0)) + pkt_len= packet_error; + else + pkt_len= my_net_read(&mpvio->thd->net); + } + else + if (mpvio->cached_client_reply.pkt) + { + DBUG_ASSERT(mpvio->status == MPVIO_EXT::RESTART); + DBUG_ASSERT(mpvio->packets_read > 0); + /* + if the have the data cached from the last server_mpvio_read_packet + (which can be the case if it's a restarted authentication) + and a client has used the correct plugin, then we can return the + cached data straight away and avoid one round trip. + */ + const char *client_auth_plugin= + ((st_mysql_auth *)(plugin_decl(mpvio->plugin)->info))->client_auth_plugin; + if (client_auth_plugin == 0 || + my_strcasecmp(system_charset_info, mpvio->cached_client_reply.plugin, + client_auth_plugin) == 0) + { + mpvio->status= MPVIO_EXT::FAILURE; + *buf= (uchar*)mpvio->cached_client_reply.pkt; + mpvio->cached_client_reply.pkt= 0; + mpvio->packets_read++; + return (int)mpvio->cached_client_reply.pkt_len; + } + /* + But if the client has used the wrong plugin, the cached data are + useless. Furthermore, we have to send a "change plugin" request + to the client. + */ + if (server_mpvio_write_packet(mpvio, 0, 0)) + pkt_len= packet_error; + else + pkt_len= my_net_read(&mpvio->thd->net); + } + else + pkt_len= my_net_read(&mpvio->thd->net); + + if (pkt_len == packet_error) + goto err; + + mpvio->packets_read++; + + /* + the 1st packet has the plugin data wrapped into the client authentication + handshake packet + */ + if (mpvio->packets_read == 1) + { + pkt_len= parse_client_handshake_packet(mpvio, buf, pkt_len); + if (pkt_len == packet_error) + goto err; + } + else + *buf = mpvio->thd->net.read_pos; + + return (int)pkt_len; + +err: + if (mpvio->status == MPVIO_EXT::FAILURE && !mpvio->thd->is_error()) + { + inc_host_errors(&mpvio->thd->net.vio->remote.sin_addr); + my_error(ER_HANDSHAKE_ERROR, MYF(0), + mpvio->thd->security_ctx->host_or_ip); + } + return -1; +} + + +/** + fills MYSQL_PLUGIN_VIO_INFO structure with the information about the + connection +*/ + +static void server_mpvio_info(MYSQL_PLUGIN_VIO *vio, + MYSQL_PLUGIN_VIO_INFO *info) +{ + MPVIO_EXT *mpvio= (MPVIO_EXT*)vio; + mpvio_info(mpvio->thd->net.vio, info); +} + + +static bool acl_check_ssl(THD *thd, ACL_USER *acl_user) +{ +#if defined(HAVE_OPENSSL) + Vio *vio=thd->net.vio; + SSL *ssl= (SSL*) vio->ssl_arg; + X509 *cert; +#endif + + /* + At this point we know that user is allowed to connect + from given host by given username/password pair. Now + we check if SSL is required, if user is using SSL and + if X509 certificate attributes are OK + */ + switch (acl_user->ssl_type) { + case SSL_TYPE_NOT_SPECIFIED: // Impossible + case SSL_TYPE_NONE: // SSL is not required + return 0; +#if defined(HAVE_OPENSSL) + case SSL_TYPE_ANY: // Any kind of SSL is ok + return vio_type(vio) != VIO_TYPE_SSL; + case SSL_TYPE_X509: /* Client should have any valid certificate. */ + /* + Connections with non-valid certificates are dropped already + in sslaccept() anyway, so we do not check validity here. + + We need to check for absence of SSL because without SSL + we should reject connection. + */ + if (vio_type(vio) == VIO_TYPE_SSL && + SSL_get_verify_result(ssl) == X509_V_OK && + (cert= SSL_get_peer_certificate(ssl))) + { + X509_free(cert); + return 0; + } + return 1; + case SSL_TYPE_SPECIFIED: /* Client should have specified attrib */ + /* If a cipher name is specified, we compare it to actual cipher in use. */ + if (vio_type(vio) != VIO_TYPE_SSL || + SSL_get_verify_result(ssl) != X509_V_OK) + return 1; + if (acl_user->ssl_cipher) + { + DBUG_PRINT("info",("comparing ciphers: '%s' and '%s'", + acl_user->ssl_cipher,SSL_get_cipher(ssl))); + if (strcmp(acl_user->ssl_cipher,SSL_get_cipher(ssl))) + { + if (global_system_variables.log_warnings) + sql_print_information("X509 ciphers mismatch: should be '%s' but is '%s'", + acl_user->ssl_cipher, SSL_get_cipher(ssl)); + return 1; + } + } + /* Prepare certificate (if exists) */ + if (!(cert= SSL_get_peer_certificate(ssl))) + return 1; + /* If X509 issuer is specified, we check it... */ + if (acl_user->x509_issuer) + { + char *ptr = X509_NAME_oneline(X509_get_issuer_name(cert), 0, 0); + DBUG_PRINT("info",("comparing issuers: '%s' and '%s'", + acl_user->x509_issuer, ptr)); + if (strcmp(acl_user->x509_issuer, ptr)) + { + if (global_system_variables.log_warnings) + sql_print_information("X509 issuer mismatch: should be '%s' " + "but is '%s'", acl_user->x509_issuer, ptr); + free(ptr); + X509_free(cert); + return 1; + } + free(ptr); + } + /* X509 subject is specified, we check it .. */ + if (acl_user->x509_subject) + { + char *ptr= X509_NAME_oneline(X509_get_subject_name(cert), 0, 0); + DBUG_PRINT("info",("comparing subjects: '%s' and '%s'", + acl_user->x509_subject, ptr)); + if (strcmp(acl_user->x509_subject,ptr)) + { + if (global_system_variables.log_warnings) + sql_print_information("X509 subject mismatch: should be '%s' but is '%s'", + acl_user->x509_subject, ptr); + free(ptr); + X509_free(cert); + return 1; + } + free(ptr); + } + X509_free(cert); + return 0; +#else /* HAVE_OPENSSL */ + default: + /* + If we don't have SSL but SSL is required for this user the + authentication should fail. + */ + return 1; +#endif /* HAVE_OPENSSL */ + } + return 1; +} + +static int do_auth_once(THD *thd, LEX_STRING *auth_plugin_name, + MPVIO_EXT *mpvio) +{ + int res= CR_OK, old_status= MPVIO_EXT::FAILURE; + bool unlock_plugin= false; + plugin_ref plugin; + +#ifdef EMBEDDED_LIBRARY + plugin= native_password_plugin; +#else + if (auth_plugin_name->str == native_password_plugin_name.str) + plugin= native_password_plugin; + else if (auth_plugin_name->str == old_password_plugin_name.str) + plugin= old_password_plugin; + else if ((plugin= my_plugin_lock_by_name(thd, auth_plugin_name, + MYSQL_AUTHENTICATION_PLUGIN))) + unlock_plugin= true; +#endif + + mpvio->plugin= plugin; + old_status= mpvio->status; + + if (plugin) + { + st_mysql_auth *auth= (st_mysql_auth*)plugin_decl(plugin)->info; + res= auth->authenticate_user(mpvio, &mpvio->auth_info); + + if (unlock_plugin) + plugin_unlock(thd, plugin); + } + else + { + /* Server cannot load the required plugin. */ + my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), auth_plugin_name->str); + res= CR_ERROR; + } + + /* + If the status was MPVIO_EXT::RESTART before the authenticate_user() call + it can never be MPVIO_EXT::RESTART after the call, because any call + to write_packet() or read_packet() will reset the status. + + But (!) if a plugin never called a read_packet() or write_packet(), the + status will stay unchanged. We'll fix it, by resetting the status here. + */ + if (old_status == MPVIO_EXT::RESTART && mpvio->status == MPVIO_EXT::RESTART) + mpvio->status= MPVIO_EXT::FAILURE; // reset to the default + + return res; +} + +/** + Perform the handshake, authorize the client and update thd sctx variables. + + @param thd thread handle + @param connect_errors number of previous failed connect attemps + from this host + @param com_change_user_pkt_len size of the COM_CHANGE_USER packet + (without the first, command, byte) or 0 + if it's not a COM_CHANGE_USER (that is, if + it's a new connection) + + @retval 0 success, thd is updated. + @retval 1 error +*/ + +bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len) +{ + int res= CR_OK; + MPVIO_EXT mpvio; + LEX_STRING *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"); + + compile_time_assert(MYSQL_USERNAME_LENGTH == USERNAME_LENGTH); + + bzero(&mpvio, sizeof(mpvio)); + mpvio.read_packet= server_mpvio_read_packet; + mpvio.write_packet= server_mpvio_write_packet; + mpvio.info= server_mpvio_info; + mpvio.thd= thd; + mpvio.connect_errors= connect_errors; + mpvio.status= MPVIO_EXT::FAILURE; + + if (command == COM_CHANGE_USER) + { + mpvio.packets_written++; // pretend that a server handshake packet was sent + mpvio.packets_read++; // take COM_CHANGE_USER packet into account + + if (parse_com_change_user_packet(&mpvio, com_change_user_pkt_len)) + DBUG_RETURN(1); + + DBUG_ASSERT(mpvio.status == MPVIO_EXT::RESTART || + mpvio.status == MPVIO_EXT::SUCCESS); + } + else + { + /* mark the thd as having no scramble yet */ + thd->scramble[SCRAMBLE_LENGTH]= 1; + + /* + perform the first authentication attempt, with the default plugin. + This sends the server handshake packet, reads the client reply + with a user name, and performs the authentication if everyone has used + the correct plugin. + */ + res= do_auth_once(thd, auth_plugin_name, &mpvio); + } + + /* + retry the authentication, if - after receiving the user name - + we found that we need to switch to a non-default plugin + */ + if (mpvio.status == MPVIO_EXT::RESTART) + { + DBUG_ASSERT(mpvio.acl_user); + DBUG_ASSERT(command == COM_CHANGE_USER || + my_strcasecmp(system_charset_info, auth_plugin_name->str, + mpvio.acl_user->plugin.str)); + auth_plugin_name= &mpvio.acl_user->plugin; + res= do_auth_once(thd, auth_plugin_name, &mpvio); + } + + Security_context *sctx= thd->security_ctx; + ACL_USER *acl_user= mpvio.acl_user; + + thd->password= mpvio.auth_info.password_used; // remember for error messages + + /* + Log the command here so that the user can check the log + for the tried logins and also to detect break-in attempts. + + if sctx->user is unset it's protocol failure, bad packet. + */ + if (sctx->user) + { + if (strcmp(sctx->priv_user, sctx->user)) + { + general_log_print(thd, command, "%s@%s as %s on %s", + sctx->user, sctx->host_or_ip, + sctx->priv_user[0] ? sctx->priv_user : "anonymous", + mpvio.db.str ? mpvio.db.str : (char*) ""); + } + else + general_log_print(thd, command, (char*) "%s@%s on %s", + sctx->user, sctx->host_or_ip, + mpvio.db.str ? mpvio.db.str : (char*) ""); + } + + if (res > CR_OK && mpvio.status != MPVIO_EXT::SUCCESS) + { + DBUG_ASSERT(mpvio.status == MPVIO_EXT::FAILURE); + + if (!thd->is_error()) + login_failed_error(thd, thd->password); + DBUG_RETURN(1); + } + + if (initialized) // if not --skip-grant-tables + { + sctx->master_access= acl_user->access; + strmake(sctx->priv_user, mpvio.auth_info.authenticated_as, USERNAME_LENGTH); + if (acl_user->host.hostname) + strmake(sctx->priv_host, acl_user->host.hostname, MAX_HOSTNAME); + else + *sctx->priv_host= 0; + + /* + OK. Let's check the SSL. Historically it was checked after the password, + as an additional layer, not instead of the password + (in which case it would've been a plugin too). + */ + if (acl_check_ssl(thd, acl_user)) + { + login_failed_error(thd, thd->password); + DBUG_RETURN(1); + } + + /* Don't allow the user to connect if he has done too many queries */ + if ((acl_user->user_resource.questions || acl_user->user_resource.updates || + acl_user->user_resource.conn_per_hour || + acl_user->user_resource.user_conn || max_user_connections) && + get_or_create_user_conn(thd, + (opt_old_style_user_limits ? sctx->user : sctx->priv_user), + (opt_old_style_user_limits ? sctx->host_or_ip : sctx->priv_host), + &acl_user->user_resource)) + DBUG_RETURN(1); // The error is set by get_or_create_user_conn() + } + else + sctx->skip_grants(); + + if (thd->user_connect && + (thd->user_connect->user_resources.conn_per_hour || + thd->user_connect->user_resources.user_conn || + max_user_connections) && + check_for_max_user_connections(thd, thd->user_connect)) + { + status_var_increment(denied_connections); + DBUG_RETURN(1); // The error is set in check_for_max_user_connections() + } + + DBUG_PRINT("info", + ("Capabilities: %lu packet_length: %ld Host: '%s' " + "Login user: '%s' Priv_user: '%s' Using password: %s " + "Access: %lu db: '%s'", + thd->client_capabilities, thd->max_client_packet_length, + sctx->host_or_ip, sctx->user, sctx->priv_user, + thd->password ? "yes": "no", + sctx->master_access, mpvio.db.str)); + + if (command == COM_CONNECT && + !(thd->main_security_ctx.master_access & SUPER_ACL)) + { + pthread_mutex_lock(&LOCK_connection_count); + bool count_ok= (*thd->scheduler->connection_count <= + *thd->scheduler->max_connections); + VOID(pthread_mutex_unlock(&LOCK_connection_count)); + if (!count_ok) + { // too many connections + my_error(ER_CON_COUNT_ERROR, MYF(0)); + DBUG_RETURN(1); + } + } + + /* + This is the default access rights for the current database. It's + set to 0 here because we don't have an active database yet (and we + may not have an active database to set. + */ + sctx->db_access=0; + + /* Change a database if necessary */ + if (mpvio.db.length) + { + if (mysql_change_db(thd, &mpvio.db, FALSE)) + { + /* mysql_change_db() has pushed the error message. */ + if (thd->user_connect) + { + status_var_increment(thd->status_var.access_denied_errors); + decrease_user_connections(thd->user_connect); + thd->user_connect= 0; + } + DBUG_RETURN(1); + } + } + + thd->net.net_skip_rest_factor= 2; // skip at most 2*max_packet_size + + if (res == CR_OK_HANDSHAKE_COMPLETE) + thd->main_da.disable_status(); + else + my_ok(thd); + + /* Ready to handle queries */ + DBUG_RETURN(0); +} + + +/** + MySQL Server Password Authentication Plugin + + In the MySQL authentication protocol: + 1. the server sends the random scramble to the client + 2. client sends the encrypted password back to the server + 3. the server checks the password. +*/ + +static int native_password_authenticate(MYSQL_PLUGIN_VIO *vio, + MYSQL_SERVER_AUTH_INFO *info) +{ + uchar *pkt; + int pkt_len; + MPVIO_EXT *mpvio=(MPVIO_EXT*)vio; + THD *thd=mpvio->thd; + + /* generate the scramble, or reuse the old one */ + if (thd->scramble[SCRAMBLE_LENGTH]) + create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); + + /* send it to the client */ + if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1)) + return CR_ERROR; + + /* reply and authenticate */ + + /* + <digression> + This is more complex than it looks. + + The plugin (we) may be called right after the client was connected - + and will need to send a scramble, read reply, authenticate. + + Or the plugin may be called after another plugin has sent a scramble, + and read the reply. If the client has used the correct client-plugin, + we won't need to read anything here from the client, the client + has already sent a reply with everything we need for authentication. + + Or the plugin may be called after another plugin has sent a scramble, + and read the reply, but the client has used the wrong client-plugin. + We'll need to sent a "switch to another plugin" packet to the + client and read the reply. "Use the short scramble" packet is a special + case of "switch to another plugin" packet. + + Or, perhaps, the plugin may be called after another plugin has + done the handshake but did not send a useful scramble. We'll need + to send a scramble (and perhaps a "switch to another plugin" packet) + and read the reply. + + Besides, a client may be an old one, that doesn't understand plugins. + Or doesn't even understand 4.0 scramble. + + And we want to keep the same protocol on the wire unless non-native + plugins are involved. + + Anyway, it still looks simple from a plugin point of view: + "send the scramble, read the reply and authenticate". + All the magic is transparently handled by the server. + </digression> + */ + + /* read the reply with the encrypted password */ + if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0) + return CR_ERROR; + +#ifdef NO_EMBEDDED_ACCESS_CHECKS + return CR_OK; +#endif + + if (pkt_len == 0) /* no password */ + return info->auth_string[0] ? CR_ERROR : CR_OK; + + info->password_used = 1; + if (pkt_len == SCRAMBLE_LENGTH) + return check_scramble(pkt, thd->scramble, mpvio->acl_user->salt) ? + CR_ERROR : CR_OK; + + inc_host_errors(&mpvio->thd->net.vio->remote.sin_addr); + my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); + return CR_ERROR; +} + + +static int old_password_authenticate(MYSQL_PLUGIN_VIO *vio, + MYSQL_SERVER_AUTH_INFO *info) +{ + uchar *pkt; + int pkt_len; + MPVIO_EXT *mpvio=(MPVIO_EXT*)vio; + THD *thd=mpvio->thd; + + /* generate the scramble, or reuse the old one */ + if (thd->scramble[SCRAMBLE_LENGTH]) + create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); + + /* send it to the client */ + if (mpvio->write_packet(mpvio, (uchar*)thd->scramble, SCRAMBLE_LENGTH + 1)) + return CR_ERROR; + + /* read the reply and authenticate */ + if ((pkt_len= mpvio->read_packet(mpvio, &pkt)) < 0) + return CR_ERROR; + +#ifdef NO_EMBEDDED_ACCESS_CHECKS + return CR_OK; +#endif + + /* + legacy: if switch_from_long_to_short_scramble, + the password is sent \0-terminated, the pkt_len is always 9 bytes. + We need to figure out the correct scramble length here. + */ + if (pkt_len == SCRAMBLE_LENGTH_323+1) + pkt_len= strnlen((char*)pkt, pkt_len); + + if (pkt_len == 0) /* no password */ + return info->auth_string[0] ? CR_ERROR : CR_OK; + + if (secure_auth(thd)) + return CR_ERROR; + + info->password_used = 1; + + if (pkt_len == SCRAMBLE_LENGTH_323) + return check_scramble_323(pkt, thd->scramble, + (ulong *)mpvio->acl_user->salt) ? CR_ERROR : CR_OK; + + inc_host_errors(&mpvio->thd->net.vio->remote.sin_addr); + my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); + return CR_ERROR; +} + +static struct st_mysql_auth native_password_handler= +{ + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + native_password_plugin_name.str, + native_password_authenticate +}; + +static struct st_mysql_auth old_password_handler= +{ + MYSQL_AUTHENTICATION_INTERFACE_VERSION, + old_password_plugin_name.str, + old_password_authenticate +}; + +mysql_declare_plugin(mysql_password) +{ + MYSQL_AUTHENTICATION_PLUGIN, /* type constant */ + &native_password_handler, /* type descriptor */ + native_password_plugin_name.str, /* Name */ + "R.J.Silk, Sergei Golubchik", /* Author */ + "Native MySQL authentication", /* Description */ + PLUGIN_LICENSE_GPL, /* License */ + NULL, /* Init function */ + NULL, /* Deinit function */ + 0x0100, /* Version (1.0) */ + NULL, /* status variables */ + NULL, /* system variables */ + NULL /* config options */ +}, +{ + MYSQL_AUTHENTICATION_PLUGIN, /* type constant */ + &old_password_handler, /* type descriptor */ + old_password_plugin_name.str, /* Name */ + "R.J.Silk, Sergei Golubchik", /* Author */ + "Old MySQL-4.0 authentication", /* Description */ + PLUGIN_LICENSE_GPL, /* License */ + NULL, /* Init function */ + NULL, /* Deinit function */ + 0x0100, /* Version (1.0) */ + NULL, /* status variables */ + NULL, /* system variables */ + NULL /* config options */ +} +mysql_declare_plugin_end; + +maria_declare_plugin(mysql_password) +{ + MYSQL_AUTHENTICATION_PLUGIN, /* type constant */ + &native_password_handler, /* type descriptor */ + native_password_plugin_name.str, /* Name */ + "R.J.Silk, Sergei Golubchik", /* Author */ + "Native MySQL authentication", /* Description */ + PLUGIN_LICENSE_GPL, /* License */ + NULL, /* Init function */ + NULL, /* Deinit function */ + 0x0100, /* Version (1.0) */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.0", /* String version */ + MariaDB_PLUGIN_MATURITY_BETA /* Maturity */ +}, +{ + MYSQL_AUTHENTICATION_PLUGIN, /* type constant */ + &old_password_handler, /* type descriptor */ + old_password_plugin_name.str, /* Name */ + "R.J.Silk, Sergei Golubchik", /* Author */ + "Old MySQL-4.0 authentication", /* Description */ + PLUGIN_LICENSE_GPL, /* License */ + NULL, /* Init function */ + NULL, /* Deinit function */ + 0x0100, /* Version (1.0) */ + NULL, /* status variables */ + NULL, /* system variables */ + "1.0", /* String version */ + MariaDB_PLUGIN_MATURITY_BETA /* Maturity */ +} +maria_declare_plugin_end; diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 4c835e2718c..5078c80cedf 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -161,53 +161,6 @@ enum mysql_db_table_field extern const TABLE_FIELD_DEF mysql_db_table_def; -/* Classes */ - -struct acl_host_and_ip -{ - char *hostname; - long ip,ip_mask; // Used with masked ip:s -}; - - -class ACL_ACCESS { -public: - ulong sort; - ulong access; -}; - - -/* ACL_HOST is used if no host is specified */ - -class ACL_HOST :public ACL_ACCESS -{ -public: - acl_host_and_ip host; - char *db; -}; - - -class ACL_USER :public ACL_ACCESS -{ -public: - acl_host_and_ip host; - uint hostname_length; - USER_RESOURCES user_resource; - char *user; - uint8 salt[SCRAMBLE_LENGTH+1]; // scrambled password in binary form - uint8 salt_len; // 0 - no password, 4 - 3.20, 8 - 3.23, 20 - 4.1.1 - enum SSL_type ssl_type; - const char *ssl_cipher, *x509_issuer, *x509_subject; -}; - - -class ACL_DB :public ACL_ACCESS -{ -public: - acl_host_and_ip host; - char *user,*db; -}; - /* prototypes */ bool hostname_requires_resolving(const char *hostname); @@ -216,10 +169,9 @@ my_bool acl_reload(THD *thd); 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); -int acl_getroot(THD *thd, USER_RESOURCES *mqh, const char *passwd, - uint passwd_len); -bool acl_getroot_no_password(Security_context *sctx, char *user, char *host, - char *ip, char *db); +bool acl_authenticate(THD *thd, uint connect_errors, uint com_change_user_pkt_len); +bool acl_getroot(Security_context *sctx, char *user, char *host, + char *ip, char *db); bool acl_check_host(const char *host, const char *ip); int check_change_password(THD *thd, const char *host, const char *user, char *password, uint password_len); diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 1da17c216f2..55555375e6e 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -1376,6 +1376,12 @@ bool close_thread_table(THD *thd, TABLE **table_ptr) DBUG_PRINT("tcache", ("table: '%s'.'%s' 0x%lx", table->s->db.str, table->s->table_name.str, (long) table)); + if (table->file) + { + table->file->update_global_table_stats(); + table->file->update_global_index_stats(); + } + *table_ptr=table->next; /* When closing a MERGE parent or child table, detach the children first. @@ -1913,6 +1919,13 @@ void close_temporary(TABLE *table, bool free_share, bool delete_table) DBUG_PRINT("tmptable", ("closing table: '%s'.'%s'", table->s->db.str, table->s->table_name.str)); + /* in_use is not set for replication temporary tables during shutdown */ + if (table->in_use) + { + table->file->update_global_table_stats(); + table->file->update_global_index_stats(); + } + free_io_cache(table); closefrm(table, 0); if (delete_table) @@ -2983,7 +2996,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root, table->status=STATUS_NO_RECORD; table->insert_values= 0; table->fulltext_searched= 0; - table->file->ft_handler= 0; + table->file->ha_start_of_new_statement(); table->reginfo.impossible_range= 0; /* Catch wrong handling of the auto_increment_field_not_null. */ DBUG_ASSERT(!table->auto_increment_field_not_null); @@ -5629,6 +5642,9 @@ static void update_field_dependencies(THD *thd, Field *field, TABLE *table) table->covering_keys.intersect(field->part_of_key); table->merge_keys.merge(field->part_of_key); + if (field->vcol_info) + table->mark_virtual_col(field); + if (thd->mark_used_columns == MARK_COLUMNS_READ) current_bitmap= table->read_set; else @@ -7925,6 +7941,12 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, { /* Mark fields as used to allow storage engine to optimze access */ bitmap_set_bit(field->table->read_set, field->field_index); + /* + Mark virtual fields for write and others that the virtual fields + depend on for read. + */ + if (field->vcol_info) + field->table->mark_virtual_col(field); if (table) { table->covering_keys.intersect(field->part_of_key); @@ -8136,7 +8158,10 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, Item *value, *fld; Item_field *field; TABLE *table= 0; + List<TABLE> tbl_list; + bool abort_on_warning_saved= thd->abort_on_warning; DBUG_ENTER("fill_record"); + tbl_list.empty(); /* Reset the table->auto_increment_field_not_null as it is valid for @@ -8170,14 +8195,54 @@ fill_record(THD * thd, List<Item> &fields, List<Item> &values, table= rfield->table; if (rfield == table->next_number_field) table->auto_increment_field_not_null= TRUE; + if (rfield->vcol_info && + value->type() != Item::DEFAULT_VALUE_ITEM && + value->type() != Item::NULL_ITEM && + table->s->table_category != TABLE_CATEGORY_TEMPORARY) + { + thd->abort_on_warning= FALSE; + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, + ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), + rfield->field_name, table->s->table_name.str); + thd->abort_on_warning= abort_on_warning_saved; + } if ((value->save_in_field(rfield, 0) < 0) && !ignore_errors) { my_message(ER_UNKNOWN_ERROR, ER(ER_UNKNOWN_ERROR), MYF(0)); goto err; } + tbl_list.push_back(table); } + /* Update virtual fields*/ + thd->abort_on_warning= FALSE; + if (tbl_list.head()) + { + List_iterator_fast<TABLE> it(tbl_list); + TABLE *prev_table= 0; + while ((table= it++)) + { + /* + Do simple optimization to prevent unnecessary re-generating + values for virtual fields + */ + if (table != prev_table) + { + prev_table= table; + if (table->vfield) + { + if (update_virtual_fields(table, TRUE)) + { + goto err; + } + } + } + } + } + thd->abort_on_warning= abort_on_warning_saved; DBUG_RETURN(thd->is_error()); err: + thd->abort_on_warning= abort_on_warning_saved; if (table) table->auto_increment_field_not_null= FALSE; DBUG_RETURN(TRUE); @@ -8213,9 +8278,31 @@ fill_record_n_invoke_before_triggers(THD *thd, List<Item> &fields, Table_triggers_list *triggers, enum trg_event_type event) { - return (fill_record(thd, fields, values, ignore_errors) || - (triggers && triggers->process_triggers(thd, event, - TRG_ACTION_BEFORE, TRUE))); + bool result; + result= (fill_record(thd, fields, values, ignore_errors) || + (triggers && triggers->process_triggers(thd, event, + TRG_ACTION_BEFORE, TRUE))); + /* + Re-calculate virtual fields to cater for cases when base columns are + updated by the triggers. + */ + if (!result && triggers) + { + TABLE *table= 0; + List_iterator_fast<Item> f(fields); + Item *fld; + Item_field *item_field; + if (fields.elements) + { + fld= (Item_field*)f++; + item_field= fld->filed_for_view_update(); + if (item_field && item_field->field && + (table= item_field->field->table) && + table->vfield) + result= update_virtual_fields(table, TRUE); + } + } + return result; } @@ -8243,11 +8330,14 @@ bool fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors) { List_iterator_fast<Item> v(values); + List<TABLE> tbl_list; Item *value; TABLE *table= 0; + bool abort_on_warning_saved= thd->abort_on_warning; DBUG_ENTER("fill_record"); Field *field; + tbl_list.empty(); /* Reset the table->auto_increment_field_not_null as it is valid for only one row. @@ -8267,12 +8357,52 @@ fill_record(THD *thd, Field **ptr, List<Item> &values, bool ignore_errors) table= field->table; if (field == table->next_number_field) table->auto_increment_field_not_null= TRUE; + if (field->vcol_info && + value->type() != Item::DEFAULT_VALUE_ITEM && + value->type() != Item::NULL_ITEM && + table->s->table_category != TABLE_CATEGORY_TEMPORARY) + { + thd->abort_on_warning= FALSE; + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN, + ER(ER_WARNING_NON_DEFAULT_VALUE_FOR_VIRTUAL_COLUMN), + field->field_name, table->s->table_name.str); + thd->abort_on_warning= abort_on_warning_saved; + } if (value->save_in_field(field, 0) < 0) goto err; + tbl_list.push_back(table); } + /* Update virtual fields*/ + thd->abort_on_warning= FALSE; + if (tbl_list.head()) + { + List_iterator_fast<TABLE> t(tbl_list); + TABLE *prev_table= 0; + while ((table= t++)) + { + /* + Do simple optimization to prevent unnecessary re-generating + values for virtual fields + */ + if (table != prev_table) + { + prev_table= table; + if (table->vfield) + { + if (update_virtual_fields(table, TRUE)) + { + goto err; + } + } + } + } + } + thd->abort_on_warning= abort_on_warning_saved; DBUG_RETURN(thd->is_error()); err: + thd->abort_on_warning= abort_on_warning_saved; if (table) table->auto_increment_field_not_null= FALSE; DBUG_RETURN(TRUE); @@ -8308,9 +8438,22 @@ fill_record_n_invoke_before_triggers(THD *thd, Field **ptr, Table_triggers_list *triggers, enum trg_event_type event) { - return (fill_record(thd, ptr, values, ignore_errors) || - (triggers && triggers->process_triggers(thd, event, - TRG_ACTION_BEFORE, TRUE))); + bool result; + result= (fill_record(thd, ptr, values, ignore_errors) || + (triggers && triggers->process_triggers(thd, event, + TRG_ACTION_BEFORE, TRUE))); + /* + Re-calculate virtual fields to cater for cases when base columns are + updated by the triggers. + */ + if (!result && triggers && *ptr) + { + TABLE *table= (*ptr)->table; + if (table->vfield) + result= update_virtual_fields(table, TRUE); + } + return result; + } diff --git a/sql/sql_builtin.cc.in b/sql/sql_builtin.cc.in index 7ecd4918d7b..3ba47e3a14e 100644 --- a/sql/sql_builtin.cc.in +++ b/sql/sql_builtin.cc.in @@ -16,13 +16,12 @@ #include <my_global.h> #include <mysql/plugin.h> -typedef struct st_mysql_plugin builtin_plugin[]; +typedef struct st_maria_plugin builtin_maria_plugin[]; -extern builtin_plugin - builtin_binlog_plugin@mysql_plugin_defs@; +extern builtin_maria_plugin + builtin_maria_binlog_plugin, builtin_maria_mysql_password_plugin@maria_plugin_defs@; -struct st_mysql_plugin *mysqld_builtins[]= +struct st_maria_plugin *mariadb_builtins[]= { - builtin_binlog_plugin@mysql_plugin_defs@,(struct st_mysql_plugin *)0 + builtin_maria_binlog_plugin, builtin_maria_mysql_password_plugin@maria_plugin_defs@,(struct st_maria_plugin *)0 }; - diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 580fe8057cd..8afcb4bc4a6 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -106,6 +106,7 @@ Key::Key(const Key &rhs, MEM_ROOT *mem_root) key_create_info(rhs.key_create_info), columns(rhs.columns, mem_root), name(rhs.name), + option_list(rhs.option_list), generated(rhs.generated) { list_copy_and_replace_each_value(columns, mem_root); @@ -195,6 +196,59 @@ bool foreign_key_prefix(Key *a, Key *b) #endif } +/* + @brief + Check if the foreign key options are compatible with the specification + of the columns on which the key is created + + @retval + FALSE The foreign key options are compatible with key columns + @retval + TRUE Otherwise +*/ +bool Foreign_key::validate(List<Create_field> &table_fields) +{ + Create_field *sql_field; + Key_part_spec *column; + List_iterator<Key_part_spec> cols(columns); + List_iterator<Create_field> it(table_fields); + DBUG_ENTER("Foreign_key::validate"); + while ((column= cols++)) + { + it.rewind(); + while ((sql_field= it++) && + my_strcasecmp(system_charset_info, + column->field_name, + sql_field->field_name)) {} + if (!sql_field) + { + my_error(ER_KEY_COLUMN_DOES_NOT_EXITS, MYF(0), column->field_name); + DBUG_RETURN(TRUE); + } + if (type == Key::FOREIGN_KEY && sql_field->vcol_info) + { + if (delete_opt == FK_OPTION_SET_NULL) + { + my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0), + "ON DELETE SET NULL"); + DBUG_RETURN(TRUE); + } + if (update_opt == FK_OPTION_SET_NULL) + { + my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0), + "ON UPDATE SET NULL"); + DBUG_RETURN(TRUE); + } + if (update_opt == FK_OPTION_CASCADE) + { + my_error(ER_WRONG_FK_OPTION_FOR_VIRTUAL_COLUMN, MYF(0), + "ON UPDATE CASCADE"); + DBUG_RETURN(TRUE); + } + } + } + DBUG_RETURN(FALSE); +} /**************************************************************************** ** Thread specific functions @@ -649,6 +703,7 @@ THD::THD() mysys_var=0; binlog_evt_union.do_union= FALSE; enable_slow_log= 0; + #ifndef DBUG_OFF dbug_sentry=THD_SENTRY_MAGIC; #endif @@ -721,6 +776,7 @@ THD::THD() void THD::push_internal_handler(Internal_error_handler *handler) { + DBUG_ENTER("THD::push_internal_handler"); if (m_internal_handler) { handler->m_prev_internal_handler= m_internal_handler; @@ -730,6 +786,7 @@ void THD::push_internal_handler(Internal_error_handler *handler) { m_internal_handler= handler; } + DBUG_VOID_RETURN; } @@ -749,8 +806,10 @@ bool THD::handle_error(uint sql_errno, const char *message, void THD::pop_internal_handler() { + DBUG_ENTER("THD::pop_internal_handler"); DBUG_ASSERT(m_internal_handler != NULL); m_internal_handler= m_internal_handler->m_prev_internal_handler; + DBUG_VOID_RETURN; } extern "C" @@ -847,14 +906,65 @@ void THD::init(void) update_charset(); reset_current_stmt_binlog_row_based(); bzero((char *) &status_var, sizeof(status_var)); + bzero((char *) &org_status_var, sizeof(org_status_var)); sql_log_bin_toplevel= options & OPTION_BIN_LOG; - + select_commands= update_commands= other_commands= 0; + /* Set to handle counting of aborted connections */ + userstat_running= opt_userstat_running; + last_global_update_time= current_connect_time= time(NULL); #if defined(ENABLED_DEBUG_SYNC) /* Initialize the Debug Sync Facility. See debug_sync.cc. */ debug_sync_init_thread(this); #endif /* defined(ENABLED_DEBUG_SYNC) */ } + +/* Updates some status variables to be used by update_global_user_stats */ + +void THD::update_stats(void) +{ + /* sql_command == SQLCOM_END in case of parse errors or quit */ + if (lex->sql_command != SQLCOM_END) + { + /* A SQL query. */ + if (lex->sql_command == SQLCOM_SELECT) + select_commands++; + else if (sql_command_flags[lex->sql_command] & CF_STATUS_COMMAND) + { + /* Ignore 'SHOW ' commands */ + } + else if (is_update_query(lex->sql_command)) + update_commands++; + else + other_commands++; + } +} + + +void THD::update_all_stats() +{ + time_t save_time; + ulonglong end_cpu_time, end_utime; + double busy_time, cpu_time; + + /* This is set at start of query if opt_userstat_running was set */ + if (!userstat_running) + return; + + end_cpu_time= my_getcputime(); + end_utime= my_micro_time_and_time(&save_time); + busy_time= (end_utime - start_utime) / 1000000.0; + cpu_time= (end_cpu_time - start_cpu_time) / 10000000.0; + /* In case there are bad values, 2629743 is the #seconds in a month. */ + if (cpu_time > 2629743.0) + cpu_time= 0; + status_var_add(status_var.cpu_time, cpu_time); + status_var_add(status_var.busy_time, busy_time); + + update_global_user_stats(this, TRUE, save_time); + userstat_running= 0; +} + /* Init THD for query processing. @@ -1025,9 +1135,8 @@ THD::~THD() from_var from this array NOTES - This function assumes that all variables are long/ulong. - If this assumption will change, then we have to explictely add - the other variables after the while loop + This function assumes that all variables at start are long/ulong and + other types are handled explicitely */ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) @@ -1039,6 +1148,13 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) while (to != end) *(to++)+= *(from++); + + /* Handle the not ulong variables. See end of system_status_var */ + to_var->bytes_received= from_var->bytes_received; + to_var->bytes_sent+= from_var->bytes_sent; + to_var->binlog_bytes_written= from_var->binlog_bytes_written; + to_var->cpu_time+= from_var->cpu_time; + to_var->busy_time+= from_var->busy_time; } /* @@ -1051,7 +1167,8 @@ void add_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var) dec_var minus this array NOTE - This function assumes that all variables are long/ulong. + This function assumes that all variables at start are long/ulong and + other types are handled explicitely */ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, @@ -1064,6 +1181,14 @@ void add_diff_to_status(STATUS_VAR *to_var, STATUS_VAR *from_var, while (to != end) *(to++)+= *(from++) - *(dec++); + + to_var->bytes_received= (from_var->bytes_received - + dec_var->bytes_received); + to_var->bytes_sent+= from_var->bytes_sent - dec_var->bytes_sent; + to_var->binlog_bytes_written= (from_var->binlog_bytes_written - + dec_var->binlog_bytes_written); + to_var->cpu_time+= from_var->cpu_time - dec_var->cpu_time; + to_var->busy_time+= from_var->busy_time - dec_var->busy_time; } #define SECONDS_TO_WAIT_FOR_KILL 2 @@ -2837,7 +2962,8 @@ void thd_increment_bytes_sent(ulong length) { THD *thd=current_thd; if (likely(thd != 0)) - { /* current_thd==0 when close_connection() calls net_send_error() */ + { + /* current_thd == 0 when close_connection() calls net_send_error() */ thd->status_var.bytes_sent+= length; } } @@ -2863,9 +2989,9 @@ void THD::set_status_var_init() void Security_context::init() { - host= user= priv_user= ip= 0; + host= user= ip= 0; host_or_ip= "connecting host"; - priv_host[0]= '\0'; + priv_user[0]= priv_host[0]= '\0'; master_access= 0; #ifndef NO_EMBEDDED_ACCESS_CHECKS db_access= NO_ACCESS; @@ -2889,8 +3015,7 @@ void Security_context::skip_grants() /* privileges for the user are unknown everything is allowed */ host_or_ip= (char *)""; master_access= ~NO_ACCESS; - priv_user= (char *)""; - *priv_host= '\0'; + *priv_user= *priv_host= '\0'; } @@ -2932,7 +3057,7 @@ bool Security_context::set_user(char *user_arg) of a statement under credentials of a different user, e.g. definer of a procedure, we authenticate this user in a local instance of Security_context by means of this method (and - ultimately by means of acl_getroot_no_password), and make the + ultimately by means of acl_getroot), and make the local instance active in the thread by re-setting thd->security_ctx pointer. @@ -2966,19 +3091,12 @@ change_security_context(THD *thd, DBUG_ASSERT(definer_user->str && definer_host->str); *backup= NULL; - /* - The current security context may have NULL members - if we have just started the thread and not authenticated - any user. This use case is currently in events worker thread. - */ - needs_change= (thd->security_ctx->priv_user == NULL || - strcmp(definer_user->str, thd->security_ctx->priv_user) || - thd->security_ctx->priv_host == NULL || + needs_change= (strcmp(definer_user->str, thd->security_ctx->priv_user) || my_strcasecmp(system_charset_info, definer_host->str, thd->security_ctx->priv_host)); if (needs_change) { - if (acl_getroot_no_password(this, definer_user->str, definer_host->str, + if (acl_getroot(this, definer_user->str, definer_host->str, definer_host->str, db->str)) { my_error(ER_NO_SUCH_USER, MYF(0), definer_user->str, @@ -3067,7 +3185,7 @@ extern "C" unsigned long thd_get_thread_id(const MYSQL_THD thd) #ifdef INNODB_COMPATIBILITY_HOOKS -extern "C" struct charset_info_st *thd_charset(MYSQL_THD thd) +extern "C" const struct charset_info_st *thd_charset(MYSQL_THD thd) { return(thd->charset()); } diff --git a/sql/sql_class.h b/sql/sql_class.h index aa39ddb2b15..a46fa6cec98 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -205,13 +205,15 @@ public: KEY_CREATE_INFO key_create_info; List<Key_part_spec> columns; const char *name; + engine_option_value *option_list; bool generated; Key(enum Keytype type_par, const char *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) :type(type_par), key_create_info(*key_info_arg), columns(cols), - name(name_arg), generated(generated_arg) + name(name_arg), option_list(create_opt), generated(generated_arg) {} Key(const Key &rhs, MEM_ROOT *mem_root); virtual ~Key() {} @@ -240,7 +242,7 @@ public: Foreign_key(const char *name_arg, List<Key_part_spec> &cols, Table_ident *table, List<Key_part_spec> &ref_cols, uint delete_opt_arg, uint update_opt_arg, uint match_opt_arg) - :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols), + :Key(FOREIGN_KEY, name_arg, &default_key_create_info, 0, cols, NULL), ref_table(table), ref_columns(ref_cols), delete_opt(delete_opt_arg), update_opt(update_opt_arg), match_opt(match_opt_arg) @@ -252,6 +254,8 @@ public: */ virtual Key *clone(MEM_ROOT *mem_root) const { return new (mem_root) Foreign_key(*this, mem_root); } + /* Used to validate foreign key options */ + bool validate(List<Create_field> &table_fields); }; typedef struct st_mysql_lock @@ -419,8 +423,6 @@ struct system_variables typedef struct system_status_var { - ulonglong bytes_received; - ulonglong bytes_sent; ulong com_other; ulong com_stat[(uint) SQLCOM_END]; ulong created_tmp_disk_tables; @@ -459,6 +461,8 @@ typedef struct system_status_var ulong select_range_count; ulong select_range_check_count; ulong select_scan_count; + ulong rows_read; + ulong rows_sent; ulong long_query_count; ulong filesort_merge_passes; ulong filesort_range_count; @@ -476,6 +480,9 @@ typedef struct system_status_var Number of statements sent from the client */ ulong questions; + ulong empty_queries; + ulong access_denied_errors; /* Can only be 0 or 1 */ + ulong lost_connections; /* IMPORTANT! SEE last_system_status_var DEFINITION BELOW. @@ -484,12 +491,16 @@ typedef struct system_status_var Status variables which it does not make sense to add to global status variable counter */ + ulonglong bytes_received; + ulonglong bytes_sent; + ulonglong binlog_bytes_written; double last_query_cost; + double cpu_time, busy_time; } STATUS_VAR; /* This is used for 'SHOW STATUS'. It must be updated to the last ulong - variable in system_status_var which is makes sens to add to the global + variable in system_status_var which is makes sense to add to the global counter */ @@ -797,7 +808,8 @@ public: priv_user - The user privilege we are using. May be "" for anonymous user. ip - client IP */ - char *host, *user, *priv_user, *ip; + char *host, *user, *ip; + char priv_user[USERNAME_LENGTH]; /* The host privilege we are using */ char priv_host[MAX_HOSTNAME]; /* points to host if host is available, otherwise points to ip */ @@ -1334,6 +1346,7 @@ public: struct my_rnd_struct rand; // used for authentication struct system_variables variables; // Changeable local variables struct system_status_var status_var; // Per thread statistic vars + struct system_status_var org_status_var; // For user statistics struct system_status_var *initial_status_var; /* used by show status */ THR_LOCK_INFO lock_info; // Locking info of this thread THR_LOCK_OWNER main_lock_id; // To use for conventional queries @@ -1434,6 +1447,8 @@ public: uint in_sub_stmt; /* TRUE when the current top has SQL_LOG_BIN ON */ bool sql_log_bin_toplevel; + /* True when opt_userstat_running is set at start of query */ + bool userstat_running; /* container for handler's private per-connection data */ Ha_data ha_data[MAX_HA]; @@ -1877,6 +1892,21 @@ public: */ LOG_INFO* current_linfo; NET* slave_net; // network connection from slave -> m. + + /* + Used to update global user stats. The global user stats are updated + occasionally with the 'diff' variables. After the update, the 'diff' + variables are reset to 0. + */ + /* Time when the current thread connected to MySQL. */ + time_t current_connect_time; + /* Last time when THD stats were updated in global_user_stats. */ + time_t last_global_update_time; + /* Number of commands not reflected in global_user_stats yet. */ + uint select_commands, update_commands, other_commands; + ulonglong start_cpu_time; + ulonglong start_bytes_received; + /* Used by the sys_var class to store temporary values */ union { @@ -1942,6 +1972,8 @@ public: alloc_root. */ void init_for_queries(); + void update_all_stats(); + void update_stats(void); void change_user(void); void cleanup(void); void cleanup_after_query(); @@ -2364,7 +2396,6 @@ private: MEM_ROOT main_mem_root; }; - /** A short cut for thd->main_da.set_ok_status(). */ inline void diff --git a/sql/sql_connect.cc b/sql/sql_connect.cc index 88023bb8e71..30c6c4fc653 100644 --- a/sql/sql_connect.cc +++ b/sql/sql_connect.cc @@ -20,23 +20,12 @@ #include "mysql_priv.h" -#ifdef HAVE_OPENSSL -/* - Without SSL the handshake consists of one packet. This packet - has both client capabilites and scrambled password. - With SSL the handshake might consist of two packets. If the first - packet (client capabilities) has CLIENT_SSL flag set, we have to - switch to SSL and read the second packet. The scrambled password - is in the second packet and client_capabilites field will be ignored. - Maybe it is better to accept flags other than CLIENT_SSL from the - second packet? -*/ -#define SSL_HANDSHAKE_SIZE 2 -#define NORMAL_HANDSHAKE_SIZE 6 -#define MIN_HANDSHAKE_SIZE 2 -#else -#define MIN_HANDSHAKE_SIZE 6 -#endif /* HAVE_OPENSSL */ +HASH global_user_stats, global_client_stats, global_table_stats; +HASH global_index_stats; +/* Protects the above global stats */ +extern pthread_mutex_t LOCK_global_user_client_stats; +extern pthread_mutex_t LOCK_global_table_stats; +extern pthread_mutex_t LOCK_global_index_stats; #ifdef __WIN__ extern void win_install_sigabrt_handler(); @@ -49,9 +38,9 @@ extern void win_install_sigabrt_handler(); #ifndef NO_EMBEDDED_ACCESS_CHECKS static HASH hash_user_connections; -static int get_or_create_user_conn(THD *thd, const char *user, - const char *host, - USER_RESOURCES *mqh) +int get_or_create_user_conn(THD *thd, const char *user, + const char *host, + USER_RESOURCES *mqh) { int return_val= 0; size_t temp_len, user_len; @@ -117,7 +106,6 @@ end: 1 error */ -static int check_for_max_user_connections(THD *thd, USER_CONN *uc) { int error=0; @@ -269,236 +257,6 @@ end: #endif /* NO_EMBEDDED_ACCESS_CHECKS */ - -/** - Check if user exist and password supplied is correct. - - @param thd thread handle, thd->security_ctx->{host,user,ip} are used - @param command originator of the check: now check_user is called - during connect and change user procedures; used for - logging. - @param passwd scrambled password received from client - @param passwd_len length of scrambled password - @param db database name to connect to, may be NULL - @param check_count TRUE if establishing a new connection. In this case - check that we have not exceeded the global - max_connections limist - - @note Host, user and passwd may point to communication buffer. - Current implementation does not depend on that, but future changes - should be done with this in mind; 'thd' is INOUT, all other params - are 'IN'. - - @retval 0 OK; thd->security_ctx->user/master_access/priv_user/db_access and - thd->db are updated; OK is sent to the client. - @retval 1 error, e.g. access denied or handshake error, not sent to - the client. A message is pushed into the error stack. -*/ - -int -check_user(THD *thd, enum enum_server_command command, - const char *passwd, uint passwd_len, const char *db, - bool check_count) -{ - DBUG_ENTER("check_user"); - LEX_STRING db_str= { (char *) db, db ? strlen(db) : 0 }; - - /* - Clear thd->db as it points to something, that will be freed when - connection is closed. We don't want to accidentally free a wrong - pointer if connect failed. Also in case of 'CHANGE USER' failure, - current database will be switched to 'no database selected'. - */ - thd->reset_db(NULL, 0); - -#ifdef NO_EMBEDDED_ACCESS_CHECKS - thd->main_security_ctx.master_access= GLOBAL_ACLS; // Full rights - /* Change database if necessary */ - if (db && db[0]) - { - if (mysql_change_db(thd, &db_str, FALSE)) - DBUG_RETURN(1); - } - my_ok(thd); - DBUG_RETURN(0); -#else - - my_bool opt_secure_auth_local; - pthread_mutex_lock(&LOCK_global_system_variables); - opt_secure_auth_local= opt_secure_auth; - pthread_mutex_unlock(&LOCK_global_system_variables); - - /* - If the server is running in secure auth mode, short scrambles are - forbidden. - */ - if (opt_secure_auth_local && passwd_len == SCRAMBLE_LENGTH_323) - { - my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); - general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); - DBUG_RETURN(1); - } - if (passwd_len != 0 && - passwd_len != SCRAMBLE_LENGTH && - passwd_len != SCRAMBLE_LENGTH_323) - { - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - DBUG_RETURN(1); - } - - USER_RESOURCES ur; - int res= acl_getroot(thd, &ur, passwd, passwd_len); -#ifndef EMBEDDED_LIBRARY - if (res == -1) - { - /* - This happens when client (new) sends password scrambled with - scramble(), but database holds old value (scrambled with - scramble_323()). Here we please client to send scrambled_password - in old format. - */ - NET *net= &thd->net; - if (opt_secure_auth_local) - { - my_error(ER_SERVER_IS_IN_SECURE_AUTH_MODE, MYF(0), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip); - general_log_print(thd, COM_CONNECT, ER(ER_SERVER_IS_IN_SECURE_AUTH_MODE), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip); - DBUG_RETURN(1); - } - /* We have to read very specific packet size */ - if (send_old_password_request(thd) || - my_net_read(net) != SCRAMBLE_LENGTH_323 + 1) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - DBUG_RETURN(1); - } - /* Final attempt to check the user based on reply */ - /* So as passwd is short, errcode is always >= 0 */ - res= acl_getroot(thd, &ur, (char *) net->read_pos, SCRAMBLE_LENGTH_323); - } -#endif /*EMBEDDED_LIBRARY*/ - /* here res is always >= 0 */ - if (res == 0) - { - if (!(thd->main_security_ctx.master_access & - NO_ACCESS)) // authentication is OK - { - DBUG_PRINT("info", - ("Capabilities: %lu packet_length: %ld Host: '%s' " - "Login user: '%s' Priv_user: '%s' Using password: %s " - "Access: %lu db: '%s'", - thd->client_capabilities, - thd->max_client_packet_length, - thd->main_security_ctx.host_or_ip, - thd->main_security_ctx.user, - thd->main_security_ctx.priv_user, - passwd_len ? "yes": "no", - thd->main_security_ctx.master_access, - (thd->db ? thd->db : "*none*"))); - - if (check_count) - { - bool count_ok= 1; - - if (!(thd->main_security_ctx.master_access & SUPER_ACL)) - { - pthread_mutex_lock(&LOCK_connection_count); - count_ok= (*thd->scheduler->connection_count <= - *thd->scheduler->max_connections); - VOID(pthread_mutex_unlock(&LOCK_connection_count)); - } - if (!count_ok) - { // too many connections - my_error(ER_CON_COUNT_ERROR, MYF(0)); - DBUG_RETURN(1); - } - } - - /* - Log the command before authentication checks, so that the user can - check the log for the tried login tried and also to detect - break-in attempts. - */ - general_log_print(thd, command, - (thd->main_security_ctx.priv_user == - thd->main_security_ctx.user ? - (char*) "%s@%s on %s" : - (char*) "%s@%s as anonymous on %s"), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip, - db ? db : (char*) ""); - - /* - This is the default access rights for the current database. It's - set to 0 here because we don't have an active database yet (and we - may not have an active database to set. - */ - thd->main_security_ctx.db_access=0; - - /* Don't allow user to connect if he has done too many queries */ - if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn || - max_user_connections) && - get_or_create_user_conn(thd, - (opt_old_style_user_limits ? thd->main_security_ctx.user : - thd->main_security_ctx.priv_user), - (opt_old_style_user_limits ? thd->main_security_ctx.host_or_ip : - thd->main_security_ctx.priv_host), - &ur)) - { - /* The error is set by get_or_create_user_conn(). */ - DBUG_RETURN(1); - } - if (thd->user_connect && - (thd->user_connect->user_resources.conn_per_hour || - thd->user_connect->user_resources.user_conn || - max_user_connections) && - check_for_max_user_connections(thd, thd->user_connect)) - { - /* The error is set in check_for_max_user_connections(). */ - DBUG_RETURN(1); - } - - /* Change database if necessary */ - if (db && db[0]) - { - if (mysql_change_db(thd, &db_str, FALSE)) - { - /* mysql_change_db() has pushed the error message. */ - if (thd->user_connect) - decrease_user_connections(thd->user_connect); - DBUG_RETURN(1); - } - } - my_ok(thd); - thd->net.net_skip_rest_factor= 2; // skip at most 2*max_packet_size - thd->password= test(passwd_len); // remember for error messages - /* Ready to handle queries */ - DBUG_RETURN(0); - } - } - else if (res == 2) // client gave short hash, server has long hash - { - my_error(ER_NOT_SUPPORTED_AUTH_MODE, MYF(0)); - general_log_print(thd, COM_CONNECT, ER(ER_NOT_SUPPORTED_AUTH_MODE)); - DBUG_RETURN(1); - } - my_error(ER_ACCESS_DENIED_ERROR, MYF(0), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip, - passwd_len ? ER(ER_YES) : ER(ER_NO)); - general_log_print(thd, COM_CONNECT, ER(ER_ACCESS_DENIED_ERROR), - thd->main_security_ctx.user, - thd->main_security_ctx.host_or_ip, - passwd_len ? ER(ER_YES) : ER(ER_NO)); - DBUG_RETURN(1); -#endif /* NO_EMBEDDED_ACCESS_CHECKS */ -} - - /* Check for maximum allowable user connections, if the mysqld server is started with corresponding variable that is greater then 0. @@ -521,10 +279,14 @@ extern "C" void free_user(struct user_conn *uc) void init_max_user_conn(void) { #ifndef NO_EMBEDDED_ACCESS_CHECKS - (void) hash_init(&hash_user_connections,system_charset_info,max_connections, - 0,0, - (hash_get_key) get_key_conn, (hash_free_key) free_user, - 0); + if (hash_init(&hash_user_connections,system_charset_info,max_connections, + 0,0, + (hash_get_key) get_key_conn, (hash_free_key) free_user, + 0)) + { + sql_print_error("Initializing hash_user_connections failed."); + exit(1); + } #endif } @@ -577,6 +339,445 @@ void reset_mqh(LEX_USER *lu, bool get_them= 0) #endif /* NO_EMBEDDED_ACCESS_CHECKS */ } +/***************************************************************************** + Handle users statistics +*****************************************************************************/ + +/* 'mysql_system_user' is used for when the user is not defined for a THD. */ +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) +{ + return user ? user : mysql_system_user; +} + +/* + Returns string as 'IP' for the client-side of the connection represented by + 'client'. Does not allocate memory. May return "". +*/ + +static const char *get_client_host(THD *client) +{ + return client->security_ctx->host_or_ip[0] ? + client->security_ctx->host_or_ip : + client->security_ctx->host ? client->security_ctx->host : ""; +} + +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= user_stats->user_name_length; + return (uchar*) user_stats->user; +} + +void free_user_stats(USER_STATS* user_stats) +{ + my_free(user_stats, MYF(0)); +} + +void init_user_stats(USER_STATS *user_stats, + const char *user, + size_t user_length, + const char *priv_user, + uint total_connections, + uint concurrent_connections, + time_t connected_time, + double busy_time, + double cpu_time, + ulonglong bytes_received, + ulonglong bytes_sent, + ulonglong binlog_bytes_written, + ha_rows rows_sent, + ha_rows rows_read, + ha_rows rows_inserted, + ha_rows rows_deleted, + ha_rows rows_updated, + ulonglong select_commands, + ulonglong update_commands, + ulonglong other_commands, + ulonglong commit_trans, + ulonglong rollback_trans, + ulonglong denied_connections, + ulonglong lost_connections, + ulonglong access_denied_errors, + ulonglong empty_queries) +{ + DBUG_ENTER("init_user_stats"); + DBUG_PRINT("enter", ("user: %s priv_user: %s", user, priv_user)); + + user_length= min(user_length, sizeof(user_stats->user)-1); + memcpy(user_stats->user, user, user_length); + user_stats->user[user_length]= 0; + user_stats->user_name_length= user_length; + strmake(user_stats->priv_user, priv_user, sizeof(user_stats->priv_user)-1); + + user_stats->total_connections= total_connections; + user_stats->concurrent_connections= concurrent_connections; + user_stats->connected_time= connected_time; + user_stats->busy_time= busy_time; + user_stats->cpu_time= cpu_time; + user_stats->bytes_received= bytes_received; + user_stats->bytes_sent= bytes_sent; + user_stats->binlog_bytes_written= binlog_bytes_written; + user_stats->rows_sent= rows_sent; + user_stats->rows_updated= rows_updated; + user_stats->rows_read= rows_read; + user_stats->select_commands= select_commands; + user_stats->update_commands= update_commands; + user_stats->other_commands= other_commands; + user_stats->commit_trans= commit_trans; + user_stats->rollback_trans= rollback_trans; + user_stats->denied_connections= denied_connections; + user_stats->lost_connections= lost_connections; + user_stats->access_denied_errors= access_denied_errors; + user_stats->empty_queries= empty_queries; + DBUG_VOID_RETURN; +} + + +#ifdef COMPLEAT_PATCH_NOT_ADDED_YET + +void add_user_stats(USER_STATS *user_stats, + uint total_connections, + uint concurrent_connections, + time_t connected_time, + double busy_time, + double cpu_time, + ulonglong bytes_received, + ulonglong bytes_sent, + ulonglong binlog_bytes_written, + ha_rows rows_sent, + ha_rows rows_read, + ha_rows rows_inserted, + ha_rows rows_deleted, + ha_rows rows_updated, + ulonglong select_commands, + ulonglong update_commands, + ulonglong other_commands, + ulonglong commit_trans, + ulonglong rollback_trans, + ulonglong denied_connections, + ulonglong lost_connections, + ulonglong access_denied_errors, + ulonglong empty_queries) +{ + user_stats->total_connections+= total_connections; + user_stats->concurrent_connections+= concurrent_connections; + user_stats->connected_time+= connected_time; + user_stats->busy_time+= busy_time; + user_stats->cpu_time+= cpu_time; + user_stats->bytes_received+= bytes_received; + user_stats->bytes_sent+= bytes_sent; + user_stats->binlog_bytes_written+= binlog_bytes_written; + user_stats->rows_sent+= rows_sent; + user_stats->rows_inserted+= rows_inserted; + user_stats->rows_deleted+= rows_deleted; + user_stats->rows_updated+= rows_updated; + user_stats->rows_read+= rows_read; + user_stats->select_commands+= select_commands; + user_stats->update_commands+= update_commands; + user_stats->other_commands+= other_commands; + user_stats->commit_trans+= commit_trans; + user_stats->rollback_trans+= rollback_trans; + user_stats->denied_connections+= denied_connections; + user_stats->lost_connections+= lost_connections; + user_stats->access_denied_errors+= access_denied_errors; + user_stats->empty_queries+= empty_queries; +} +#endif + + +void init_global_user_stats(void) +{ + if (hash_init(&global_user_stats, system_charset_info, max_connections, + 0, 0, (hash_get_key) get_key_user_stats, + (hash_free_key)free_user_stats, 0)) + { + sql_print_error("Initializing global_user_stats failed."); + exit(1); + } +} + +void init_global_client_stats(void) +{ + if (hash_init(&global_client_stats, system_charset_info, max_connections, + 0, 0, (hash_get_key) get_key_user_stats, + (hash_free_key)free_user_stats, 0)) + { + sql_print_error("Initializing global_client_stats failed."); + exit(1); + } +} + +extern "C" uchar *get_key_table_stats(TABLE_STATS *table_stats, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= table_stats->table_name_length; + return (uchar*) table_stats->table; +} + +extern "C" void free_table_stats(TABLE_STATS* table_stats) +{ + my_free(table_stats, MYF(0)); +} + +void init_global_table_stats(void) +{ + if (hash_init(&global_table_stats, system_charset_info, max_connections, + 0, 0, (hash_get_key) get_key_table_stats, + (hash_free_key)free_table_stats, 0)) { + sql_print_error("Initializing global_table_stats failed."); + exit(1); + } +} + +extern "C" uchar *get_key_index_stats(INDEX_STATS *index_stats, size_t *length, + my_bool not_used __attribute__((unused))) +{ + *length= index_stats->index_name_length; + return (uchar*) index_stats->index; +} + +extern "C" void free_index_stats(INDEX_STATS* index_stats) +{ + my_free(index_stats, MYF(0)); +} + +void init_global_index_stats(void) +{ + if (hash_init(&global_index_stats, system_charset_info, max_connections, + 0, 0, (hash_get_key) get_key_index_stats, + (hash_free_key)free_index_stats, 0)) + { + sql_print_error("Initializing global_index_stats failed."); + exit(1); + } +} + + +void free_global_user_stats(void) +{ + hash_free(&global_user_stats); +} + +void free_global_table_stats(void) +{ + hash_free(&global_table_stats); +} + +void free_global_index_stats(void) +{ + hash_free(&global_index_stats); +} + +void free_global_client_stats(void) +{ + hash_free(&global_client_stats); +} + +/* + Increments the global stats connection count for an entry from + global_client_stats or global_user_stats. Returns 0 on success + and 1 on error. +*/ + +static bool increment_count_by_name(const char *name, size_t name_length, + const char *role_name, + HASH *users_or_clients, THD *thd) +{ + USER_STATS *user_stats; + + if (!(user_stats= (USER_STATS*) hash_search(users_or_clients, (uchar*) name, + name_length))) + { + /* First connection for this user or client */ + if (!(user_stats= ((USER_STATS*) + my_malloc(sizeof(USER_STATS), + MYF(MY_WME | MY_ZEROFILL))))) + return TRUE; // Out of memory + + init_user_stats(user_stats, name, name_length, role_name, + 0, 0, // connections + 0, 0, 0, // time + 0, 0, 0, // bytes sent, received and written + 0, 0, // Rows sent and read + 0, 0, 0, // rows inserted, deleted and updated + 0, 0, 0, // select, update and other commands + 0, 0, // commit and rollback trans + thd->status_var.access_denied_errors, + 0, // lost connections + 0, // access denied errors + 0); // empty queries + + if (my_hash_insert(users_or_clients, (uchar*)user_stats)) + { + my_free(user_stats, 0); + return TRUE; // Out of memory + } + } + user_stats->total_connections++; + return FALSE; +} + + +/* + Increments the global user and client stats connection count. + + @param use_lock if true, LOCK_global_user_client_stats will be locked + + @retval 0 ok + @retval 1 error. +*/ + +#ifndef EMBEDDED_LIBRARY +static bool increment_connection_count(THD* thd, bool use_lock) +{ + const char *user_string= get_valid_user_string(thd->main_security_ctx.user); + const char *client_string= get_client_host(thd); + bool return_value= FALSE; + + if (!thd->userstat_running) + return FALSE; + + if (use_lock) + pthread_mutex_lock(&LOCK_global_user_client_stats); + + if (increment_count_by_name(user_string, strlen(user_string), user_string, + &global_user_stats, thd)) + { + return_value= TRUE; + goto end; + } + if (increment_count_by_name(client_string, strlen(client_string), + user_string, &global_client_stats, thd)) + { + return_value= TRUE; + goto end; + } + +end: + if (use_lock) + pthread_mutex_unlock(&LOCK_global_user_client_stats); + return return_value; +} +#endif + +/* + Used to update the global user and client stats +*/ + +static void update_global_user_stats_with_user(THD *thd, + USER_STATS *user_stats, + time_t now) +{ + DBUG_ASSERT(thd->userstat_running); + + user_stats->connected_time+= now - thd->last_global_update_time; + user_stats->busy_time+= (thd->status_var.busy_time - + thd->org_status_var.busy_time); + user_stats->cpu_time+= (thd->status_var.cpu_time - + thd->org_status_var.cpu_time); + /* + This is handle specially as bytes_recieved is incremented BEFORE + org_status_var is copied. + */ + user_stats->bytes_received+= (thd->org_status_var.bytes_received- + thd->start_bytes_received); + user_stats->bytes_sent+= (thd->status_var.bytes_sent - + thd->org_status_var.bytes_sent); + user_stats->binlog_bytes_written+= + (thd->status_var.binlog_bytes_written - + thd->org_status_var.binlog_bytes_written); + user_stats->rows_read+= (thd->status_var.rows_read - + thd->org_status_var.rows_read); + user_stats->rows_sent+= (thd->status_var.rows_sent - + thd->org_status_var.rows_sent); + user_stats->rows_inserted+= (thd->status_var.ha_write_count - + thd->org_status_var.ha_write_count); + user_stats->rows_deleted+= (thd->status_var.ha_delete_count - + thd->org_status_var.ha_delete_count); + user_stats->rows_updated+= (thd->status_var.ha_update_count - + thd->org_status_var.ha_update_count); + user_stats->select_commands+= thd->select_commands; + user_stats->update_commands+= thd->update_commands; + user_stats->other_commands+= thd->other_commands; + user_stats->commit_trans+= (thd->status_var.ha_commit_count - + thd->org_status_var.ha_commit_count); + user_stats->rollback_trans+= (thd->status_var.ha_rollback_count + + thd->status_var.ha_savepoint_rollback_count - + thd->org_status_var.ha_rollback_count - + thd->org_status_var. + ha_savepoint_rollback_count); + user_stats->access_denied_errors+= + (thd->status_var.access_denied_errors - + thd->org_status_var.access_denied_errors); + user_stats->empty_queries+= (thd->status_var.empty_queries - + thd->org_status_var.empty_queries); + + /* The following can only contain 0 or 1 and then connection ends */ + user_stats->denied_connections+= thd->status_var.access_denied_errors; + user_stats->lost_connections+= thd->status_var.lost_connections; +} + + +/* Updates the global stats of a user or client */ +void update_global_user_stats(THD *thd, bool create_user, time_t now) +{ + const char *user_string, *client_string; + USER_STATS *user_stats; + size_t user_string_length, client_string_length; + DBUG_ASSERT(thd->userstat_running); + + user_string= get_valid_user_string(thd->main_security_ctx.user); + user_string_length= strlen(user_string); + client_string= get_client_host(thd); + client_string_length= strlen(client_string); + + pthread_mutex_lock(&LOCK_global_user_client_stats); + + // Update by user name + if ((user_stats= (USER_STATS*) hash_search(&global_user_stats, + (uchar*) user_string, + user_string_length))) + { + /* Found user. */ + update_global_user_stats_with_user(thd, user_stats, now); + } + else + { + /* Create the entry */ + if (create_user) + { + increment_count_by_name(user_string, user_string_length, user_string, + &global_user_stats, thd); + } + } + + /* Update by client IP */ + if ((user_stats= (USER_STATS*)hash_search(&global_client_stats, + (uchar*) client_string, + client_string_length))) + { + // Found by client IP + update_global_user_stats_with_user(thd, user_stats, now); + } + else + { + // Create the entry + if (create_user) + { + increment_count_by_name(client_string, client_string_length, + user_string, &global_client_stats, thd); + } + } + /* Reset variables only used for counting */ + thd->select_commands= thd->update_commands= thd->other_commands= 0; + thd->last_global_update_time= now; + + pthread_mutex_unlock(&LOCK_global_user_client_stats); +} + void thd_init_client_charset(THD *thd, uint cs_number) { @@ -634,9 +835,8 @@ bool init_new_connection_handler_thread() thd thread handle RETURN - 0 success, OK is sent to user, thd is updated. - -1 error, which is sent to user - > 0 error code (not sent to user) + 0 success, thd is updated. + 1 error */ #ifndef EMBEDDED_LIBRARY @@ -644,8 +844,6 @@ static int check_connection(THD *thd) { uint connect_errors= 0; NET *net= &thd->net; - ulong pkt_len= 0; - char *end; DBUG_PRINT("info", ("New connection received on %s", vio_description(net->vio))); @@ -707,200 +905,10 @@ static int check_connection(THD *thd) } vio_keepalive(net->vio, TRUE); - ulong server_capabilites; - { - /* buff[] needs to big enough to hold the server_version variable */ - char buff[SERVER_VERSION_LENGTH + 1 + SCRAMBLE_LENGTH + 1 + 64]; - server_capabilites= CLIENT_BASIC_FLAGS; - - if (opt_using_transactions) - server_capabilites|= CLIENT_TRANSACTIONS; -#ifdef HAVE_COMPRESS - server_capabilites|= CLIENT_COMPRESS; -#endif /* HAVE_COMPRESS */ -#ifdef HAVE_OPENSSL - if (ssl_acceptor_fd) - { - server_capabilites |= CLIENT_SSL; /* Wow, SSL is available! */ - server_capabilites |= CLIENT_SSL_VERIFY_SERVER_CERT; - } -#endif /* HAVE_OPENSSL */ - - end= strnmov(buff, server_version, SERVER_VERSION_LENGTH) + 1; - int4store((uchar*) end, thd->thread_id); - end+= 4; - /* - So as check_connection is the only entry point to authorization - procedure, scramble is set here. This gives us new scramble for - each handshake. - */ - create_random_string(thd->scramble, SCRAMBLE_LENGTH, &thd->rand); - /* - Old clients does not understand long scrambles, but can ignore packet - tail: that's why first part of the scramble is placed here, and second - part at the end of packet. - */ - end= strmake(end, thd->scramble, SCRAMBLE_LENGTH_323) + 1; - - int2store(end, server_capabilites); - /* write server characteristics: up to 16 bytes allowed */ - end[2]=(char) default_charset_info->number; - int2store(end+3, thd->server_status); - bzero(end+5, 13); - end+= 18; - /* write scramble tail */ - end= strmake(end, thd->scramble + SCRAMBLE_LENGTH_323, - SCRAMBLE_LENGTH - SCRAMBLE_LENGTH_323) + 1; - - /* At this point we write connection message and read reply */ - if (net_write_command(net, (uchar) protocol_version, (uchar*) "", 0, - (uchar*) buff, (size_t) (end-buff)) || - (pkt_len= my_net_read(net)) == packet_error || - pkt_len < MIN_HANDSHAKE_SIZE) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), - thd->main_security_ctx.host_or_ip); - return 1; - } - } -#ifdef _CUSTOMCONFIG_ -#include "_cust_sql_parse.h" -#endif - if (connect_errors) - reset_host_errors(&thd->remote.sin_addr); if (thd->packet.alloc(thd->variables.net_buffer_length)) return 1; /* The error is set by alloc(). */ - thd->client_capabilities= uint2korr(net->read_pos); - if (thd->client_capabilities & CLIENT_PROTOCOL_41) - { - thd->client_capabilities|= ((ulong) uint2korr(net->read_pos+2)) << 16; - thd->max_client_packet_length= uint4korr(net->read_pos+4); - DBUG_PRINT("info", ("client_character_set: %d", (uint) net->read_pos[8])); - thd_init_client_charset(thd, (uint) net->read_pos[8]); - thd->update_charset(); - end= (char*) net->read_pos+32; - } - else - { - thd->max_client_packet_length= uint3korr(net->read_pos+2); - end= (char*) net->read_pos+5; - } - /* - Disable those bits which are not supported by the server. - This is a precautionary measure, if the client lies. See Bug#27944. - */ - thd->client_capabilities&= server_capabilites; - - if (thd->client_capabilities & CLIENT_IGNORE_SPACE) - thd->variables.sql_mode|= MODE_IGNORE_SPACE; -#ifdef HAVE_OPENSSL - DBUG_PRINT("info", ("client capabilities: %lu", thd->client_capabilities)); - if (thd->client_capabilities & CLIENT_SSL) - { - char error_string[1024]; - /* Do the SSL layering. */ - if (!ssl_acceptor_fd) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } - DBUG_PRINT("info", ("IO layer change in progress...")); - if (sslaccept(ssl_acceptor_fd, net->vio, net->read_timeout, error_string)) - { - DBUG_PRINT("error", ("Failed to accept new SSL connection")); - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } - DBUG_PRINT("info", ("Reading user information over SSL layer")); - if ((pkt_len= my_net_read(net)) == packet_error || - pkt_len < NORMAL_HANDSHAKE_SIZE) - { - DBUG_PRINT("error", ("Failed to read user information (pkt_len= %lu)", - pkt_len)); - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } - } -#endif /* HAVE_OPENSSL */ - - if (end >= (char*) net->read_pos+ pkt_len +2) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } - - if (thd->client_capabilities & CLIENT_INTERACTIVE) - thd->variables.net_wait_timeout= thd->variables.net_interactive_timeout; - if ((thd->client_capabilities & CLIENT_TRANSACTIONS) && - opt_using_transactions) - net->return_status= &thd->server_status; - - char *user= end; - char *passwd= strend(user)+1; - uint user_len= passwd - user - 1; - char *db= passwd; - char db_buff[NAME_LEN + 1]; // buffer to store db in utf8 - char user_buff[USERNAME_LENGTH + 1]; // buffer to store user in utf8 - uint dummy_errors; - - /* - Old clients send null-terminated string as password; new clients send - the size (1 byte) + string (not null-terminated). Hence in case of empty - password both send '\0'. - - This strlen() can't be easily deleted without changing protocol. - - Cast *passwd to an unsigned char, so that it doesn't extend the sign for - *passwd > 127 and become 2**32-127+ after casting to uint. - */ - uint passwd_len= thd->client_capabilities & CLIENT_SECURE_CONNECTION ? - (uchar)(*passwd++) : strlen(passwd); - db= thd->client_capabilities & CLIENT_CONNECT_WITH_DB ? - db + passwd_len + 1 : 0; - /* strlen() can't be easily deleted without changing protocol */ - uint db_len= db ? strlen(db) : 0; - - if (passwd + passwd_len + db_len > (char *)net->read_pos + pkt_len) - { - inc_host_errors(&thd->remote.sin_addr); - my_error(ER_HANDSHAKE_ERROR, MYF(0), thd->main_security_ctx.host_or_ip); - return 1; - } - - /* Since 4.1 all database names are stored in utf8 */ - if (db) - { - db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, - system_charset_info, - db, db_len, - thd->charset(), &dummy_errors)]= 0; - db= db_buff; - } - - user_buff[user_len= copy_and_convert(user_buff, sizeof(user_buff)-1, - system_charset_info, user, user_len, - thd->charset(), &dummy_errors)]= '\0'; - user= user_buff; - - /* If username starts and ends in "'", chop them off */ - if (user_len > 1 && user[0] == '\'' && user[user_len - 1] == '\'') - { - user[user_len-1]= 0; - user++; - user_len-= 2; - } - - if (thd->main_security_ctx.user) - x_free(thd->main_security_ctx.user); - if (!(thd->main_security_ctx.user= my_strdup(user, MYF(MY_WME)))) - return 1; /* The error is set by my_strdup(). */ - return check_user(thd, COM_CONNECT, passwd, passwd_len, db, TRUE); + return acl_authenticate(thd, connect_errors, 0); } @@ -972,6 +980,14 @@ bool login_connection(THD *thd) /* Connect completed, set read/write timeouts back to default */ my_net_set_read_timeout(net, thd->variables.net_read_timeout); my_net_set_write_timeout(net, thd->variables.net_write_timeout); + + /* Updates global user connection stats. */ + if (increment_connection_count(thd, TRUE)) + { + net_send_error(thd, ER_OUTOFMEMORY); // Out of memory + DBUG_RETURN(1); + } + DBUG_RETURN(0); } @@ -993,6 +1009,7 @@ void end_connection(THD *thd) if (thd->killed || (net->error && net->vio != 0)) { statistic_increment(aborted_threads,&LOCK_status); + status_var_increment(thd->status_var.lost_connections); } if (net->error && net->vio != 0) @@ -1119,10 +1136,14 @@ pthread_handler_t handle_one_connection(void *arg) for (;;) { NET *net= &thd->net; + bool create_user= TRUE; lex_start(thd); if (login_connection(thd)) + { + create_user= FALSE; goto end_thread; + } prepare_new_connection_state(thd); @@ -1136,12 +1157,14 @@ pthread_handler_t handle_one_connection(void *arg) end_thread: close_connection(thd, 0, 1); + if (thd->userstat_running) + update_global_user_stats(thd, create_user, time(NULL)); + if (thd->scheduler->end_thread(thd,1)) return 0; // Probably no-threads /* - If end_thread() returns, we are either running with - thread-handler=no-threads or this thread has been schedule to + If end_thread() returns, this thread has been schedule to handle the next connection. */ thd= current_thd; diff --git a/sql/sql_cursor.cc b/sql/sql_cursor.cc index 6f61dc40f66..71c28f425d4 100644 --- a/sql/sql_cursor.cc +++ b/sql/sql_cursor.cc @@ -655,7 +655,7 @@ void Materialized_cursor::fetch(ulong num_rows) result->begin_dataset(); for (fetch_limit+= num_rows; fetch_count < fetch_limit; fetch_count++) { - if ((res= table->file->rnd_next(table->record[0]))) + if ((res= table->file->ha_rnd_next(table->record[0]))) break; /* Send data only if the read was successful. */ result->send_data(item_list); diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc index 3bbf4b78d07..2f3ce99ab9c 100644 --- a/sql/sql_handler.cc +++ b/sql/sql_handler.cc @@ -562,8 +562,8 @@ retry: if (table->file->inited != handler::NONE) { error=keyname ? - table->file->index_next(table->record[0]) : - table->file->rnd_next(table->record[0]); + table->file->ha_index_next(table->record[0]) : + table->file->ha_rnd_next(table->record[0]); break; } /* else fall through */ @@ -572,13 +572,13 @@ retry: { table->file->ha_index_or_rnd_end(); table->file->ha_index_init(keyno, 1); - error= table->file->index_first(table->record[0]); + error= table->file->ha_index_first(table->record[0]); } else { table->file->ha_index_or_rnd_end(); if (!(error= table->file->ha_rnd_init(1))) - error= table->file->rnd_next(table->record[0]); + error= table->file->ha_rnd_next(table->record[0]); } mode=RNEXT; break; @@ -586,7 +586,7 @@ retry: DBUG_ASSERT(keyname != 0); if (table->file->inited != handler::NONE) { - error=table->file->index_prev(table->record[0]); + error=table->file->ha_index_prev(table->record[0]); break; } /* else fall through */ @@ -594,13 +594,13 @@ retry: DBUG_ASSERT(keyname != 0); table->file->ha_index_or_rnd_end(); table->file->ha_index_init(keyno, 1); - error= table->file->index_last(table->record[0]); + error= table->file->ha_index_last(table->record[0]); mode=RPREV; break; case RNEXT_SAME: /* Continue scan on "(keypart1,keypart2,...)=(c1, c2, ...) */ DBUG_ASSERT(keyname != 0); - error= table->file->index_next_same(table->record[0], key, key_len); + error= table->file->ha_index_next_same(table->record[0], key, key_len); break; case RKEY: { @@ -640,8 +640,8 @@ retry: table->file->ha_index_or_rnd_end(); table->file->ha_index_init(keyno, 1); key_copy(key, table->record[0], table->key_info + keyno, key_len); - error= table->file->index_read_map(table->record[0], - key, keypart_map, ha_rkey_mode); + error= table->file->ha_index_read_map(table->record[0], + key, keypart_map, ha_rkey_mode); mode=rkey_to_rnext[(int)ha_rkey_mode]; break; } @@ -663,6 +663,8 @@ retry: } goto ok; } + /* Generate values for virtual fields */ + update_virtual_fields(table); if (cond && !cond->val_int()) continue; if (num_rows >= offset_limit_cnt) diff --git a/sql/sql_help.cc b/sql/sql_help.cc index 7eff6a5e0fa..5658a3578ab 100644 --- a/sql/sql_help.cc +++ b/sql/sql_help.cc @@ -294,13 +294,13 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations, rkey_id->store((longlong) key_id, TRUE); rkey_id->get_key_image(buff, rkey_id->pack_length(), Field::itRAW); - int key_res= relations->file->index_read_map(relations->record[0], - buff, (key_part_map) 1, - HA_READ_KEY_EXACT); + int key_res= relations->file->ha_index_read_map(relations->record[0], + buff, (key_part_map) 1, + HA_READ_KEY_EXACT); for ( ; !key_res && key_id == (int16) rkey_id->val_int() ; - key_res= relations->file->index_next(relations->record[0])) + key_res= relations->file->ha_index_next(relations->record[0])) { uchar topic_id_buff[8]; longlong topic_id= rtopic_id->val_int(); @@ -308,8 +308,8 @@ int get_topics_for_keyword(THD *thd, TABLE *topics, TABLE *relations, field->store((longlong) topic_id, TRUE); field->get_key_image(topic_id_buff, field->pack_length(), Field::itRAW); - if (!topics->file->index_read_map(topics->record[0], topic_id_buff, - (key_part_map)1, HA_READ_KEY_EXACT)) + if (!topics->file->ha_index_read_map(topics->record[0], topic_id_buff, + (key_part_map)1, HA_READ_KEY_EXACT)) { memorize_variant_topic(thd,topics,count,find_fields, names,name,description,example); diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index fc53529da37..eec271d6e13 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -72,15 +72,6 @@ static void unlink_blobs(register TABLE *table); #endif static bool check_view_insertability(THD *thd, TABLE_LIST *view); -/* Define to force use of my_malloc() if the allocated memory block is big */ - -#ifndef HAVE_ALLOCA -#define my_safe_alloca(size, min_length) my_alloca(size) -#define my_safe_afree(ptr, size, min_length) my_afree(ptr) -#else -#define my_safe_alloca(size, min_length) ((size <= min_length) ? my_alloca(size) : my_malloc(size,MYF(0))) -#define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0)) -#endif /* Check that insert/update fields are from the same single table of a view. @@ -280,6 +271,9 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, } } } + /* Mark virtual columns used in the insert statement */ + if (table->vfield) + table->mark_virtual_columns_for_write(); // For the values we need select_priv #ifndef NO_EMBEDDED_ACCESS_CHECKS table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); @@ -1450,7 +1444,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) goto err; if (table->file->ha_table_flags() & HA_DUPLICATE_POS) { - if (table->file->rnd_pos(table->record[1],table->file->dup_ref)) + if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref)) goto err; } else @@ -1471,9 +1465,10 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) } } key_copy((uchar*) key,table->record[0],table->key_info+key_nr,0); - if ((error=(table->file->index_read_idx_map(table->record[1],key_nr, - (uchar*) key, HA_WHOLE_KEY, - HA_READ_KEY_EXACT)))) + if ((error= (table->file->ha_index_read_idx_map(table->record[1], + key_nr, (uchar*) key, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)))) goto err; } if (info->handle_duplicates == DUP_UPDATE) @@ -1774,8 +1769,10 @@ public: table(0),tables_in_use(0),stacked_inserts(0), status(0), dead(0), group_count(0) { - thd.security_ctx->user=thd.security_ctx->priv_user=(char*) delayed_user; + thd.security_ctx->user=(char*) delayed_user; thd.security_ctx->host=(char*) my_localhost; + strmake(thd.security_ctx->priv_user, thd.security_ctx->user, + USERNAME_LENGTH); thd.current_tablenr=0; thd.version=refresh_version; thd.command=COM_DELAYED_INSERT; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 5a3907d0f7f..63ff2e05817 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -340,6 +340,7 @@ void lex_start(THD *thd) lex->reset_query_tables_list(FALSE); lex->expr_allows_subselect= TRUE; lex->use_only_table_context= FALSE; + lex->parse_vcol_expr= FALSE; lex->name.str= 0; lex->name.length= 0; @@ -789,9 +790,9 @@ int MYSQLlex(void *arg, void *yythd) Lex_input_stream *lip= & thd->m_parser_state->m_lip; LEX *lex= thd->lex; YYSTYPE *yylval=(YYSTYPE*) arg; - CHARSET_INFO *cs= thd->charset(); - uchar *state_map= cs->state_map; - uchar *ident_map= cs->ident_map; + CHARSET_INFO *const cs= thd->charset(); + const uchar *const state_map= cs->state_map; + const uchar *const ident_map= cs->ident_map; LINT_INIT(c); lip->yylval=yylval; // The global state diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 5e37e618250..22934ad684e 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -118,6 +118,8 @@ enum enum_sql_command { SQLCOM_SHOW_CREATE_TRIGGER, SQLCOM_ALTER_DB_UPGRADE, SQLCOM_SHOW_PROFILE, SQLCOM_SHOW_PROFILES, + SQLCOM_SHOW_USER_STATS, SQLCOM_SHOW_TABLE_STATS, SQLCOM_SHOW_INDEX_STATS, + SQLCOM_SHOW_CLIENT_STATS, /* When a command is added here, be sure it's also added in mysqld.cc @@ -950,6 +952,8 @@ extern sys_var *trg_new_row_fake_var; enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE, XA_SUSPEND, XA_FOR_MIGRATE}; +extern const LEX_STRING null_lex_str; +extern const LEX_STRING empty_lex_str; /* Class representing list of all tables used by statement. @@ -1544,6 +1548,7 @@ typedef struct st_lex : public Query_tables_list LEX_USER *grant_user; XID *xid; THD *thd; + Virtual_column_info *vcol_info; /* maintain a list of used plugins for this LEX */ DYNAMIC_ARRAY plugins; @@ -1626,6 +1631,14 @@ typedef struct st_lex : public Query_tables_list syntax error back. */ bool expr_allows_subselect; + /* + A special command "PARSE_VCOL_EXPR" is defined for the parser + to translate a defining expression of a virtual column into an + Item object. + The following flag is used to prevent other applications to use + this command. + */ + bool parse_vcol_expr; thr_lock_type lock_option; enum SSL_type ssl_type; /* defined in violite.h */ @@ -1736,6 +1749,11 @@ typedef struct st_lex : public Query_tables_list const char *stmt_definition_end; /** + Collects create options for Field and KEY + */ + engine_option_value *option_list, *option_list_last; + + /** During name resolution search only in the table list given by Name_resolution_context::first_name_resolution_table and Name_resolution_context::last_name_resolution_table diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 9503e8a5d81..ffc1157719b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -338,10 +338,14 @@ void init_update_queries(void) sql_command_flags[SQLCOM_SHOW_CREATE_EVENT]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_PROFILES]= CF_STATUS_COMMAND; sql_command_flags[SQLCOM_SHOW_PROFILE]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_CLIENT_STATS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_USER_STATS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_TABLE_STATS]= CF_STATUS_COMMAND; + sql_command_flags[SQLCOM_SHOW_INDEX_STATS]= CF_STATUS_COMMAND; - sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND | - CF_SHOW_TABLE_COMMAND | - CF_REEXECUTION_FRAGILE); + sql_command_flags[SQLCOM_SHOW_TABLES]= (CF_STATUS_COMMAND | + CF_SHOW_TABLE_COMMAND | + CF_REEXECUTION_FRAGILE); sql_command_flags[SQLCOM_SHOW_TABLE_STATUS]= (CF_STATUS_COMMAND | CF_SHOW_TABLE_COMMAND | CF_REEXECUTION_FRAGILE); @@ -440,9 +444,8 @@ static void handle_bootstrap_impl(THD *thd) thd_proc_info(thd, 0); thd->version=refresh_version; - thd->security_ctx->priv_user= - thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME)); - thd->security_ctx->priv_host[0]=0; + thd->security_ctx->user= (char*) my_strdup("boot", MYF(MY_WME)); + thd->security_ctx->priv_user[0]= thd->security_ctx->priv_host[0]=0; /* Make the "client" handle multiple results. This is necessary to enable stored procedures with SELECTs and Dynamic SQL @@ -571,7 +574,6 @@ end: return 0; } - /** @brief Check access privs for a MERGE table and fix children lock types. @@ -825,6 +827,8 @@ bool do_command(THD *thd) net_new_transaction(net); + /* Save for user statistics */ + thd->start_bytes_received= thd->status_var.bytes_received; packet_length= my_net_read(net); #if defined(ENABLED_PROFILING) && defined(COMMUNITY_SERVER) thd->profiling.start_new_query(); @@ -1088,96 +1092,34 @@ bool dispatch_command(enum enum_server_command command, THD *thd, case COM_CHANGE_USER: { status_var_increment(thd->status_var.com_other); - char *user= (char*) packet, *packet_end= packet + packet_length; - /* Safe because there is always a trailing \0 at the end of the packet */ - char *passwd= strend(user)+1; thd->change_user(); thd->clear_error(); // if errors from rollback - /* - Old clients send null-terminated string ('\0' for empty string) for - password. New clients send the size (1 byte) + string (not null - terminated, so also '\0' for empty string). + /* acl_authenticate() takes the data from net->read_pos */ + net->read_pos= (uchar*)packet; - Cast *passwd to an unsigned char, so that it doesn't extend the sign - for *passwd > 127 and become 2**32-127 after casting to uint. - */ - char db_buff[NAME_LEN+1]; // buffer to store db in utf8 - char *db= passwd; - char *save_db; - /* - If there is no password supplied, the packet must contain '\0', - in any type of handshake (4.1 or pre-4.1). - */ - if (passwd >= packet_end) - { - my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); - break; - } - uint passwd_len= (thd->client_capabilities & CLIENT_SECURE_CONNECTION ? - (uchar)(*passwd++) : strlen(passwd)); - uint dummy_errors, save_db_length, db_length; - int res; + uint save_db_length= thd->db_length; + char *save_db= thd->db; + USER_CONN *save_user_connect= thd->user_connect; Security_context save_security_ctx= *thd->security_ctx; - USER_CONN *save_user_connect; - - db+= passwd_len + 1; - /* - Database name is always NUL-terminated, so in case of empty database - the packet must contain at least the trailing '\0'. - */ - if (db >= packet_end) - { - my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); - break; - } - db_length= strlen(db); - - char *ptr= db + db_length + 1; - uint cs_number= 0; + CHARSET_INFO *save_character_set_client= + thd->variables.character_set_client; + CHARSET_INFO *save_collation_connection= + thd->variables.collation_connection; + CHARSET_INFO *save_character_set_results= + thd->variables.character_set_results; - if (ptr < packet_end) - { - if (ptr + 2 > packet_end) - { - my_message(ER_UNKNOWN_COM_ERROR, ER(ER_UNKNOWN_COM_ERROR), MYF(0)); - break; - } - - cs_number= uint2korr(ptr); - } - - /* Convert database name to utf8 */ - db_buff[copy_and_convert(db_buff, sizeof(db_buff)-1, - system_charset_info, db, db_length, - thd->charset(), &dummy_errors)]= 0; - db= db_buff; - - /* Save user and privileges */ - save_db_length= thd->db_length; - save_db= thd->db; - save_user_connect= thd->user_connect; - - if (!(thd->security_ctx->user= my_strdup(user, MYF(0)))) - { - thd->security_ctx->user= save_security_ctx.user; - my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0)); - break; - } - - /* Clear variables that are allocated */ - thd->user_connect= 0; - thd->security_ctx->priv_user= thd->security_ctx->user; - res= check_user(thd, COM_CHANGE_USER, passwd, passwd_len, db, FALSE); - - if (res) + if (acl_authenticate(thd, 0, packet_length)) { x_free(thd->security_ctx->user); *thd->security_ctx= save_security_ctx; thd->user_connect= save_user_connect; - thd->db= save_db; - thd->db_length= save_db_length; + thd->reset_db(save_db, save_db_length); + thd->variables.character_set_client= save_character_set_client; + thd->variables.collation_connection= save_collation_connection; + thd->variables.character_set_results= save_character_set_results; + thd->update_charset(); } else { @@ -1188,12 +1130,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, #endif /* NO_EMBEDDED_ACCESS_CHECKS */ x_free(save_db); x_free(save_security_ctx.user); - - if (cs_number) - { - thd_init_client_charset(thd, cs_number); - thd->update_charset(); - } } break; } @@ -1349,7 +1285,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, table_list.select_lex= &(thd->lex->select_lex); lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); thd->lex-> select_lex.table_list.link_in_list((uchar*) &table_list, @@ -1659,6 +1595,9 @@ bool dispatch_command(enum enum_server_command command, THD *thd, /* Free tables */ close_thread_tables(thd); + /* Update status; Must be done after close_thread_tables */ + thd->update_all_stats(); + if (sql_command_flags[thd->lex->sql_command] & CF_CHANGES_DATA) { net_end_statement(thd); @@ -1834,6 +1773,12 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident, thd->profiling.discard_current_query(); #endif break; + case SCH_USER_STATS: + case SCH_CLIENT_STATS: + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL)) + DBUG_RETURN(1); + case SCH_TABLE_STATS: + case SCH_INDEX_STATS: case SCH_OPEN_TABLES: case SCH_VARIABLES: case SCH_STATUS: @@ -2275,6 +2220,10 @@ mysql_execute_command(THD *thd) case SQLCOM_SHOW_COLLATIONS: case SQLCOM_SHOW_STORAGE_ENGINES: case SQLCOM_SHOW_PROFILE: + case SQLCOM_SHOW_CLIENT_STATS: + case SQLCOM_SHOW_USER_STATS: + case SQLCOM_SHOW_TABLE_STATS: + case SQLCOM_SHOW_INDEX_STATS: case SQLCOM_SELECT: thd->status_var.last_query_cost= 0.0; if (all_tables) @@ -4330,8 +4279,8 @@ end_with_restore_list: if (sp_grant_privileges(thd, lex->sphead->m_db.str, name, lex->sql_command == SQLCOM_CREATE_PROCEDURE)) push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, - ER_PROC_AUTO_GRANT_FAIL, - ER(ER_PROC_AUTO_GRANT_FAIL)); + ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL)); + thd->clear_error(); } /* @@ -5022,6 +4971,7 @@ create_sp_error: break; } thd_proc_info(thd, "query end"); + thd->update_stats(); /* Binlog-related cleanup: @@ -5115,6 +5065,10 @@ static bool execute_sqlcom_select(THD *thd, TABLE_LIST *all_tables) delete result; } } + /* Count number of empty select queries */ + if (!thd->sent_row_count) + status_var_increment(thd->status_var.empty_queries); + status_var_add(thd->status_var.rows_sent, thd->sent_row_count); return res; } @@ -5274,6 +5228,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, if (!no_errors) { const char *db_name= db ? db : thd->db; + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, db_name); } @@ -5306,12 +5261,15 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, { // We can never grant this DBUG_PRINT("error",("No possible access")); if (!no_errors) + { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_ACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, (thd->password ? ER(ER_YES) : ER(ER_NO))); /* purecov: tested */ + } DBUG_RETURN(TRUE); /* purecov: tested */ } @@ -5337,11 +5295,14 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, DBUG_PRINT("error",("Access denied")); if (!no_errors) + { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, (db ? db : (thd->db ? thd->db : "unknown"))); /* purecov: tested */ + } DBUG_RETURN(TRUE); /* purecov: tested */ } @@ -5370,6 +5331,7 @@ static bool check_show_access(THD *thd, TABLE_LIST *table) if (!thd->col_access && check_grant_db(thd, dst_db_name)) { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), thd->security_ctx->priv_user, thd->security_ctx->priv_host, @@ -5432,14 +5394,14 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, { TABLE_LIST *org_tables= tables; TABLE_LIST *first_not_own_table= thd->lex->first_not_own_table(); - uint i= 0; Security_context *sctx= thd->security_ctx, *backup_ctx= thd->security_ctx; + uint i; /* The check that first_not_own_table is not reached is for the case when the given table list refers to the list for prelocking (contains tables of other queries). For simple queries first_not_own_table is 0. */ - for (; i < number && tables != first_not_own_table; + for (i=0; i < number && tables != first_not_own_table; tables= tables->next_global, i++) { if (tables->security_ctx) @@ -5451,9 +5413,12 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, (want_access & ~(SELECT_ACL | EXTRA_ACL | FILE_ACL))) { if (!no_errors) + { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->priv_host, INFORMATION_SCHEMA_NAME.str); + } return TRUE; } /* @@ -5617,6 +5582,7 @@ bool check_global_access(THD *thd, ulong want_access) return 0; get_privilege_desc(command, sizeof(command), want_access); my_error(ER_SPECIFIC_ACCESS_DENIED_ERROR, MYF(0), command); + status_var_increment(thd->status_var.access_denied_errors); return 1; #else return 0; @@ -5720,7 +5686,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) Call it after we use THD for queries, not before. */ -void mysql_reset_thd_for_next_command(THD *thd) +void mysql_reset_thd_for_next_command(THD *thd, my_bool calculate_userstat) { DBUG_ENTER("mysql_reset_thd_for_next_command"); DBUG_ASSERT(!thd->spcont); /* not for substatements of routines */ @@ -5765,6 +5731,15 @@ void mysql_reset_thd_for_next_command(THD *thd) thd->total_warn_count=0; // Warnings for this query thd->rand_used= 0; thd->sent_row_count= thd->examined_row_count= 0; + + /* Copy data for user stats */ + if ((thd->userstat_running= calculate_userstat)) + { + thd->start_cpu_time= my_getcputime(); + memcpy(&thd->org_status_var, &thd->status_var, sizeof(thd->status_var)); + thd->select_commands= thd->update_commands= thd->other_commands= 0; + } + thd->query_plan_flags= QPLAN_INIT; thd->query_plan_fsort_passes= 0; @@ -5963,7 +5938,6 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, const char ** found_semicolon) { DBUG_ENTER("mysql_parse"); - DBUG_EXECUTE_IF("parser_debug", turn_parser_debug_on();); /* @@ -5983,7 +5957,7 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, FIXME: cleanup the dependencies in the code to simplify this. */ lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); if (query_cache_send_result_to_client(thd, (char*) inBuf, length) <= 0) { @@ -6056,10 +6030,11 @@ void mysql_parse(THD *thd, const char *inBuf, uint length, } else { + /* Update statistics for getting the query from the cache */ + thd->lex->sql_command= SQLCOM_SELECT; /* There are no multi queries in the cache. */ *found_semicolon= NULL; } - DBUG_VOID_RETURN; } @@ -6083,7 +6058,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) Parser_state parser_state(thd, inBuf, length); lex_start(thd); - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, 0); if (!parse_sql(thd, & parser_state, NULL) && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) @@ -6110,7 +6085,9 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, LEX_STRING *comment, char *change, List<String> *interval_list, CHARSET_INFO *cs, - uint uint_geom_type) + uint uint_geom_type, + Virtual_column_info *vcol_info, + engine_option_value *create_options) { register Create_field *new_field; LEX *lex= thd->lex; @@ -6128,7 +6105,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); key= new Key(Key::PRIMARY, NullS, &default_key_create_info, - 0, lex->col_list); + 0, lex->col_list, NULL); lex->alter_info.key_list.push_back(key); lex->col_list.empty(); } @@ -6138,7 +6115,7 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, lex->col_list.push_back(new Key_part_spec(field_name->str, 0)); key= new Key(Key::UNIQUE, NullS, &default_key_create_info, 0, - lex->col_list); + lex->col_list, NULL); lex->alter_info.key_list.push_back(key); lex->col_list.empty(); } @@ -6196,7 +6173,8 @@ bool add_field_to_list(THD *thd, LEX_STRING *field_name, enum_field_types type, if (!(new_field= new Create_field()) || new_field->init(thd, field_name->str, type, length, decimals, type_modifier, default_value, on_update_value, comment, change, - interval_list, cs, uint_geom_type)) + interval_list, cs, uint_geom_type, vcol_info, + create_options)) DBUG_RETURN(1); lex->alter_info.create_list.push_back(new_field); @@ -6922,6 +6900,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, if (flush_error_log()) result=1; } + if (((options & (REFRESH_SLOW_QUERY_LOG | REFRESH_LOG)) == + REFRESH_SLOW_QUERY_LOG)) + { + /* We are only flushing slow query log */ + logger.flush_slow_log(thd); + } + #ifdef HAVE_QUERY_CACHE if (options & REFRESH_QUERY_CACHE_FREE) { @@ -7004,29 +6989,58 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables, } #endif #ifdef OPENSSL - if (options & REFRESH_DES_KEY_FILE) - { - if (des_key_file && load_des_key_file(des_key_file)) - result= 1; - } + if (options & REFRESH_DES_KEY_FILE) + { + if (des_key_file && load_des_key_file(des_key_file)) + result= 1; + } #endif #ifdef HAVE_REPLICATION - if (options & REFRESH_SLAVE) - { - tmp_write_to_binlog= 0; - pthread_mutex_lock(&LOCK_active_mi); - if (reset_slave(thd, active_mi)) - result=1; - pthread_mutex_unlock(&LOCK_active_mi); - } + if (options & REFRESH_SLAVE) + { + tmp_write_to_binlog= 0; + pthread_mutex_lock(&LOCK_active_mi); + if (reset_slave(thd, active_mi)) + result=1; + pthread_mutex_unlock(&LOCK_active_mi); + } #endif - if (options & REFRESH_USER_RESOURCES) - reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ - *write_to_binlog= tmp_write_to_binlog; - /* - If the query was killed then this function must fail. - */ - return result || (thd ? thd->killed : 0); + if (options & REFRESH_USER_RESOURCES) + reset_mqh((LEX_USER *) NULL, 0); /* purecov: inspected */ + if (options & REFRESH_TABLE_STATS) + { + pthread_mutex_lock(&LOCK_global_table_stats); + free_global_table_stats(); + init_global_table_stats(); + pthread_mutex_unlock(&LOCK_global_table_stats); + } + if (options & REFRESH_INDEX_STATS) + { + pthread_mutex_lock(&LOCK_global_index_stats); + free_global_index_stats(); + init_global_index_stats(); + pthread_mutex_unlock(&LOCK_global_index_stats); + } + if (options & (REFRESH_USER_STATS | REFRESH_CLIENT_STATS)) + { + pthread_mutex_lock(&LOCK_global_user_client_stats); + if (options & REFRESH_USER_STATS) + { + free_global_user_stats(); + init_global_user_stats(); + } + if (options & REFRESH_CLIENT_STATS) + { + free_global_client_stats(); + init_global_client_stats(); + } + pthread_mutex_unlock(&LOCK_global_user_client_stats); + } + *write_to_binlog= tmp_write_to_binlog; + /* + If the query was killed then this function must fail. + */ + return result || (thd ? thd->killed : 0); } @@ -7062,7 +7076,6 @@ uint kill_one_thread(THD *thd, ulong id, bool only_kill_query) VOID(pthread_mutex_unlock(&LOCK_thread_count)); if (tmp) { - /* If we're SUPER, we can KILL anything, including system-threads. No further checks. @@ -7647,8 +7660,9 @@ void get_default_definer(THD *thd, LEX_USER *definer) definer->host.str= (char *) sctx->priv_host; definer->host.length= strlen(definer->host.str); - definer->password.str= NULL; - definer->password.length= 0; + definer->password= null_lex_str; + definer->plugin= empty_lex_str; + definer->auth= empty_lex_str; } diff --git a/sql/sql_partition.cc b/sql/sql_partition.cc index 143073a67b5..e26197a3096 100644 --- a/sql/sql_partition.cc +++ b/sql/sql_partition.cc @@ -984,7 +984,8 @@ static bool fix_fields_part_func(THD *thd, Item* func_expr, TABLE *table, saved_allow_sum_func= thd->lex->allow_sum_func; thd->lex->allow_sum_func= 0; - error= func_expr->fix_fields(thd, (Item**)&func_expr); + if (!(error= func_expr->fix_fields(thd, (Item**)&func_expr))) + func_expr->walk(&Item::vcol_in_partition_func_processor, 0, NULL); /* Restore full_group_by_flag and allow_sum_func, diff --git a/sql/sql_plugin.cc b/sql/sql_plugin.cc index 4ead793737b..e8dab5fdbae 100644 --- a/sql/sql_plugin.cc +++ b/sql/sql_plugin.cc @@ -1,4 +1,4 @@ -/* Copyright (C) 2005 MySQL AB +/* Copyright (C) 2005 MySQL AB, 2009 Sun Microsystems, Inc. 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 @@ -16,10 +16,11 @@ #include "mysql_priv.h" #include <my_pthread.h> #include <my_getopt.h> +#include <mysql/plugin_auth.h> #define REPORT_TO_LOG 1 #define REPORT_TO_USER 2 -extern struct st_mysql_plugin *mysqld_builtins[]; +extern struct st_maria_plugin *mariadb_builtins[]; /** @note The order of the enumeration is critical. @@ -46,7 +47,10 @@ const LEX_STRING plugin_type_names[MYSQL_MAX_PLUGIN_TYPE_NUM]= { C_STRING_WITH_LEN("STORAGE ENGINE") }, { C_STRING_WITH_LEN("FTPARSER") }, { C_STRING_WITH_LEN("DAEMON") }, - { C_STRING_WITH_LEN("INFORMATION SCHEMA") } + { C_STRING_WITH_LEN("INFORMATION SCHEMA") }, + { C_STRING_WITH_LEN("AUDIT") }, + { C_STRING_WITH_LEN("REPLICATION") }, + { C_STRING_WITH_LEN("AUTHENTICATION") } }; extern int initialize_schema_table(st_plugin_int *plugin); @@ -59,12 +63,12 @@ extern int finalize_schema_table(st_plugin_int *plugin); */ plugin_type_init plugin_type_initialize[MYSQL_MAX_PLUGIN_TYPE_NUM]= { - 0,ha_initialize_handlerton,0,0,initialize_schema_table + 0,ha_initialize_handlerton,0,0,initialize_schema_table, 0, 0, 0 }; plugin_type_init plugin_type_deinitialize[MYSQL_MAX_PLUGIN_TYPE_NUM]= { - 0,ha_finalize_handlerton,0,0,finalize_schema_table + 0,ha_finalize_handlerton,0,0,finalize_schema_table, 0, 0, 0 }; #ifdef HAVE_DLOPEN @@ -74,6 +78,14 @@ static const char *sizeof_st_plugin_sym= "_mysql_sizeof_struct_st_plugin_"; static const char *plugin_declarations_sym= "_mysql_plugin_declarations_"; static int min_plugin_interface_version= MYSQL_PLUGIN_INTERFACE_VERSION & ~0xFF; +static const char *maria_plugin_interface_version_sym= + "_maria_plugin_interface_version_"; +static const char *maria_sizeof_st_plugin_sym= + "_maria_sizeof_struct_st_plugin_"; +static const char *maria_plugin_declarations_sym= + "_maria_plugin_declarations_"; +static int min_maria_plugin_interface_version= + MARIA_PLUGIN_INTERFACE_VERSION & ~0xFF; #endif /* Note that 'int version' must be the first field of every plugin @@ -85,7 +97,10 @@ static int min_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= MYSQL_HANDLERTON_INTERFACE_VERSION, MYSQL_FTPARSER_INTERFACE_VERSION, MYSQL_DAEMON_INTERFACE_VERSION, - MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION + MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION, + 0xff00, /* audit plugins are supported in a later versions */ + 0xff00, /* replication plugins are supported in a later versions */ + MYSQL_AUTHENTICATION_INTERFACE_VERSION }; static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= { @@ -93,10 +108,15 @@ static int cur_plugin_info_interface_version[MYSQL_MAX_PLUGIN_TYPE_NUM]= MYSQL_HANDLERTON_INTERFACE_VERSION, MYSQL_FTPARSER_INTERFACE_VERSION, MYSQL_DAEMON_INTERFACE_VERSION, - MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION + MYSQL_INFORMATION_SCHEMA_INTERFACE_VERSION, + 0x0000, /* audit plugins are supported in a later versions */ + 0x0000, /* replication plugins are supported in a later versions */ + MYSQL_AUTHENTICATION_INTERFACE_VERSION }; -static bool initialized= 0; +/* support for Services */ + +#include "sql_plugin_services.h" /* A mutex LOCK_plugin must be acquired before accessing the @@ -110,6 +130,8 @@ static HASH plugin_hash[MYSQL_MAX_PLUGIN_TYPE_NUM]; static bool reap_needed= false; static int plugin_array_version=0; +static bool initialized= 0; + /* write-lock on LOCK_system_variables_hash is required before modifying the following variables/structures @@ -197,7 +219,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, const char *list); static int test_plugin_options(MEM_ROOT *, struct st_plugin_int *, int *, char **); -static bool register_builtin(struct st_mysql_plugin *, struct st_plugin_int *, +static bool register_builtin(struct st_maria_plugin *, struct st_plugin_int *, struct st_plugin_int **); static void unlock_variables(THD *thd, struct system_variables *vars); static void cleanup_variables(THD *thd, struct system_variables *vars); @@ -222,6 +244,22 @@ extern bool throw_bounds_warning(THD *thd, bool fixed, bool unsignd, extern bool check_if_table_exists(THD *thd, TABLE_LIST *table, bool *exists); #endif /* EMBEDDED_LIBRARY */ +static void report_error(int where_to, uint error, ...) +{ + va_list args; + if (where_to & REPORT_TO_USER) + { + va_start(args, error); + my_printv_error(error, ER(error), MYF(0), args); + va_end(args); + } + if (where_to & REPORT_TO_LOG) + { + va_start(args, error); + error_log_print(ERROR_LEVEL, ER(error), args); + va_end(args); + } +} /**************************************************************************** Value type thunks, allows the C world to play in the C++ world @@ -338,11 +376,230 @@ static inline void free_plugin_mem(struct st_plugin_dl *p) } +/** + Reads data from mysql plugin interface + + @param plugin_dl Structure where the data should be put + @param sym Reverence on version info + @param dlpath Path to the module + @param report What errors should be reported + + @retval FALSE OK + @retval TRUE ERROR +*/ + +#ifdef HAVE_DLOPEN +static my_bool read_mysql_plugin_info(struct st_plugin_dl *plugin_dl, + void *sym, char *dlpath, + int report) +{ + DBUG_ENTER("read_maria_plugin_info"); + /* Determine interface version */ + if (!sym) + { + free_plugin_mem(plugin_dl); + report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_interface_version_sym); + DBUG_RETURN(TRUE); + } + plugin_dl->mariaversion= 0; + plugin_dl->mysqlversion= *(int *)sym; + /* Versioning */ + if (plugin_dl->mysqlversion < min_plugin_interface_version || + (plugin_dl->mysqlversion >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8)) + { + free_plugin_mem(plugin_dl); + report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, + "plugin interface version mismatch"); + DBUG_RETURN(TRUE); + } + /* Find plugin declarations */ + if (!(sym= dlsym(plugin_dl->handle, plugin_declarations_sym))) + { + free_plugin_mem(plugin_dl); + report_error(report, ER_CANT_FIND_DL_ENTRY, plugin_declarations_sym); + DBUG_RETURN(TRUE); + } + + /* convert mysql declaration to maria one */ + { + int i; + uint sizeof_st_plugin; + struct st_mysql_plugin *old; + struct st_maria_plugin *cur; + char *ptr= (char *)sym; + + if ((sym= dlsym(plugin_dl->handle, sizeof_st_plugin_sym))) + sizeof_st_plugin= *(int *)sym; + else + { + DBUG_ASSERT(min_plugin_interface_version == 0); + sizeof_st_plugin= (int)offsetof(struct st_mysql_plugin, version); + } + + for (i= 0; + ((struct st_mysql_plugin *)(ptr + i * sizeof_st_plugin))->info; + i++) + /* no op */; + + cur= (struct st_maria_plugin*) + my_malloc((i + 1) * sizeof(struct st_maria_plugin), + MYF(MY_ZEROFILL|MY_WME)); + if (!cur) + { + free_plugin_mem(plugin_dl); + report_error(report, ER_OUTOFMEMORY, plugin_dl->dl.length); + DBUG_RETURN(TRUE); + } + /* + All st_plugin fields not initialized in the plugin explicitly, are + set to 0. It matches C standard behaviour for struct initializers that + have less values than the struct definition. + */ + for (i=0; + (old= (struct st_mysql_plugin *)(ptr + i * sizeof_st_plugin))->info; + i++) + { + + cur->type= old->type; + cur->info= old->info; + cur->name= old->name; + cur->author= old->author; + cur->descr= old->descr; + cur->license= old->license; + cur->init= old->init; + cur->deinit= old->deinit; + cur->version= old->version; + cur->status_vars= old->status_vars; + cur->system_vars= old->system_vars; + /* + Something like this should be added to process + new mysql plugin versions: + if (plugin_dl->mysqlversion > 0x0101) + { + cur->newfield= CONSTANT_MEANS_UNKNOWN; + } + else + { + cur->newfield= old->newfield; + } + */ + /* Maria only fields */ + cur->version_info= "Unknown"; + cur->maturity= MariaDB_PLUGIN_MATURITY_UNKNOWN; + } + plugin_dl->allocated= true; + plugin_dl->plugins= (struct st_maria_plugin *)cur; + } + + DBUG_RETURN(FALSE); +} + + +/** + Reads data from maria plugin interface + + @param plugin_dl Structure where the data should be put + @param sym Reverence on version info + @param dlpath Path to the module + @param report what errors should be reported + + @retval FALSE OK + @retval TRUE ERROR +*/ + +static my_bool read_maria_plugin_info(struct st_plugin_dl *plugin_dl, + void *sym, char *dlpath, + int report) +{ + DBUG_ENTER("read_maria_plugin_info"); + + /* Determine interface version */ + if (!(sym)) + { + /* + Actually this branch impossible because in case of absence of maria + version we try mysql version. + */ + free_plugin_mem(plugin_dl); + report_error(report, ER_CANT_FIND_DL_ENTRY, + maria_plugin_interface_version_sym); + DBUG_RETURN(TRUE); + } + plugin_dl->mariaversion= *(int *)sym; + plugin_dl->mysqlversion= 0; + /* Versioning */ + if (plugin_dl->mariaversion < min_maria_plugin_interface_version || + (plugin_dl->mariaversion >> 8) > (MARIA_PLUGIN_INTERFACE_VERSION >> 8)) + { + free_plugin_mem(plugin_dl); + report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, + "plugin interface version mismatch"); + DBUG_RETURN(TRUE); + } + /* Find plugin declarations */ + if (!(sym= dlsym(plugin_dl->handle, maria_plugin_declarations_sym))) + { + free_plugin_mem(plugin_dl); + report_error(report, ER_CANT_FIND_DL_ENTRY, maria_plugin_declarations_sym); + DBUG_RETURN(TRUE); + } + if (plugin_dl->mariaversion != MARIA_PLUGIN_INTERFACE_VERSION) + { + uint sizeof_st_plugin; + struct st_maria_plugin *old, *cur; + char *ptr= (char *)sym; + + if ((sym= dlsym(plugin_dl->handle, maria_sizeof_st_plugin_sym))) + sizeof_st_plugin= *(int *)sym; + else + { + free_plugin_mem(plugin_dl); + report_error(report, ER_CANT_FIND_DL_ENTRY, maria_sizeof_st_plugin_sym); + DBUG_RETURN(TRUE); + } + + if (sizeof_st_plugin != sizeof(st_mysql_plugin)) + { + int i; + for (i= 0; + ((struct st_maria_plugin *)(ptr + i * sizeof_st_plugin))->info; + i++) + /* no op */; + + cur= (struct st_maria_plugin*) + my_malloc((i + 1) * sizeof(struct st_maria_plugin), + MYF(MY_ZEROFILL|MY_WME)); + if (!cur) + { + free_plugin_mem(plugin_dl); + report_error(report, ER_OUTOFMEMORY, plugin_dl->dl.length); + DBUG_RETURN(TRUE); + } + /* + All st_plugin fields not initialized in the plugin explicitly, are + set to 0. It matches C standard behaviour for struct initializers that + have less values than the struct definition. + */ + for (i=0; + (old= (struct st_maria_plugin *)(ptr + i * sizeof_st_plugin))->info; + i++) + memcpy(cur + i, old, min(sizeof(cur[i]), sizeof_st_plugin)); + + sym= cur; + plugin_dl->allocated= true; + } + } + plugin_dl->plugins= (struct st_maria_plugin *)sym; + + DBUG_RETURN(FALSE); +} +#endif /* HAVE_DLOPEN */ + static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) { #ifdef HAVE_DLOPEN char dlpath[FN_REFLEN]; - uint plugin_dir_len, dummy_errors, dlpathlen; + uint plugin_dir_len, dummy_errors, dlpathlen, i; struct st_plugin_dl *tmp, plugin_dl; void *sym; DBUG_ENTER("plugin_dl_add"); @@ -357,10 +614,7 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) system_charset_info, 1) || plugin_dir_len + dl->length + 1 >= FN_REFLEN) { - if (report & REPORT_TO_USER) - my_error(ER_UDF_NO_PATHS, MYF(0)); - if ((report & (REPORT_TO_LOG | REPORT_TO_USER)) == REPORT_TO_LOG) - sql_print_error("%s", ER(ER_UDF_NO_PATHS)); + report_error(report, ER_UDF_NO_PATHS); DBUG_RETURN(0); } /* If this dll is already loaded just increase ref_count. */ @@ -385,118 +639,52 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (*errmsg == ':') errmsg++; if (*errmsg == ' ') errmsg++; } - if (report & REPORT_TO_USER) - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, errno, errmsg); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dlpath, errno, errmsg); - DBUG_RETURN(0); - } - /* Determine interface version */ - if (!(sym= dlsym(plugin_dl.handle, plugin_interface_version_sym))) - { - free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_interface_version_sym); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), plugin_interface_version_sym); + report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, errno, errmsg); DBUG_RETURN(0); } - plugin_dl.version= *(int *)sym; - /* Versioning */ - if (plugin_dl.version < min_plugin_interface_version || - (plugin_dl.version >> 8) > (MYSQL_PLUGIN_INTERFACE_VERSION >> 8)) + + /* Checks which plugin interface present and reads info */ + if (!(sym= dlsym(plugin_dl.handle, maria_plugin_interface_version_sym))) { - free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dlpath, 0, - "plugin interface version mismatch"); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dlpath, 0, - "plugin interface version mismatch"); - DBUG_RETURN(0); + if (read_mysql_plugin_info(&plugin_dl, + dlsym(plugin_dl.handle, + plugin_interface_version_sym), + dlpath, + report)) + DBUG_RETURN(0); } - /* Find plugin declarations */ - if (!(sym= dlsym(plugin_dl.handle, plugin_declarations_sym))) + else { - free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), plugin_declarations_sym); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), plugin_declarations_sym); - DBUG_RETURN(0); + if (read_maria_plugin_info(&plugin_dl, sym, dlpath, report)) + DBUG_RETURN(0); } - if (plugin_dl.version != MYSQL_PLUGIN_INTERFACE_VERSION) + /* link the services in */ + for (i= 0; i < array_elements(list_of_services); i++) { - int i; - uint sizeof_st_plugin; - struct st_mysql_plugin *old, *cur; - char *ptr= (char *)sym; - - if ((sym= dlsym(plugin_dl.handle, sizeof_st_plugin_sym))) - sizeof_st_plugin= *(int *)sym; - else - { -#ifdef ERROR_ON_NO_SIZEOF_PLUGIN_SYMBOL - free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), sizeof_st_plugin_sym); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), sizeof_st_plugin_sym); - DBUG_RETURN(0); -#else - /* - When the following assert starts failing, we'll have to switch - to the upper branch of the #ifdef - */ - DBUG_ASSERT(min_plugin_interface_version == 0); - sizeof_st_plugin= (int)offsetof(struct st_mysql_plugin, version); -#endif - } - - if (sizeof_st_plugin != sizeof(st_mysql_plugin)) + if ((sym= dlsym(plugin_dl.handle, list_of_services[i].name))) { - for (i= 0; - ((struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info; - i++) - /* no op */; - - cur= (struct st_mysql_plugin*) - my_malloc((i+1)*sizeof(struct st_mysql_plugin), MYF(MY_ZEROFILL|MY_WME)); - if (!cur) + uint ver= (uint)(intptr) *(void **)sym; + if (ver > list_of_services[i].version || + (ver >> 8) < (list_of_services[i].version >> 8)) { - free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_OUTOFMEMORY), plugin_dl.dl.length); + char buf[MYSQL_ERRMSG_SIZE]; + my_snprintf(buf, sizeof(buf), + "service '%s' interface version mismatch", + list_of_services[i].name); + report_error(report, ER_CANT_OPEN_LIBRARY, dlpath, 0, buf); DBUG_RETURN(0); } - /* - All st_plugin fields not initialized in the plugin explicitly, are - set to 0. It matches C standard behaviour for struct initializers that - have less values than the struct definition. - */ - for (i=0; - (old=(struct st_mysql_plugin *)(ptr+i*sizeof_st_plugin))->info; - i++) - memcpy(cur+i, old, min(sizeof(cur[i]), sizeof_st_plugin)); - - sym= cur; - plugin_dl.allocated= true; + *(void **)sym= list_of_services[i].service; } } - plugin_dl.plugins= (struct st_mysql_plugin *)sym; /* Duplicate and convert dll name */ plugin_dl.dl.length= dl->length * files_charset_info->mbmaxlen + 1; if (! (plugin_dl.dl.str= (char*) my_malloc(plugin_dl.dl.length, MYF(0)))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_OUTOFMEMORY, MYF(0), plugin_dl.dl.length); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_OUTOFMEMORY), plugin_dl.dl.length); + report_error(report, ER_OUTOFMEMORY, plugin_dl.dl.length); DBUG_RETURN(0); } plugin_dl.dl.length= copy_and_convert(plugin_dl.dl.str, plugin_dl.dl.length, @@ -507,19 +695,13 @@ static st_plugin_dl *plugin_dl_add(const LEX_STRING *dl, int report) if (! (tmp= plugin_dl_insert_or_reuse(&plugin_dl))) { free_plugin_mem(&plugin_dl); - if (report & REPORT_TO_USER) - my_error(ER_OUTOFMEMORY, MYF(0), sizeof(struct st_plugin_dl)); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_OUTOFMEMORY), sizeof(struct st_plugin_dl)); + report_error(report, ER_OUTOFMEMORY, sizeof(struct st_plugin_dl)); DBUG_RETURN(0); } DBUG_RETURN(tmp); #else DBUG_ENTER("plugin_dl_add"); - if (report & REPORT_TO_USER) - my_error(ER_FEATURE_DISABLED, MYF(0), "plugin", "HAVE_DLOPEN"); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_FEATURE_DISABLED), "plugin", "HAVE_DLOPEN"); + report_error(report, ER_FEATURE_DISABLED, "plugin", "HAVE_DLOPEN"); DBUG_RETURN(0); #endif } @@ -638,7 +820,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO) /* For debugging, we do an additional malloc which allows the memory manager and/or valgrind to track locked references and - double unlocks to aid resolving reference counting.problems. + double unlocks to aid resolving reference counting problems. */ if (!(plugin= (plugin_ref) my_malloc_ci(sizeof(pi), MYF(MY_WME)))) DBUG_RETURN(NULL); @@ -646,7 +828,7 @@ static plugin_ref intern_plugin_lock(LEX *lex, plugin_ref rc CALLER_INFO_PROTO) *plugin= pi; #endif pi->ref_count++; - DBUG_PRINT("info",("thd: 0x%lx plugin: \"%s\" ref_count: %d", + DBUG_PRINT("lock",("thd: 0x%lx plugin: \"%s\" LOCK ref_count: %d", (long) current_thd, pi->name.str, pi->ref_count)); if (lex) @@ -737,14 +919,11 @@ static bool plugin_add(MEM_ROOT *tmp_root, int *argc, char **argv, int report) { struct st_plugin_int tmp; - struct st_mysql_plugin *plugin; + struct st_maria_plugin *plugin; DBUG_ENTER("plugin_add"); if (plugin_find_internal(name, MYSQL_ANY_PLUGIN)) { - if (report & REPORT_TO_USER) - my_error(ER_UDF_EXISTS, MYF(0), name->str); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_UDF_EXISTS), name->str); + report_error(report, ER_UDF_EXISTS, name->str); DBUG_RETURN(TRUE); } /* Clear the whole struct to catch future extensions. */ @@ -771,10 +950,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, strxnmov(buf, sizeof(buf) - 1, "API version for ", plugin_type_names[plugin->type].str, " plugin is too different", NullS); - if (report & REPORT_TO_USER) - my_error(ER_CANT_OPEN_LIBRARY, MYF(0), dl->str, 0, buf); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_OPEN_LIBRARY), dl->str, 0, buf); + report_error(report, ER_CANT_OPEN_LIBRARY, dl->str, 0, buf); goto err; } tmp.plugin= plugin; @@ -803,10 +979,7 @@ static bool plugin_add(MEM_ROOT *tmp_root, DBUG_RETURN(FALSE); } } - if (report & REPORT_TO_USER) - my_error(ER_CANT_FIND_DL_ENTRY, MYF(0), name->str); - if (report & REPORT_TO_LOG) - sql_print_error(ER(ER_CANT_FIND_DL_ENTRY), name->str); + report_error(report, ER_CANT_FIND_DL_ENTRY, name->str); err: plugin_dl_del(dl); DBUG_RETURN(TRUE); @@ -962,8 +1135,6 @@ static void intern_plugin_unlock(LEX *lex, plugin_ref plugin) my_free((uchar*) plugin, MYF(MY_WME)); #endif - DBUG_PRINT("info",("unlocking plugin, name= %s, ref_count= %d", - pi->name.str, pi->ref_count)); if (lex) { /* @@ -983,6 +1154,9 @@ static void intern_plugin_unlock(LEX *lex, plugin_ref plugin) DBUG_ASSERT(pi->ref_count); pi->ref_count--; + DBUG_PRINT("lock",("thd: 0x%lx plugin: \"%s\" UNLOCK ref_count: %d", + (long) current_thd, pi->name.str, pi->ref_count)); + if (pi->state == PLUGIN_IS_DELETED && !pi->ref_count) reap_needed= true; @@ -1013,6 +1187,9 @@ void plugin_unlock_list(THD *thd, plugin_ref *list, uint count) { LEX *lex= thd ? thd->lex : 0; DBUG_ENTER("plugin_unlock_list"); + if (count == 0) + DBUG_VOID_RETURN; + DBUG_ASSERT(list); pthread_mutex_lock(&LOCK_plugin); while (count--) @@ -1025,9 +1202,14 @@ void plugin_unlock_list(THD *thd, plugin_ref *list, uint count) static int plugin_initialize(struct st_plugin_int *plugin) { + int ret= 1; DBUG_ENTER("plugin_initialize"); safe_mutex_assert_owner(&LOCK_plugin); + uint state= plugin->state; + DBUG_ASSERT(state == PLUGIN_IS_UNINITIALIZED); + + pthread_mutex_unlock(&LOCK_plugin); if (plugin_type_initialize[plugin->plugin->type]) { if ((*plugin_type_initialize[plugin->plugin->type])(plugin)) @@ -1046,8 +1228,7 @@ static int plugin_initialize(struct st_plugin_int *plugin) goto err; } } - - plugin->state= PLUGIN_IS_READY; + state= PLUGIN_IS_READY; // plugin->init() succeeded if (plugin->plugin->status_vars) { @@ -1066,7 +1247,8 @@ static int plugin_initialize(struct st_plugin_int *plugin) if (add_status_vars(array)) // add_status_vars makes a copy goto err; #else - add_status_vars(plugin->plugin->status_vars); // add_status_vars makes a copy + if (add_status_vars(plugin->plugin->status_vars)) + goto err; #endif /* FIX_LATER */ } @@ -1086,9 +1268,13 @@ static int plugin_initialize(struct st_plugin_int *plugin) } } - DBUG_RETURN(0); + ret= 0; + err: - DBUG_RETURN(1); + pthread_mutex_lock(&LOCK_plugin); + plugin->state= state; + + DBUG_RETURN(ret); } @@ -1139,8 +1325,8 @@ int plugin_init(int *argc, char **argv, int flags) { uint i; bool is_myisam; - struct st_mysql_plugin **builtins; - struct st_mysql_plugin *plugin; + struct st_maria_plugin **builtins; + struct st_maria_plugin *plugin; struct st_plugin_int tmp, *plugin_ptr, **reap; MEM_ROOT tmp_root; bool reaped_mandatory_plugin= FALSE; @@ -1179,7 +1365,7 @@ int plugin_init(int *argc, char **argv, int flags) /* First we register builtin plugins */ - for (builtins= mysqld_builtins; *builtins; builtins++) + for (builtins= mariadb_builtins; *builtins; builtins++) { for (plugin= *builtins; plugin->info; plugin++) { @@ -1294,7 +1480,7 @@ err: } -static bool register_builtin(struct st_mysql_plugin *plugin, +static bool register_builtin(struct st_maria_plugin *plugin, struct st_plugin_int *tmp, struct st_plugin_int **ptr) { @@ -1330,7 +1516,7 @@ static bool register_builtin(struct st_mysql_plugin *plugin, RETURN false - plugin registered successfully */ -bool plugin_register_builtin(THD *thd, struct st_mysql_plugin *plugin) +bool plugin_register_builtin(THD *thd, struct st_maria_plugin *plugin) { struct st_plugin_int tmp, *ptr; bool result= true; @@ -1459,7 +1645,7 @@ static bool plugin_load_list(MEM_ROOT *tmp_root, int *argc, char **argv, char buffer[FN_REFLEN]; LEX_STRING name= {buffer, 0}, dl= {NULL, 0}, *str= &name; struct st_plugin_dl *plugin_dl; - struct st_mysql_plugin *plugin; + struct st_maria_plugin *plugin; char *p= buffer; DBUG_ENTER("plugin_load_list"); while (list) @@ -1787,10 +1973,10 @@ bool mysql_uninstall_plugin(THD *thd, const LEX_STRING *name) table->use_all_columns(); table->field[0]->store(name->str, name->length, system_charset_info); - if (! table->file->index_read_idx_map(table->record[0], 0, - (uchar *)table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (! table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { int error; /* diff --git a/sql/sql_plugin.h b/sql/sql_plugin.h index 79ee296ac64..5822b096fa0 100644 --- a/sql/sql_plugin.h +++ b/sql/sql_plugin.h @@ -27,16 +27,6 @@ class sys_var; #define INITIAL_LEX_PLUGIN_LIST_SIZE 16 -/* - the following #define adds server-only members to enum_mysql_show_type, - that is defined in plugin.h -*/ -#define SHOW_FUNC SHOW_FUNC, SHOW_KEY_CACHE_LONG, SHOW_KEY_CACHE_LONGLONG, \ - SHOW_LONG_STATUS, SHOW_DOUBLE_STATUS, SHOW_HAVE, \ - SHOW_MY_BOOL, SHOW_HA_ROWS, SHOW_SYS, SHOW_LONG_NOFLUSH, \ - SHOW_LONGLONG_STATUS -#include <mysql/plugin.h> -#undef SHOW_FUNC typedef enum enum_mysql_show_type SHOW_TYPE; typedef struct st_mysql_show_var SHOW_VAR; @@ -62,8 +52,9 @@ struct st_plugin_dl { LEX_STRING dl; void *handle; - struct st_mysql_plugin *plugins; - int version; + struct st_maria_plugin *plugins; + int mysqlversion; + int mariaversion; bool allocated; uint ref_count; /* number of plugins loaded from the library */ }; @@ -73,7 +64,7 @@ struct st_plugin_dl struct st_plugin_int { LEX_STRING name; - struct st_mysql_plugin *plugin; + struct st_maria_plugin *plugin; struct st_plugin_dl *plugin_dl; uint state; uint ref_count; /* number of threads using the plugin */ diff --git a/sql/sql_plugin_services.h b/sql/sql_plugin_services.h new file mode 100644 index 00000000000..7491ddab79d --- /dev/null +++ b/sql/sql_plugin_services.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2009 Sun Microsystems, Inc. + + 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/* support for Services */ +#include <service_versions.h> + +struct st_service_ref { + const char *name; + uint version; + void *service; +}; + +static struct my_snprintf_service_st my_snprintf_handler = { + my_snprintf, + my_vsnprintf +}; + +static struct thd_alloc_service_st thd_alloc_handler= { + thd_alloc, + thd_calloc, + thd_strdup, + thd_strmake, + thd_memdup, + thd_make_lex_string +}; + +static struct st_service_ref list_of_services[]= +{ + { "my_snprintf_service", VERSION_my_snprintf, &my_snprintf_handler }, + { "thd_alloc_service", VERSION_thd_alloc, &thd_alloc_handler } +}; + diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index f7ee60531bc..e4940204386 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -2069,14 +2069,13 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) Prepared_statement *stmt; bool error; DBUG_ENTER("mysqld_stmt_prepare"); - DBUG_PRINT("prep_query", ("%s", packet)); /* First of all clear possible warnings from the previous command */ - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); if (! (stmt= new Prepared_statement(thd))) - DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */ + goto end; /* out of memory: error is set in Sql_alloc */ if (thd->stmt_map.insert(thd, stmt)) { @@ -2084,7 +2083,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) The error is set in the insert. The statement itself will be also deleted there (this is how the hash works). */ - DBUG_VOID_RETURN; + goto end; } /* Reset warnings from previous command */ @@ -2111,6 +2110,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) thd->protocol= save_protocol; /* check_prepared_statemnt sends the metadata packet in case of success */ +end: DBUG_VOID_RETURN; } @@ -2455,7 +2455,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) packet+= 9; /* stmt_id + 5 bytes of flags */ /* First of all clear possible warnings from the previous command */ - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); if (!(stmt= find_prepared_statement(thd, stmt_id))) { @@ -2554,7 +2554,8 @@ void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length) DBUG_ENTER("mysqld_stmt_fetch"); /* First of all clear possible warnings from the previous command */ - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); + status_var_increment(thd->status_var.com_stmt_fetch); if (!(stmt= find_prepared_statement(thd, stmt_id))) { @@ -2620,7 +2621,7 @@ void mysqld_stmt_reset(THD *thd, char *packet) DBUG_ENTER("mysqld_stmt_reset"); /* First of all clear possible warnings from the previous command */ - mysql_reset_thd_for_next_command(thd); + mysql_reset_thd_for_next_command(thd, opt_userstat_running); status_var_increment(thd->status_var.com_stmt_reset); if (!(stmt= find_prepared_statement(thd, stmt_id))) diff --git a/sql/sql_select.cc b/sql/sql_select.cc index e3b34126828..279c4bcac60 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -9842,6 +9842,10 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps) bitmap_init(&table->tmp_set, (my_bitmap_map*) (bitmaps+ bitmap_buffer_size(field_count)), field_count, FALSE); + bitmap_init(&table->vcol_set, + (my_bitmap_map*) (bitmaps+ 2+bitmap_buffer_size(field_count)), + field_count, FALSE); + /* write_set and all_set are copies of read_set */ table->def_write_set= table->def_read_set; table->s->all_set= table->def_read_set; @@ -9990,7 +9994,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields, &tmpname, (uint) strlen(tmp_table_name)+1, &group_buff, (group && ! using_unique_constraint ? param->group_length : 0), - &bitmaps, bitmap_buffer_size(field_count)*2, + &bitmaps, bitmap_buffer_size(field_count)*3, NullS)) { if (temp_pool_slot != MY_BIT_NONE) @@ -10596,7 +10600,7 @@ TABLE *create_virtual_tmp_table(THD *thd, List<Create_field> &field_list) &share, sizeof(*share), &field, (field_count + 1) * sizeof(Field*), &blob_field, (field_count+1) *sizeof(uint), - &bitmaps, bitmap_buffer_size(field_count)*2, + &bitmaps, bitmap_buffer_size(field_count)*3, NullS)) return 0; @@ -10687,8 +10691,9 @@ error: static bool open_tmp_table(TABLE *table) { int error; - if ((error=table->file->ha_open(table, table->s->table_name.str,O_RDWR, - HA_OPEN_TMP_TABLE | HA_OPEN_INTERNAL_TABLE))) + if ((error= table->file->ha_open(table, table->s->table_name.str, O_RDWR, + HA_OPEN_TMP_TABLE | + HA_OPEN_INTERNAL_TABLE))) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ table->db_stat=0; @@ -11033,7 +11038,7 @@ create_internal_tmp_table_from_heap2(THD *thd, TABLE *table, is safe as this is a temporary MyISAM table without timestamp/autoincrement or partitioning. */ - while (!table->file->rnd_next(new_table.record[1])) + while (!table->file->ha_rnd_next(new_table.record[1])) { write_err= new_table.file->ha_write_row(new_table.record[1]); DBUG_EXECUTE_IF("raise_error", write_err= HA_ERR_FOUND_DUPP_KEY ;); @@ -11567,6 +11572,8 @@ evaluate_join_record(JOIN *join, JOIN_TAB *join_tab, } DBUG_PRINT("info", ("select cond 0x%lx", (ulong)select_cond)); + update_virtual_fields(join_tab->table); + if (select_cond) { select_cond_result= test(select_cond->val_int()); @@ -11785,6 +11792,8 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skip_last) return NESTED_LOOP_KILLED; // Aborted by user /* purecov: inspected */ } SQL_SELECT *select=join_tab->select; + if (rc == NESTED_LOOP_OK) + update_virtual_fields(join_tab->table); if (rc == NESTED_LOOP_OK && (!join_tab->cache.select || !join_tab->cache.select->skip_record())) { @@ -11847,10 +11856,10 @@ int safe_index_read(JOIN_TAB *tab) { int error; TABLE *table= tab->table; - if ((error=table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT))) return report_error(table, error); return 0; } @@ -11959,8 +11968,8 @@ join_read_system(JOIN_TAB *tab) int error; if (table->status & STATUS_GARBAGE) // If first read { - if ((error=table->file->read_first_row(table->record[0], - table->s->primary_key))) + if ((error= table->file->ha_read_first_row(table->record[0], + table->s->primary_key))) { if (error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -11968,6 +11977,7 @@ join_read_system(JOIN_TAB *tab) empty_record(table); // Make empty record return -1; } + update_virtual_fields(table); store_record(table,record[1]); } else if (!table->status) // Only happens with left join @@ -12002,10 +12012,10 @@ join_read_const(JOIN_TAB *tab) error=HA_ERR_KEY_NOT_FOUND; else { - error=table->file->index_read_idx_map(table->record[0],tab->ref.key, - (uchar*) tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_idx_map(table->record[0],tab->ref.key, + (uchar*) tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT); } if (error) { @@ -12016,6 +12026,7 @@ join_read_const(JOIN_TAB *tab) return report_error(table, error); return -1; } + update_virtual_fields(table); store_record(table,record[1]); } else if (!(table->status & ~STATUS_NULL_ROW)) // Only happens with left join @@ -12059,10 +12070,10 @@ join_read_key(JOIN_TAB *tab) tab->read_record.file->unlock_row(); tab->ref.has_record= FALSE; } - error=table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT); + error= table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT); if (error && error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -12143,10 +12154,10 @@ join_read_always_key(JOIN_TAB *tab) if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) return -1; - if ((error=table->file->index_read_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts), - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts), + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -12177,9 +12188,9 @@ join_read_last_key(JOIN_TAB *tab) } if (cp_buffer_from_ref(tab->join->thd, table, &tab->ref)) return -1; - if ((error=table->file->index_read_last_map(table->record[0], - tab->ref.key_buff, - make_prev_keypart_map(tab->ref.key_parts)))) + if ((error= table->file->ha_index_read_last_map(table->record[0], + tab->ref.key_buff, + make_prev_keypart_map(tab->ref.key_parts)))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -12204,9 +12215,9 @@ join_read_next_same(READ_RECORD *info) TABLE *table= info->table; JOIN_TAB *tab=table->reginfo.join_tab; - if ((error=table->file->index_next_same(table->record[0], - tab->ref.key_buff, - tab->ref.key_length))) + if ((error= table->file->ha_index_next_same(table->record[0], + tab->ref.key_buff, + tab->ref.key_length))) { if (error != HA_ERR_END_OF_FILE) return report_error(table, error); @@ -12224,7 +12235,7 @@ join_read_prev_same(READ_RECORD *info) TABLE *table= info->table; JOIN_TAB *tab=table->reginfo.join_tab; - if ((error=table->file->index_prev(table->record[0]))) + if ((error= table->file->ha_index_prev(table->record[0]))) return report_error(table, error); if (key_cmp_if_same(table, tab->ref.key_buff, tab->ref.key, tab->ref.key_length)) @@ -12296,7 +12307,7 @@ join_read_first(JOIN_TAB *tab) error= table->file->ha_index_init(tab->index, tab->sorted); if (!error) error= table->file->prepare_index_scan(); - if (error || (error=tab->table->file->index_first(tab->table->record[0]))) + if (error || (error=tab->table->file->ha_index_first(tab->table->record[0]))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) report_error(table, error); @@ -12310,8 +12321,9 @@ static int join_read_next(READ_RECORD *info) { int error; - if ((error=info->file->index_next(info->record))) + if ((error= info->file->ha_index_next(info->record))) return report_error(info->table, error); + return 0; } @@ -12337,8 +12349,9 @@ join_read_last(JOIN_TAB *tab) error= table->file->ha_index_init(tab->index, 1); if (!error) error= table->file->prepare_index_scan(); - if (error || (error= tab->table->file->index_last(tab->table->record[0]))) + if (error || (error= tab->table->file->ha_index_last(tab->table->record[0]))) return report_error(table, error); + return 0; } @@ -12347,7 +12360,7 @@ static int join_read_prev(READ_RECORD *info) { int error; - if ((error= info->file->index_prev(info->record))) + if ((error= info->file->ha_index_prev(info->record))) return report_error(info->table, error); return 0; } @@ -12372,7 +12385,7 @@ join_ft_read_first(JOIN_TAB *tab) #endif table->file->ft_init(); - if ((error= table->file->ft_read(table->record[0]))) + if ((error= table->file->ha_ft_read(table->record[0]))) return report_error(table, error); return 0; } @@ -12381,7 +12394,7 @@ static int join_ft_read_next(READ_RECORD *info) { int error; - if ((error= info->file->ft_read(info->table->record[0]))) + if ((error= info->file->ha_ft_read(info->table->record[0]))) return report_error(info->table, error); return 0; } @@ -12673,7 +12686,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), { int error; join->found_records++; - if ((error=table->file->ha_write_row(table->record[0]))) + if ((error= table->file->ha_write_row(table->record[0]))) { if (!table->file->is_fatal_error(error, HA_CHECK_DUP)) goto end; @@ -12728,15 +12741,15 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), if (item->maybe_null) group->buff[-1]= (char) group->field->is_null(); } - if (!table->file->index_read_map(table->record[1], - join->tmp_table_param.group_buff, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (!table->file->ha_index_read_map(table->record[1], + join->tmp_table_param.group_buff, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { /* Update old record */ restore_record(table,record[1]); update_tmptable_sum_func(join->sum_funcs,table); - if ((error=table->file->ha_update_row(table->record[1], - table->record[0]))) + if ((error= table->file->ha_update_row(table->record[1], + table->record[0]))) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ @@ -12759,7 +12772,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), } init_tmptable_sum_functions(join->sum_funcs); copy_funcs(join->tmp_table_param.items_to_copy); - if ((error=table->file->ha_write_row(table->record[0]))) + if ((error= table->file->ha_write_row(table->record[0]))) { if (create_internal_tmp_table_from_heap(join->thd, table, &join->tmp_table_param, @@ -12800,7 +12813,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), copy_fields(&join->tmp_table_param); // Groups are copied twice. copy_funcs(join->tmp_table_param.items_to_copy); - if (!(error=table->file->ha_write_row(table->record[0]))) + if (!(error= table->file->ha_write_row(table->record[0]))) join->send_records++; // New group else { @@ -12809,15 +12822,15 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ } - if (table->file->rnd_pos(table->record[1],table->file->dup_ref)) + if (table->file->ha_rnd_pos(table->record[1],table->file->dup_ref)) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ } restore_record(table,record[1]); update_tmptable_sum_func(join->sum_funcs,table); - if ((error=table->file->ha_update_row(table->record[1], - table->record[0]))) + if ((error= table->file->ha_update_row(table->record[1], + table->record[0]))) { table->file->print_error(error,MYF(0)); /* purecov: inspected */ DBUG_RETURN(NESTED_LOOP_ERROR); /* purecov: inspected */ @@ -14156,7 +14169,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field, new_record=(char*) table->record[1]+offset; file->ha_rnd_init(1); - error=file->rnd_next(record); + error= file->ha_rnd_next(record); for (;;) { if (thd->killed) @@ -14169,7 +14182,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field, { if (error == HA_ERR_RECORD_DELETED) { - error= file->rnd_next(record); + error= file->ha_rnd_next(record); continue; } if (error == HA_ERR_END_OF_FILE) @@ -14178,9 +14191,9 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field, } if (having && !having->val_int()) { - if ((error=file->ha_delete_row(record))) + if ((error= file->ha_delete_row(record))) goto err; - error=file->rnd_next(record); + error= file->ha_rnd_next(record); continue; } if (copy_blobs(first_field)) @@ -14195,7 +14208,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field, bool found=0; for (;;) { - if ((error=file->rnd_next(record))) + if ((error= file->ha_rnd_next(record))) { if (error == HA_ERR_RECORD_DELETED) continue; @@ -14205,7 +14218,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field, } if (compare_record(table, first_field) == 0) { - if ((error=file->ha_delete_row(record))) + if ((error= file->ha_delete_row(record))) goto err; } else if (!found) @@ -14295,7 +14308,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, error=0; goto err; } - if ((error=file->rnd_next(record))) + if ((error= file->ha_rnd_next(record))) { if (error == HA_ERR_RECORD_DELETED) continue; @@ -14305,7 +14318,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, } if (having && !having->val_int()) { - if ((error=file->ha_delete_row(record))) + if ((error= file->ha_delete_row(record))) goto err; continue; } @@ -14322,7 +14335,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table, if (hash_search(&hash, org_key_pos, key_length)) { /* Duplicated found ; Remove the row */ - if ((error=file->ha_delete_row(record))) + if ((error= file->ha_delete_row(record))) goto err; } else diff --git a/sql/sql_servers.cc b/sql/sql_servers.cc index 1655426bb4a..b88320ed2bf 100644 --- a/sql/sql_servers.cc +++ b/sql/sql_servers.cc @@ -526,10 +526,10 @@ int insert_server_record(TABLE *table, FOREIGN_SERVER *server) system_charset_info); /* read index until record is that specified in server_name */ - if ((error= table->file->index_read_idx_map(table->record[0], 0, - (uchar *)table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { /* if not found, err */ if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) @@ -869,10 +869,10 @@ update_server_record(TABLE *table, FOREIGN_SERVER *server) server->server_name_length, system_charset_info); - if ((error= table->file->index_read_idx_map(table->record[0], 0, - (uchar *)table->field[0]->ptr, - ~(longlong)0, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + ~(longlong)0, + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) table->file->print_error(error, MYF(0)); @@ -926,10 +926,10 @@ delete_server_record(TABLE *table, /* set the field that's the PK to the value we're looking for */ table->field[0]->store(server_name, server_name_length, system_charset_info); - if ((error= table->file->index_read_idx_map(table->record[0], 0, - (uchar *)table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT))) + if ((error= table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar *)table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT))) { if (error != HA_ERR_KEY_NOT_FOUND && error != HA_ERR_END_OF_FILE) table->file->print_error(error, MYF(0)); diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 68d1d4619f4..e35cbe17dc5 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -18,6 +18,7 @@ #include "mysql_priv.h" #include "sql_select.h" // For select_describe +#include "create_options.h" #include "sql_show.h" #include "repl_failsafe.h" #include "sp.h" @@ -94,11 +95,21 @@ static int make_version_string(char *buf, int buf_length, uint version) return my_snprintf(buf, buf_length, "%d.%d", version>>8,version&0xff); } + +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 my_bool show_plugins(THD *thd, plugin_ref plugin, void *arg) { TABLE *table= (TABLE*) arg; - struct st_mysql_plugin *plug= plugin_decl(plugin); + struct st_maria_plugin *plug= plugin_decl(plugin); struct st_plugin_dl *plugin_dl= plugin_dlib(plugin); CHARSET_INFO *cs= system_charset_info; char version_buf[20]; @@ -143,7 +154,7 @@ static my_bool show_plugins(THD *thd, plugin_ref plugin, table->field[5]->set_notnull(); table->field[6]->store(version_buf, make_version_string(version_buf, sizeof(version_buf), - plugin_dl->version), + plugin_dl->mariaversion), cs); table->field[6]->set_notnull(); } @@ -186,6 +197,26 @@ static my_bool show_plugins(THD *thd, plugin_ref plugin, } table->field[9]->set_notnull(); + if ((uint) plug->maturity <= MariaDB_PLUGIN_MATURITY_STABLE) + table->field[10]->store(maturity_name[plug->maturity].str, + maturity_name[plug->maturity].length, + cs); + else + { + DBUG_ASSERT(0); + table->field[10]->store("Unknown", 7, cs); + } + table->field[10]->set_notnull(); + + if (plug->version_info) + { + table->field[11]->store(plug->version_info, + strlen(plug->version_info), cs); + table->field[11]->set_notnull(); + } + else + table->field[11]->set_null(); + return schema_table_store_record(thd, table); } @@ -821,6 +852,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname, sctx->master_access); if (!(db_access & DB_ACLS) && check_grant_db(thd,dbname)) { + status_var_increment(thd->status_var.access_denied_errors); my_error(ER_DBACCESS_DENIED_ERROR, MYF(0), sctx->priv_user, sctx->host_or_ip, dbname); general_log_print(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR), @@ -1012,7 +1044,7 @@ append_identifier(THD *thd, String *packet, const char *name, uint length) /* The identifier must be quoted as it includes a quote character or - it's a keyword + it's a keyword */ VOID(packet->reserve(length*2 + 2)); @@ -1172,6 +1204,31 @@ static bool get_field_default_value(THD *thd, TABLE *table, return has_default; } + +/** + Appends list of options to string + + @param thd thread handler + @param packet string to append + @param opt list of options +*/ + +static void append_create_options(THD *thd, String *packet, + engine_option_value *opt) +{ + for(; opt; opt= opt->next) + { + DBUG_ASSERT(opt->value.str); + packet->append(' '); + append_identifier(thd, packet, opt->name.str, opt->name.length); + packet->append('='); + if (opt->quoted_value) + append_unescaped(packet, opt->value.str, opt->value.length); + else + packet->append(opt->value.str, opt->value.length); + } +} + /* Build a CREATE TABLE statement for a table. @@ -1291,6 +1348,19 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, field->sql_type(type); packet->append(type.ptr(), type.length(), system_charset_info); + if (field->vcol_info) + { + packet->append(STRING_WITH_LEN(" AS (")); + packet->append(field->vcol_info->expr_str.str, + field->vcol_info->expr_str.length, + system_charset_info); + packet->append(STRING_WITH_LEN(")")); + if (field->stored_in_db) + packet->append(STRING_WITH_LEN(" PERSISTENT")); + else + packet->append(STRING_WITH_LEN(" VIRTUAL")); + } + if (field->has_charset() && !(thd->variables.sql_mode & (MODE_MYSQL323 | MODE_MYSQL40))) { @@ -1321,7 +1391,8 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" NULL")); } - if (get_field_default_value(thd, table, field, &def_value, 1)) + if (!field->vcol_info && + get_field_default_value(thd, table, field, &def_value, 1)) { packet->append(STRING_WITH_LEN(" DEFAULT ")); packet->append(def_value.ptr(), def_value.length(), system_charset_info); @@ -1340,6 +1411,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" COMMENT ")); append_unescaped(packet, field->comment.str, field->comment.length); } + append_create_options(thd, packet, field->option_list); } key_info= table->key_info; @@ -1411,6 +1483,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, append_identifier(thd, packet, parser_name->str, parser_name->length); packet->append(STRING_WITH_LEN(" */ ")); } + append_create_options(thd, packet, key_info->option_list); } /* @@ -1570,6 +1643,7 @@ int store_create_info(THD *thd, TABLE_LIST *table_list, String *packet, 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); append_directory(thd, packet, "DATA", create_info.data_file_name); append_directory(thd, packet, "INDEX", create_info.index_file_name); } @@ -2203,12 +2277,36 @@ void remove_status_vars(SHOW_VAR *list) } } -inline void make_upper(char *buf) + + +static void update_key_cache_stat_var(KEY_CACHE *key_cache, size_t ofs) { - for (; *buf; buf++) - *buf= my_toupper(system_charset_info, *buf); + uint var_no; + if (ofs == offsetof(KEY_CACHE, blocks_used) || + ofs == offsetof(KEY_CACHE, blocks_unused) || + ofs == offsetof(KEY_CACHE, global_blocks_changed)) + { + var_no= (ofs-offsetof(KEY_CACHE, blocks_used))/sizeof(ulong); + *(ulong *)((char *) key_cache + ofs)= + (ulong) get_key_cache_stat_value(key_cache, var_no); + return; + } + + if (ofs == offsetof(KEY_CACHE, global_cache_r_requests) || + ofs == offsetof(KEY_CACHE, global_cache_read) || + ofs == offsetof(KEY_CACHE, global_cache_w_requests) || + ofs == offsetof(KEY_CACHE, global_cache_write)) + { + var_no= NUM_LONG_KEY_CACHE_STAT_VARIABLES + + (ofs-offsetof(KEY_CACHE, global_cache_w_requests))/ + sizeof(ulonglong); + *(ulonglong *)((char *) key_cache + ofs)= + get_key_cache_stat_value(key_cache, var_no); + return; + } } + static bool show_status_array(THD *thd, const char *wild, SHOW_VAR *variables, enum enum_var_type value_type, @@ -2246,7 +2344,7 @@ static bool show_status_array(THD *thd, const char *wild, strnmov(prefix_end, variables->name, len); name_buffer[sizeof(name_buffer)-1]=0; /* Safety */ if (ucase_names) - make_upper(name_buffer); + my_caseup_str(system_charset_info, name_buffer); restore_record(table, s->default_values); table->field[0]->store(name_buffer, strlen(name_buffer), @@ -2341,10 +2439,12 @@ static bool show_status_array(THD *thd, const char *wild, break; } case SHOW_KEY_CACHE_LONG: + update_key_cache_stat_var(dflt_key_cache, (size_t) value); value= (char*) dflt_key_cache + (ulong)value; end= int10_to_str(*(long*) value, buff, 10); break; case SHOW_KEY_CACHE_LONGLONG: + update_key_cache_stat_var(dflt_key_cache, (size_t) value); value= (char*) dflt_key_cache + (ulong)value; end= longlong10_to_str(*(longlong*) value, buff, 10); break; @@ -2373,6 +2473,323 @@ end: DBUG_RETURN(res); } +#ifdef COMPLEAT_PATCH_NOT_ADDED_YET +/* + Aggregate values for mapped_user entries by their role. + + SYNOPSIS + aggregate_user_stats + all_user_stats - input to aggregate + agg_user_stats - returns aggregated values + + RETURN + 0 - OK + 1 - error +*/ + +static int aggregate_user_stats(HASH *all_user_stats, HASH *agg_user_stats) +{ + DBUG_ENTER("aggregate_user_stats"); + if (hash_init(agg_user_stats, system_charset_info, + max(all_user_stats->records, 1), + 0, 0, (hash_get_key)get_key_user_stats, + (hash_free_key)free_user_stats, 0)) + { + sql_print_error("Malloc in aggregate_user_stats failed"); + DBUG_RETURN(1); + } + + for (uint i= 0; i < all_user_stats->records; i++) + { + USER_STATS *user= (USER_STATS*)hash_element(all_user_stats, i); + USER_STATS *agg_user; + uint name_length= strlen(user->priv_user); + + if (!(agg_user= (USER_STATS*) hash_search(agg_user_stats, + (uchar*)user->priv_user, + name_length))) + { + // First entry for this role. + if (!(agg_user= (USER_STATS*) my_malloc(sizeof(USER_STATS), + MYF(MY_WME | MY_ZEROFILL)))) + { + sql_print_error("Malloc in aggregate_user_stats failed"); + DBUG_RETURN(1); + } + + init_user_stats(agg_user, user->priv_user, name_length, + user->priv_user, + user->total_connections, user->concurrent_connections, + user->connected_time, user->busy_time, user->cpu_time, + user->bytes_received, user->bytes_sent, + user->binlog_bytes_written, + user->rows_sent, user->rows_read, + user->rows_inserted, user->rows_deleted, + user->rows_updated, + user->select_commands, user->update_commands, + user->other_commands, + user->commit_trans, user->rollback_trans, + user->denied_connections, user->lost_connections, + user->access_denied_errors, user->empty_queries); + + if (my_hash_insert(agg_user_stats, (uchar*) agg_user)) + { + /* Out of memory */ + my_free(agg_user, 0); + sql_print_error("Malloc in aggregate_user_stats failed"); + DBUG_RETURN(1); + } + } + else + { + /* Aggregate with existing values for this role. */ + add_user_stats(agg_user, + user->total_connections, user->concurrent_connections, + user->connected_time, user->busy_time, user->cpu_time, + user->bytes_received, user->bytes_sent, + user->binlog_bytes_written, + user->rows_sent, user->rows_read, + user->rows_inserted, user->rows_deleted, + user->rows_updated, + user->select_commands, user->update_commands, + user->other_commands, + user->commit_trans, user->rollback_trans, + user->denied_connections, user->lost_connections, + user->access_denied_errors, user->empty_queries); + } + } + DBUG_PRINT("exit", ("aggregated %lu input into %lu output entries", + all_user_stats->records, agg_user_stats->records)); + DBUG_RETURN(0); +} +#endif + +/* + Write result to network for SHOW USER_STATISTICS + + SYNOPSIS + send_user_stats + all_user_stats - values to return + table - I_S table + + RETURN + 0 - OK + 1 - error +*/ + +int send_user_stats(THD* thd, HASH *all_user_stats, TABLE *table) +{ + DBUG_ENTER("send_user_stats"); + + for (uint i= 0; i < all_user_stats->records; i++) + { + uint j= 0; + USER_STATS *user_stats= (USER_STATS*) hash_element(all_user_stats, i); + + table->field[j++]->store(user_stats->user, user_stats->user_name_length, + system_charset_info); + table->field[j++]->store((longlong)user_stats->total_connections,TRUE); + table->field[j++]->store((longlong)user_stats->concurrent_connections, TRUE); + table->field[j++]->store((longlong)user_stats->connected_time, TRUE); + table->field[j++]->store((double)user_stats->busy_time); + table->field[j++]->store((double)user_stats->cpu_time); + table->field[j++]->store((longlong)user_stats->bytes_received, TRUE); + table->field[j++]->store((longlong)user_stats->bytes_sent, TRUE); + table->field[j++]->store((longlong)user_stats->binlog_bytes_written, TRUE); + table->field[j++]->store((longlong)user_stats->rows_read, TRUE); + table->field[j++]->store((longlong)user_stats->rows_sent, TRUE); + table->field[j++]->store((longlong)user_stats->rows_deleted, TRUE); + table->field[j++]->store((longlong)user_stats->rows_inserted, TRUE); + table->field[j++]->store((longlong)user_stats->rows_updated, TRUE); + table->field[j++]->store((longlong)user_stats->select_commands, TRUE); + table->field[j++]->store((longlong)user_stats->update_commands, TRUE); + table->field[j++]->store((longlong)user_stats->other_commands, TRUE); + table->field[j++]->store((longlong)user_stats->commit_trans, TRUE); + table->field[j++]->store((longlong)user_stats->rollback_trans, TRUE); + table->field[j++]->store((longlong)user_stats->denied_connections, TRUE); + table->field[j++]->store((longlong)user_stats->lost_connections, TRUE); + table->field[j++]->store((longlong)user_stats->access_denied_errors, TRUE); + table->field[j++]->store((longlong)user_stats->empty_queries, TRUE); + if (schema_table_store_record(thd, table)) + { + DBUG_PRINT("error", ("store record error")); + DBUG_RETURN(1); + } + } + DBUG_RETURN(0); +} + +/* + Process SHOW USER_STATISTICS + + SYNOPSIS + mysqld_show_user_stats + thd - current thread + wild - limit results to the entry for this user + with_roles - when true, display role for mapped users + + RETURN + 0 - OK + 1 - error +*/ + +int fill_schema_user_stats(THD* thd, TABLE_LIST* tables, COND* cond) +{ + TABLE *table= tables->table; + int result; + DBUG_ENTER("fill_schema_user_stats"); + + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL)) + DBUG_RETURN(1); + + /* + Iterates through all the global stats and sends them to the client. + Pattern matching on the client IP is supported. + */ + + pthread_mutex_lock(&LOCK_global_user_client_stats); + result= send_user_stats(thd, &global_user_stats, table) != 0; + pthread_mutex_unlock(&LOCK_global_user_client_stats); + + DBUG_PRINT("exit", ("result: %d", result)); + DBUG_RETURN(result); +} + +/* + Process SHOW CLIENT_STATISTICS + + SYNOPSIS + mysqld_show_client_stats + thd - current thread + wild - limit results to the entry for this client + + RETURN + 0 - OK + 1 - error +*/ + +int fill_schema_client_stats(THD* thd, TABLE_LIST* tables, COND* cond) +{ + TABLE *table= tables->table; + int result; + DBUG_ENTER("fill_schema_client_stats"); + + if (check_global_access(thd, SUPER_ACL | PROCESS_ACL)) + DBUG_RETURN(1); + + /* + Iterates through all the global stats and sends them to the client. + Pattern matching on the client IP is supported. + */ + + pthread_mutex_lock(&LOCK_global_user_client_stats); + result= send_user_stats(thd, &global_client_stats, table) != 0; + pthread_mutex_unlock(&LOCK_global_user_client_stats); + + DBUG_PRINT("exit", ("result: %d", result)); + DBUG_RETURN(result); +} + + +/* Fill information schema table with table statistics */ + +int fill_schema_table_stats(THD *thd, TABLE_LIST *tables, COND *cond) +{ + TABLE *table= tables->table; + DBUG_ENTER("fill_schema_table_stats"); + + pthread_mutex_lock(&LOCK_global_table_stats); + for (uint i= 0; i < global_table_stats.records; i++) + { + char *end_of_schema; + TABLE_STATS *table_stats= + (TABLE_STATS*)hash_element(&global_table_stats, i); + TABLE_LIST tmp_table; + size_t schema_length, table_name_length; + + end_of_schema= strend(table_stats->table); + schema_length= (size_t) (end_of_schema - table_stats->table); + table_name_length= strlen(table_stats->table + schema_length + 1); + + bzero((char*) &tmp_table,sizeof(tmp_table)); + tmp_table.db= table_stats->table; + tmp_table.table_name= end_of_schema+1; + tmp_table.grant.privilege= 0; + if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db, + &tmp_table.grant.privilege, 0, 0, + is_schema_db(tmp_table.db)) || + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, + 1)) + continue; + + table->field[0]->store(table_stats->table, schema_length, + system_charset_info); + table->field[1]->store(table_stats->table + schema_length+1, + table_name_length, system_charset_info); + table->field[2]->store((longlong)table_stats->rows_read, TRUE); + table->field[3]->store((longlong)table_stats->rows_changed, TRUE); + table->field[4]->store((longlong)table_stats->rows_changed_x_indexes, + TRUE); + if (schema_table_store_record(thd, table)) + { + VOID(pthread_mutex_unlock(&LOCK_global_table_stats)); + DBUG_RETURN(1); + } + } + pthread_mutex_unlock(&LOCK_global_table_stats); + DBUG_RETURN(0); +} + + +/* Fill information schema table with index statistics */ + +int fill_schema_index_stats(THD *thd, TABLE_LIST *tables, COND *cond) +{ + TABLE *table= tables->table; + DBUG_ENTER("fill_schema_index_stats"); + + pthread_mutex_lock(&LOCK_global_index_stats); + for (uint i= 0; i < global_index_stats.records; i++) + { + INDEX_STATS *index_stats = + (INDEX_STATS*) hash_element(&global_index_stats, i); + TABLE_LIST tmp_table; + char *index_name; + size_t schema_name_length, table_name_length, index_name_length; + + bzero((char*) &tmp_table,sizeof(tmp_table)); + tmp_table.db= index_stats->index; + tmp_table.table_name= strend(index_stats->index)+1; + tmp_table.grant.privilege= 0; + if (check_access(thd, SELECT_ACL | EXTRA_ACL, tmp_table.db, + &tmp_table.grant.privilege, 0, 0, + is_schema_db(tmp_table.db)) || + check_grant(thd, SELECT_ACL, &tmp_table, 1, UINT_MAX, 1)) + continue; + + index_name= strend(tmp_table.table_name)+1; + schema_name_length= (tmp_table.table_name - index_stats->index) -1; + table_name_length= (index_name - tmp_table.table_name)-1; + index_name_length= (index_stats->index_name_length - schema_name_length - + table_name_length - 3); + + table->field[0]->store(tmp_table.db, schema_name_length, + system_charset_info); + table->field[1]->store(tmp_table.table_name, table_name_length, + system_charset_info); + table->field[2]->store(index_name, index_name_length, system_charset_info); + table->field[3]->store((longlong)index_stats->rows_read, TRUE); + + if (schema_table_store_record(thd, table)) + { + VOID(pthread_mutex_unlock(&LOCK_global_index_stats)); + DBUG_RETURN(1); + } + } + pthread_mutex_unlock(&LOCK_global_index_stats); + DBUG_RETURN(0); +} + /* collect status for all running threads */ @@ -4013,6 +4430,8 @@ static int get_schema_column_record(THD *thd, TABLE_LIST *tables, field->unireg_check != Field::TIMESTAMP_DN_FIELD) table->field[16]->store(STRING_WITH_LEN("on update CURRENT_TIMESTAMP"), cs); + if (field->vcol_info) + table->field[16]->store(STRING_WITH_LEN("VIRTUAL"), cs); table->field[18]->store(field->comment.str, field->comment.length, cs); if (schema_table_store_record(thd, table)) @@ -4069,7 +4488,7 @@ static my_bool iter_schema_engines(THD *thd, plugin_ref plugin, if (plugin_state(plugin) != PLUGIN_IS_READY) { - struct st_mysql_plugin *plug= plugin_decl(plugin); + struct st_maria_plugin *plug= plugin_decl(plugin); if (!(wild && wild[0] && wild_case_compare(scs, plug->name,wild))) { @@ -4311,7 +4730,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) DBUG_RETURN(1); } proc_table->file->ha_index_init(0, 1); - if ((res= proc_table->file->index_first(proc_table->record[0]))) + if ((res= proc_table->file->ha_index_first(proc_table->record[0]))) { res= (res == HA_ERR_END_OF_FILE) ? 0 : 1; goto err; @@ -4321,7 +4740,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond) res= 1; goto err; } - while (!proc_table->file->index_next(proc_table->record[0])) + while (!proc_table->file->ha_index_next(proc_table->record[0])) { if (store_schema_proc(thd, table, proc_table, wild, full_access, definer)) { @@ -5567,6 +5986,81 @@ struct schema_table_ref ST_SCHEMA_TABLE *schema_table; }; +ST_FIELD_INFO user_stats_fields_info[]= +{ + {"USER", USERNAME_LENGTH, MYSQL_TYPE_STRING, 0, 0, "User", SKIP_OPEN_TABLE}, + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE}, + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE}, + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE}, + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Busy_time",SKIP_OPEN_TABLE}, + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Cpu_time",SKIP_OPEN_TABLE}, + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received",SKIP_OPEN_TABLE}, + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent",SKIP_OPEN_TABLE}, + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written",SKIP_OPEN_TABLE}, + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE}, + {"ROWS_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_sent",SKIP_OPEN_TABLE}, + {"ROWS_DELETED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_deleted",SKIP_OPEN_TABLE}, + {"ROWS_INSERTED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_inserted",SKIP_OPEN_TABLE}, + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated",SKIP_OPEN_TABLE}, + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands",SKIP_OPEN_TABLE}, + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands",SKIP_OPEN_TABLE}, + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands",SKIP_OPEN_TABLE}, + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions",SKIP_OPEN_TABLE}, + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions",SKIP_OPEN_TABLE}, + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections",SKIP_OPEN_TABLE}, + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections",SKIP_OPEN_TABLE}, + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied",SKIP_OPEN_TABLE}, + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries",SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} +}; + +ST_FIELD_INFO client_stats_fields_info[]= +{ + {"CLIENT", LIST_PROCESS_HOST_LEN, MYSQL_TYPE_STRING, 0, 0, "Client",SKIP_OPEN_TABLE}, + {"TOTAL_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Total_connections",SKIP_OPEN_TABLE}, + {"CONCURRENT_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Concurrent_connections",SKIP_OPEN_TABLE}, + {"CONNECTED_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Connected_time",SKIP_OPEN_TABLE}, + {"BUSY_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Busy_time",SKIP_OPEN_TABLE}, + {"CPU_TIME", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_DOUBLE, 0, 0, "Cpu_time",SKIP_OPEN_TABLE}, + {"BYTES_RECEIVED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_received",SKIP_OPEN_TABLE}, + {"BYTES_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Bytes_sent",SKIP_OPEN_TABLE}, + {"BINLOG_BYTES_WRITTEN", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Binlog_bytes_written",SKIP_OPEN_TABLE}, + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE}, + {"ROWS_SENT", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_sent",SKIP_OPEN_TABLE}, + {"ROWS_DELETED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_deleted",SKIP_OPEN_TABLE}, + {"ROWS_INSERTED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_inserted",SKIP_OPEN_TABLE}, + {"ROWS_UPDATED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_updated",SKIP_OPEN_TABLE}, + {"SELECT_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Select_commands",SKIP_OPEN_TABLE}, + {"UPDATE_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Update_commands",SKIP_OPEN_TABLE}, + {"OTHER_COMMANDS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Other_commands",SKIP_OPEN_TABLE}, + {"COMMIT_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Commit_transactions",SKIP_OPEN_TABLE}, + {"ROLLBACK_TRANSACTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rollback_transactions",SKIP_OPEN_TABLE}, + {"DENIED_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Denied_connections",SKIP_OPEN_TABLE}, + {"LOST_CONNECTIONS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Lost_connections",SKIP_OPEN_TABLE}, + {"ACCESS_DENIED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Access_denied",SKIP_OPEN_TABLE}, + {"EMPTY_QUERIES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Empty_queries",SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} +}; + + +ST_FIELD_INFO table_stats_fields_info[]= +{ + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema",SKIP_OPEN_TABLE}, + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name",SKIP_OPEN_TABLE}, + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE}, + {"ROWS_CHANGED", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed",SKIP_OPEN_TABLE}, + {"ROWS_CHANGED_X_INDEXES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_changed_x_#indexes",SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, 0} +}; + +ST_FIELD_INFO index_stats_fields_info[]= +{ + {"TABLE_SCHEMA", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_schema",SKIP_OPEN_TABLE}, + {"TABLE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Table_name",SKIP_OPEN_TABLE}, + {"INDEX_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Index_name",SKIP_OPEN_TABLE}, + {"ROWS_READ", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONG, 0, 0, "Rows_read",SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0,0} +}; /* Find schema_tables elment by name @@ -6206,6 +6700,90 @@ int fill_schema_files(THD *thd, TABLE_LIST *tables, COND *cond) } +static +int store_key_cache_table_record(THD *thd, TABLE *table, + const char *name, uint name_length, + KEY_CACHE *key_cache, + uint partitions, uint partition_no) +{ + KEY_CACHE_STATISTICS keycache_stats; + uint err; + DBUG_ENTER("store_key_cache_table_record"); + + get_key_cache_statistics(key_cache, partition_no, &keycache_stats); + + if (!key_cache->key_cache_inited || keycache_stats.mem_size == 0) + DBUG_RETURN(0); + + restore_record(table, s->default_values); + table->field[0]->store(name, name_length, system_charset_info); + if (partitions == 0) + table->field[1]->set_null(); + else + { + table->field[1]->set_notnull(); + table->field[1]->store((long) partitions, TRUE); + } + + if (partition_no == 0) + table->field[2]->set_null(); + else + { + table->field[2]->set_notnull(); + table->field[2]->store((long) partition_no, TRUE); + } + table->field[3]->store(keycache_stats.mem_size, TRUE); + table->field[4]->store(keycache_stats.block_size, TRUE); + table->field[5]->store(keycache_stats.blocks_used, TRUE); + table->field[6]->store(keycache_stats.blocks_unused, TRUE); + table->field[7]->store(keycache_stats.blocks_changed, TRUE); + table->field[8]->store(keycache_stats.read_requests, TRUE); + table->field[9]->store(keycache_stats.reads, TRUE); + table->field[10]->store(keycache_stats.write_requests, TRUE); + table->field[11]->store(keycache_stats.writes, TRUE); + + err= schema_table_store_record(thd, table); + DBUG_RETURN(err); +} + + +int fill_key_cache_tables(THD *thd, TABLE_LIST *tables, COND *cond) +{ + TABLE *table= tables->table; + I_List_iterator<NAMED_LIST> it(key_caches); + NAMED_LIST *element; + DBUG_ENTER("fill_key_cache_tables"); + + while ((element= it++)) + { + KEY_CACHE *key_cache= (KEY_CACHE *) element->data; + + if (!key_cache->key_cache_inited) + continue; + + uint partitions= key_cache->partitions; + DBUG_ASSERT(partitions <= MAX_KEY_CACHE_PARTITIONS); + + if (partitions) + { + for (uint i= 0; i < partitions; i++) + { + if (store_key_cache_table_record(thd, table, + element->name, element->name_length, + key_cache, partitions, i+1)) + DBUG_RETURN(1); + } + } + + if (store_key_cache_table_record(thd, table, + element->name, element->name_length, + key_cache, partitions, 0)) + DBUG_RETURN(1); + } + DBUG_RETURN(0); +} + + ST_FIELD_INFO schema_fields_info[]= { {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, @@ -6691,6 +7269,8 @@ ST_FIELD_INFO plugin_fields_info[]= {"PLUGIN_AUTHOR", NAME_CHAR_LEN, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, {"PLUGIN_DESCRIPTION", 65535, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, {"PLUGIN_LICENSE", 80, MYSQL_TYPE_STRING, 0, 1, "License", SKIP_OPEN_TABLE}, + {"PLUGIN_MATURITY", 12, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, + {"PLUGIN_AUTH_VERSION", 80, MYSQL_TYPE_STRING, 0, 1, 0, SKIP_OPEN_TABLE}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} }; @@ -6783,6 +7363,35 @@ ST_FIELD_INFO referential_constraints_fields_info[]= }; +ST_FIELD_INFO keycache_fields_info[]= +{ + {"KEY_CACHE_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE}, + {"PARTITIONS", 3, MYSQL_TYPE_LONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED) , 0, SKIP_OPEN_TABLE}, + {"PARTITION_NUMBER", 3, MYSQL_TYPE_LONG, 0, + (MY_I_S_MAYBE_NULL | MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, + {"FULL_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE}, + {"BLOCK_SIZE", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_UNSIGNED), 0, SKIP_OPEN_TABLE }, + {"USED_BLOCKS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_UNSIGNED), "Key_blocks_used", SKIP_OPEN_TABLE}, + {"UNUSED_BLOCKS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_UNSIGNED), "Key_blocks_unused", SKIP_OPEN_TABLE}, + {"DIRTY_BLOCKS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_UNSIGNED), "Key_blocks_not_flushed", SKIP_OPEN_TABLE}, + {"READ_REQUESTS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_UNSIGNED), "Key_read_requests", SKIP_OPEN_TABLE}, + {"READS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_UNSIGNED), "Key_reads", SKIP_OPEN_TABLE}, + {"WRITE_REQUESTS", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_UNSIGNED), "Key_write_requests", SKIP_OPEN_TABLE}, + {"WRITES", MY_INT64_NUM_DECIMAL_DIGITS, MYSQL_TYPE_LONGLONG, 0, + (MY_I_S_UNSIGNED), "Key_writes", SKIP_OPEN_TABLE}, + {0, 0, MYSQL_TYPE_STRING, 0, 0, 0, SKIP_OPEN_TABLE} +}; + + /* Description of ST_FIELD_INFO in table.h @@ -6794,6 +7403,8 @@ ST_SCHEMA_TABLE schema_tables[]= { {"CHARACTER_SETS", charsets_fields_info, create_schema_table, fill_schema_charsets, make_character_sets_old_format, 0, -1, -1, 0, 0}, + {"CLIENT_STATISTICS", client_stats_fields_info, create_schema_table, + fill_schema_client_stats, make_old_format, 0, -1, -1, 0, 0}, {"COLLATIONS", collation_fields_info, create_schema_table, fill_schema_collation, make_old_format, 0, -1, -1, 0, 0}, {"COLLATION_CHARACTER_SET_APPLICABILITY", coll_charset_app_fields_info, @@ -6818,6 +7429,10 @@ ST_SCHEMA_TABLE schema_tables[]= fill_status, make_old_format, 0, 0, -1, 0, 0}, {"GLOBAL_VARIABLES", variables_fields_info, create_schema_table, fill_variables, make_old_format, 0, 0, -1, 0, 0}, + {"INDEX_STATISTICS", index_stats_fields_info, create_schema_table, + fill_schema_index_stats, make_old_format, 0, -1, -1, 0, 0}, + {"KEY_CACHES", keycache_fields_info, create_schema_table, + fill_key_cache_tables, make_old_format, 0, -1,-1, 0, 0}, {"KEY_COLUMN_USAGE", key_column_usage_fields_info, create_schema_table, get_all_tables, 0, get_schema_key_column_usage_record, 4, 5, 0, OPEN_TABLE_ONLY}, @@ -6859,11 +7474,15 @@ ST_SCHEMA_TABLE schema_tables[]= get_all_tables, make_table_names_old_format, 0, 1, 2, 1, 0}, {"TABLE_PRIVILEGES", table_privileges_fields_info, create_schema_table, fill_schema_table_privileges, 0, 0, -1, -1, 0, 0}, + {"TABLE_STATISTICS", table_stats_fields_info, create_schema_table, + fill_schema_table_stats, make_old_format, 0, -1, -1, 0, 0}, {"TRIGGERS", triggers_fields_info, create_schema_table, get_all_tables, make_old_format, get_schema_triggers_record, 5, 6, 0, OPEN_TABLE_ONLY}, {"USER_PRIVILEGES", user_privileges_fields_info, create_schema_table, fill_schema_user_privileges, 0, 0, -1, -1, 0, 0}, + {"USER_STATISTICS", user_stats_fields_info, create_schema_table, + fill_schema_user_stats, make_old_format, 0, -1, -1, 0, 0}, {"VARIABLES", variables_fields_info, create_schema_table, fill_variables, make_old_format, 0, 0, -1, 1, 0}, {"VIEWS", view_fields_info, create_schema_table, diff --git a/sql/sql_string.cc b/sql/sql_string.cc index bdecabd782b..db3d07a28a4 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -26,9 +26,11 @@ #ifdef HAVE_FCONVERT #include <floatingpoint.h> #endif - #include "sql_string.h" +#ifdef MYSQL_CLIENT +#error Attempt to use server-side sql_string on client. Use client/sql_string.cc +#endif /***************************************************************************** ** String functions *****************************************************************************/ diff --git a/sql/sql_string.h b/sql/sql_string.h index 9e22f7b6a27..a9df0dc2620 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -1,3 +1,5 @@ +#ifndef MYSQL_SQL_STRING_H_INCLUDED +#define MYSQL_SQL_STRING_H_INCLUDED /* Copyright (C) 2000 MySQL AB This program is free software; you can redistribute it and/or modify @@ -23,6 +25,10 @@ #define NOT_FIXED_DEC 31 #endif +#ifdef MYSQL_CLIENT +#error Attempt to use server-side sql_string on client. Use client/sql_string.h +#endif + class String; int sortcmp(const String *a,const String *b, CHARSET_INFO *cs); String *copy_if_not_alloced(String *a,String *b,uint32 arg_length); @@ -394,3 +400,5 @@ static inline bool check_if_only_end_space(CHARSET_INFO *cs, char *str, { return str+ cs->cset->scan(cs, str, end, MY_SEQ_SPACES) == end; } + +#endif // MYSQL_SQL_STRING_H_INCLUDED diff --git a/sql/sql_table.cc b/sql/sql_table.cc index 6b7b3e2c0ab..fb71cc82efe 100644 --- a/sql/sql_table.cc +++ b/sql/sql_table.cc @@ -19,6 +19,7 @@ #include <hash.h> #include <myisam.h> #include <my_dir.h> +#include "create_options.h" #include "sp_head.h" #include "sql_trigger.h" #include "sql_show.h" @@ -42,17 +43,10 @@ static int copy_data_between_tables(TABLE *from,TABLE *to, static bool prepare_blob_field(THD *thd, Create_field *sql_field); static bool check_engine(THD *, const char *, HA_CREATE_INFO *); -static int -mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, - Alter_info *alter_info, - bool tmp_table, - uint *db_options, - handler *file, KEY **key_info_buffer, - uint *key_count, int select_field_count); -static bool -mysql_prepare_alter_table(THD *thd, TABLE *table, - HA_CREATE_INFO *create_info, - Alter_info *alter_info); +static int mysql_prepare_create_table(THD *, HA_CREATE_INFO *, Alter_info *, + bool, uint *, handler *, KEY **, uint *, int); +static bool mysql_prepare_alter_table(THD *, TABLE *, HA_CREATE_INFO *, + Alter_info *); #ifndef DBUG_OFF @@ -2510,7 +2504,12 @@ int prepare_create_field(Create_field *sql_field, (sql_field->decimals << FIELDFLAG_DEC_SHIFT)); break; } - if (!(sql_field->flags & NOT_NULL_FLAG)) + if (sql_field->flags & NOT_NULL_FLAG) + DBUG_PRINT("info", ("1")); + if (sql_field->vcol_info) + DBUG_PRINT("info", ("2")); + 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; @@ -2824,6 +2823,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, null_fields--; sql_field->flags= dup_field->flags; sql_field->interval= dup_field->interval; + sql_field->vcol_info= dup_field->vcol_info; + sql_field->stored_in_db= dup_field->stored_in_db; it2.remove(); // Remove first (create) definition select_field_pos--; break; @@ -2856,7 +2857,28 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, sql_field->offset= record_offset; if (MTYP_TYPENR(sql_field->unireg_check) == Field::NEXT_NUMBER) auto_increment++; - record_offset+= sql_field->pack_length; + if (parse_option_list(thd, &sql_field->option_struct, + sql_field->option_list, + create_info->db_type->field_options, FALSE, + thd->mem_root)) + DBUG_RETURN(TRUE); + /* + For now skip fields that are not physically stored in the database + (virtual fields) and update their offset later + (see the next loop). + */ + if (sql_field->stored_in_db) + record_offset+= sql_field->pack_length; + } + /* Update virtual fields' offset*/ + it.rewind(); + while ((sql_field=it++)) + { + if (!sql_field->stored_in_db) + { + sql_field->offset= record_offset; + record_offset+= sql_field->pack_length; + } } if (timestamps_with_niladic > 1) { @@ -2906,6 +2928,8 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, if (key->type == Key::FOREIGN_KEY) { fk_key_count++; + if (((Foreign_key *)key)->validate(alter_info->create_list)) + DBUG_RETURN(TRUE); Foreign_key *fk_key= (Foreign_key*) key; if (fk_key->ref_columns.elements && fk_key->ref_columns.elements != fk_key->columns.elements) @@ -3036,6 +3060,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, key_info->key_part=key_part_info; key_info->usable_key_parts= key_number; key_info->algorithm= key->key_create_info.algorithm; + key_info->option_list= key->option_list; + if (parse_option_list(thd, &key_info->option_struct, + key_info->option_list, + create_info->db_type->index_options, FALSE, + thd->mem_root)) + DBUG_RETURN(TRUE); if (key->type == Key::FULLTEXT) { @@ -3192,6 +3222,17 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } #endif + if (!sql_field->stored_in_db) + { + /* Key fields must always be physically stored. */ + my_error(ER_KEY_BASED_ON_GENERATED_VIRTUAL_COLUMN, MYF(0)); + DBUG_RETURN(TRUE); + } + if (key->type == Key::PRIMARY && sql_field->vcol_info) + { + my_error(ER_PRIMARY_KEY_BASED_ON_VIRTUAL_COLUMN, MYF(0)); + DBUG_RETURN(TRUE); + } if (!(sql_field->flags & NOT_NULL_FLAG)) { if (key->type == Key::PRIMARY) @@ -3402,6 +3443,12 @@ mysql_prepare_create_table(THD *thd, HA_CREATE_INFO *create_info, } } + if (parse_option_list(thd, &create_info->option_struct, + create_info->option_list, + create_info->db_type->table_options, FALSE, + thd->mem_root)) + DBUG_RETURN(TRUE); + DBUG_RETURN(FALSE); } @@ -5643,6 +5690,7 @@ compare_tables(TABLE *table, KEY_PART_INFO *key_part; KEY_PART_INFO *end; THD *thd= table->in_use; + uint i; /* Remember if the new definition has new VARCHAR column; create_info->varchar will be reset in mysql_prepare_create_table. @@ -5733,6 +5781,12 @@ compare_tables(TABLE *table, DBUG_RETURN(0); } + if ((create_info->fileds_option_struct= + (void**)thd->calloc(sizeof(void*) * table->s->fields)) == NULL || + (create_info->keys_option_struct= + (void**)thd->calloc(sizeof(void*) * table->s->keys)) == NULL) + DBUG_RETURN(1); + /* Use transformed info to evaluate possibility of fast ALTER TABLE but use the preserved field to persist modifications. @@ -5740,15 +5794,19 @@ compare_tables(TABLE *table, new_field_it.init(alter_info->create_list); tmp_new_field_it.init(tmp_alter_info.create_list); - /* Go through fields and check if the original ones are compatible + /* + Go through fields and check if the original ones are compatible with new table. */ - for (f_ptr= table->field, new_field= new_field_it++, + for (i= 0, f_ptr= table->field, new_field= new_field_it++, tmp_new_field= tmp_new_field_it++; (field= *f_ptr); - f_ptr++, new_field= new_field_it++, + i++, f_ptr++, new_field= new_field_it++, tmp_new_field= tmp_new_field_it++) { + DBUG_ASSERT(i < table->s->fields); + create_info->fileds_option_struct[i]= tmp_new_field->option_struct; + /* Make sure we have at least the default charset in use. */ if (!new_field->charset) new_field->charset= create_info->default_table_charset; @@ -5762,6 +5820,19 @@ compare_tables(TABLE *table, DBUG_RETURN(0); } + /* + Check if the altered column is computed and either + is stored or is used in the partitioning expression. + TODO: Mark such a column with an alter flag only if + the defining expression has changed. + */ + if (field->vcol_info && + (field->stored_in_db || field->vcol_info->is_in_partitioning_expr())) + { + *need_copy_table= ALTER_TABLE_DATA_CHANGED; + DBUG_RETURN(0); + } + /* Don't pack rows in old tables if the user has requested this. */ if (create_info->row_type == ROW_TYPE_DYNAMIC || (tmp_new_field->flags & BLOB_FLAG) || @@ -5889,7 +5960,9 @@ compare_tables(TABLE *table, for (new_key= *key_info_buffer; new_key < new_key_end; new_key++) { /* Search an old key with the same name. */ - for (table_key= table->key_info; table_key < table_key_end; table_key++) + for (i= 0, table_key= table->key_info; + table_key < table_key_end; + i++, table_key++) { if (! strcmp(table_key->name, new_key->name)) break; @@ -5908,6 +5981,11 @@ compare_tables(TABLE *table, } DBUG_PRINT("info", ("index added: '%s'", new_key->name)); } + else + { + DBUG_ASSERT(i < table->s->keys); + create_info->keys_option_struct[i]= new_key->option_struct; + } } /* Check if changes are compatible with current handler without a copy */ @@ -6083,6 +6161,8 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, } restore_record(table, s->default_values); // Empty record for DEFAULT + create_info->option_list= merge_engine_table_options(table->s->option_list, + create_info->option_list, thd->mem_root); /* First collect all fields from table which isn't in drop_list */ @@ -6124,6 +6204,13 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, if (def) { // Field is changed def->field=field; + if (field->stored_in_db != def->stored_in_db) + { + my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN, + MYF(0), + "Changing the STORED status"); + goto err; + } if (!def->after) { new_create_list.push_back(def); @@ -6328,7 +6415,7 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, key= new Key(key_type, key_name, &key_create_info, test(key_info->flags & HA_GENERATED_KEY), - key_parts); + key_parts, key_info->option_list); new_key_list.push_back(key); } } @@ -6336,6 +6423,9 @@ mysql_prepare_alter_table(THD *thd, TABLE *table, Key *key; while ((key=key_it++)) // Add new keys { + if (key->type == Key::FOREIGN_KEY && + ((Foreign_key *)key)->validate(new_create_list)) + goto err; if (key->type != Key::FOREIGN_KEY) new_key_list.push_back(key); if (key->name && @@ -7742,6 +7832,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, /* Tell handler that we have values for all columns in the to table */ to->use_all_columns(); + to->mark_virtual_columns_for_write(); init_read_record(&info, thd, from, (SQL_SELECT *) 0, 1, 1, FALSE); errpos= 4; if (ignore) @@ -7756,6 +7847,7 @@ copy_data_between_tables(TABLE *from,TABLE *to, error= 1; break; } + update_virtual_fields(from); thd->row_count++; /* Return error if source table isn't empty. */ if (error_if_not_empty) @@ -7776,6 +7868,12 @@ copy_data_between_tables(TABLE *from,TABLE *to, copy_ptr->do_copy(copy_ptr); } prev_insert_id= to->file->next_insert_id; + update_virtual_fields(to, TRUE); + if (thd->is_error()) + { + error= 1; + break; + } error=to->file->ha_write_row(to->record[0]); to->auto_increment_field_not_null= FALSE; if (error) @@ -7957,7 +8055,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables, goto err; } ha_checksum row_crc= 0; - int error= t->file->rnd_next(t->record[0]); + int error= t->file->ha_rnd_next(t->record[0]); if (unlikely(error)) { if (error == HA_ERR_RECORD_DELETED) diff --git a/sql/sql_test.cc b/sql/sql_test.cc index 5de8301fd05..5c9077f86ea 100644 --- a/sql/sql_test.cc +++ b/sql/sql_test.cc @@ -445,7 +445,8 @@ static int print_key_cache_status(const char *name, KEY_CACHE *key_cache) Buffer_size: %10lu\n\ Block_size: %10lu\n\ Division_limit: %10lu\n\ -Age_limit: %10lu\n\ +Age_threshold: %10lu\n\ +Partitions: %10lu\n\ blocks used: %10lu\n\ not flushed: %10lu\n\ w_requests: %10s\n\ @@ -455,6 +456,7 @@ reads: %10s\n\n", name, (ulong) key_cache->param_buff_size, key_cache->param_block_size, key_cache->param_division_limit, key_cache->param_age_threshold, + key_cache->param_partitions, key_cache->blocks_used,key_cache->global_blocks_changed, llstr(key_cache->global_cache_w_requests,llbuff1), llstr(key_cache->global_cache_write,llbuff2), diff --git a/sql/sql_udf.cc b/sql/sql_udf.cc index d455a66c4f2..b18f0c28bdf 100644 --- a/sql/sql_udf.cc +++ b/sql/sql_udf.cc @@ -577,10 +577,10 @@ int mysql_drop_function(THD *thd,const LEX_STRING *udf_name) goto err; table->use_all_columns(); table->field[0]->store(exact_name_str, exact_name_len, &my_charset_bin); - if (!table->file->index_read_idx_map(table->record[0], 0, - (uchar*) table->field[0]->ptr, - HA_WHOLE_KEY, - HA_READ_KEY_EXACT)) + if (!table->file->ha_index_read_idx_map(table->record[0], 0, + (uchar*) table->field[0]->ptr, + HA_WHOLE_KEY, + HA_READ_KEY_EXACT)) { int error; if ((error = table->file->ha_delete_row(table->record[0]))) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 0bef5aa3ae8..d9f4d84623d 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -143,7 +143,7 @@ static void prepare_record_for_error_message(int error, TABLE *table) /* Tell the engine about the new set. */ table->file->column_bitmaps_signal(); /* Read record that is identified by table->file->ref. */ - (void) table->file->rnd_pos(table->record[1], table->file->ref); + (void) table->file->ha_rnd_pos(table->record[1], table->file->ref); /* Copy the newly read columns into the new record. */ for (field_p= table->field; (field= *field_p); field_p++) if (bitmap_is_set(&unique_map, field->field_index)) @@ -1939,7 +1939,7 @@ int multi_update::do_updates() { if (thd->killed && trans_safe) goto err; - if ((local_error=tmp_table->file->rnd_next(tmp_table->record[0]))) + if ((local_error= tmp_table->file->ha_rnd_next(tmp_table->record[0]))) { if (local_error == HA_ERR_END_OF_FILE) break; @@ -1954,12 +1954,12 @@ int multi_update::do_updates() uint field_num= 0; do { - if((local_error= - tbl->file->rnd_pos(tbl->record[0], - (uchar *) tmp_table->field[field_num]->ptr))) + if ((local_error= + tbl->file->ha_rnd_pos(tbl->record[0], + (uchar*) tmp_table->field[field_num]->ptr))) goto err; field_num++; - } while((tbl= check_opt_it++)); + } while ((tbl= check_opt_it++)); table->status|= STATUS_UPDATED; store_record(table,record[1]); diff --git a/sql/sql_view.cc b/sql/sql_view.cc index e1e0b3ff6c4..8ef23cfd078 100644 --- a/sql/sql_view.cc +++ b/sql/sql_view.cc @@ -1185,7 +1185,7 @@ bool mysql_make_view(THD *thd, File_parser *parser, TABLE_LIST *table, + MODE_PIPES_AS_CONCAT affect expression parsing + MODE_ANSI_QUOTES affect expression parsing + MODE_IGNORE_SPACE affect expression parsing - - MODE_NOT_USED not used :) + - MODE_IGNORE_BAD_TABLE_OPTIONS affect only CREATE/ALTER TABLE parsing * MODE_ONLY_FULL_GROUP_BY affect execution * MODE_NO_UNSIGNED_SUBTRACTION affect execution - MODE_NO_DIR_IN_CREATE affect table creation only diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index fc2e18f28f2..cae37ddf2e6 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -44,6 +44,7 @@ #include "sp_rcontext.h" #include "sp.h" #include "event_parse_data.h" +#include "create_options.h" #include <myisam.h> #include <myisammrg.h> @@ -56,6 +57,7 @@ int yylex(void *yylval, void *yythd); const LEX_STRING null_lex_str= {0,0}; +const LEX_STRING empty_lex_str= { (char*) "", 0 }; #define yyoverflow(A,B,C,D,E,F) \ { \ @@ -607,6 +609,7 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table) lex->alter_info.flags= ALTER_ADD_INDEX; lex->col_list.empty(); lex->change= NullS; + lex->option_list= NULL; return FALSE; } @@ -616,7 +619,7 @@ static bool add_create_index (LEX *lex, Key::Keytype type, const char *name, { Key *key; key= new Key(type, name, info ? info : &lex->key_create_info, generated, - lex->col_list); + lex->col_list, lex->option_list); if (key == NULL) return TRUE; @@ -710,6 +713,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %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 */ @@ -759,6 +763,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token CHECK_SYM /* SQL-2003-R */ %token CIPHER_SYM %token CLIENT_SYM +%token CLIENT_STATS_SYM %token CLOSE_SYM /* SQL-2003-R */ %token COALESCE /* SQL-2003-N */ %token CODE_SYM @@ -876,6 +881,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token FULLTEXT_SYM %token FUNCTION_SYM /* SQL-2003-R */ %token GE +%token GENERATED_SYM %token GEOMETRYCOLLECTION %token GEOMETRY_SYM %token GET_FORMAT /* MYSQL-FUNC */ @@ -905,6 +911,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IMPORT %token INDEXES %token INDEX_SYM +%token INDEX_STATS_SYM %token INFILE %token INITIAL_SIZE_SYM %token INNER_SYM /* SQL-2003-R */ @@ -1053,11 +1060,13 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token PAGE_CHECKSUM_SYM %token PARAM_MARKER %token PARSER_SYM +%token PARSE_VCOL_EXPR_SYM %token PARTIAL /* SQL-2003-N */ %token PARTITIONING_SYM %token PARTITIONS_SYM %token PARTITION_SYM /* SQL-2003-R */ %token PASSWORD +%token PERSISTENT_SYM %token PHASE_SYM %token PLUGINS_SYM %token PLUGIN_SYM @@ -1146,6 +1155,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token SIGNED_SYM %token SIMPLE_SYM /* SQL-2003-N */ %token SLAVE +%token SLOW_SYM %token SMALLINT /* SQL-2003-R */ %token SNAPSHOT_SYM %token SOCKET_SYM @@ -1190,6 +1200,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token TABLES %token TABLESPACE %token TABLE_REF_PRIORITY +%token TABLE_STATS_SYM %token TABLE_SYM /* SQL-2003-R */ %token TABLE_CHECKSUM_SYM %token TEMPORARY /* SQL-2003-N */ @@ -1237,6 +1248,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token UPGRADE_SYM %token USAGE /* SQL-2003-N */ %token USER /* SQL-2003-R */ +%token USER_STATS_SYM %token USE_FRM %token USE_SYM %token USING /* SQL-2003-R */ @@ -1251,7 +1263,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %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 @@ -1310,7 +1324,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); text_string opt_gconcat_separator %type <num> - type int_type real_type order_dir lock_option + type int_type real_type order_dir lock_option field_def udf_type if_exists opt_local opt_table_options table_options table_option opt_if_not_exists opt_no_write_to_binlog delete_option opt_temporary all_or_any opt_distinct @@ -1469,6 +1483,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); key_using_alg server_def server_options_list server_option definer_opt no_definer definer + parse_vcol_expr vcol_opt_specifier vcol_opt_attribute + vcol_opt_attribute_list vcol_attribute END_OF_INPUT %type <NONE> call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -1600,6 +1616,7 @@ statement: | lock | optimize | keycache + | parse_vcol_expr | partition_entry | preload | prepare @@ -2327,6 +2344,7 @@ sp_init_param: lex->interval_list.empty(); lex->uint_geom_type= 0; + lex->vcol_info= 0; } ; @@ -3882,7 +3900,11 @@ create2: ; create2a: - field_list ')' opt_create_table_options + field_list ')' + { + Lex->create_info.option_list= NULL; + } + opt_create_table_options opt_partitioning create3 {} | opt_partitioning @@ -4735,6 +4757,30 @@ create_table_option: Lex->create_info.used_fields|= HA_CREATE_USED_TRANSACTIONAL; Lex->create_info.transactional= $3; } + | IDENT_sys equal TEXT_STRING_sys + { + new (YYTHD->mem_root) + engine_option_value($1, $3, true, &Lex->create_info.option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ident + { + new (YYTHD->mem_root) + engine_option_value($1, $3, false, &Lex->create_info.option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ulonglong_num + { + new (YYTHD->mem_root) + engine_option_value($1, $3, &Lex->create_info.option_list, + &Lex->option_list_last, YYTHD->mem_root); + } + | IDENT_sys equal DEFAULT + { + new (YYTHD->mem_root) + engine_option_value($1, &Lex->create_info.option_list, + &Lex->option_list_last); + } ; default_charset: @@ -4856,25 +4902,33 @@ column_def: ; key_def: - normal_key_type opt_ident key_alg '(' key_list ')' normal_key_options + normal_key_type opt_ident key_alg '(' key_list ')' + { Lex->option_list= NULL; } + normal_key_options { if (add_create_index (Lex, $1, $2)) MYSQL_YYABORT; } | fulltext opt_key_or_index opt_ident init_key_options - '(' key_list ')' fulltext_key_options + '(' key_list ')' + { Lex->option_list= NULL; } + fulltext_key_options { if (add_create_index (Lex, $1, $3)) MYSQL_YYABORT; } | spatial opt_key_or_index opt_ident init_key_options - '(' key_list ')' spatial_key_options + '(' key_list ')' + { Lex->option_list= NULL; } + spatial_key_options { if (add_create_index (Lex, $1, $3)) MYSQL_YYABORT; } | opt_constraint constraint_key_type opt_ident key_alg - '(' key_list ')' normal_key_options + '(' key_list ')' + { Lex->option_list= NULL; } + normal_key_options { if (add_create_index (Lex, $2, $3 ? $3 : $1)) MYSQL_YYABORT; @@ -4936,8 +4990,10 @@ field_spec: lex->default_value= lex->on_update_value= 0; lex->comment=null_lex_str; lex->charset=NULL; + lex->vcol_info= 0; + lex->option_list= NULL; } - type opt_attribute + field_def { LEX *lex=Lex; if (add_field_to_list(lex->thd, &$1, (enum enum_field_types) $3, @@ -4945,8 +5001,98 @@ field_spec: lex->default_value, lex->on_update_value, &lex->comment, lex->change,&lex->interval_list,lex->charset, - lex->uint_geom_type)) + lex->uint_geom_type, + lex->vcol_info, lex->option_list)) + MYSQL_YYABORT; + } + ; + +field_def: + type opt_attribute {} + | type opt_generated_always AS '(' virtual_column_func ')' + vcol_opt_specifier + vcol_opt_attribute + { + $$= (enum enum_field_types)MYSQL_TYPE_VIRTUAL; + Lex->vcol_info->set_field_type((enum enum_field_types) $1); + } + ; + +opt_generated_always: + /* empty */ + | GENERATED_SYM ALWAYS_SYM {} + ; + +vcol_opt_specifier: + /* empty */ + { + Lex->vcol_info->set_stored_in_db_flag(FALSE); + } + | VIRTUAL_SYM + { + Lex->vcol_info->set_stored_in_db_flag(FALSE); + } + | PERSISTENT_SYM + { + Lex->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->type|= UNIQUE_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | UNIQUE_SYM KEY_SYM + { + LEX *lex=Lex; + lex->type|= UNIQUE_KEY_FLAG; + lex->alter_info.flags|= ALTER_ADD_INDEX; + } + | COMMENT_SYM TEXT_STRING_sys { Lex->comment= $2; } + ; + +parse_vcol_expr: + PARSE_VCOL_EXPR_SYM '(' virtual_column_func ')' + { + /* + "PARSE_VCOL_EXPR" can only be used by the SQL server + when reading a '*.frm' file. + Prevent the end user from invoking this command. + */ + if (!Lex->parse_vcol_expr) + { + my_message(ER_SYNTAX_ERROR, ER(ER_SYNTAX_ERROR), MYF(0)); MYSQL_YYABORT; + } + } + ; + +virtual_column_func: + remember_name expr remember_end + { + Lex->vcol_info= new Virtual_column_info(); + if (!Lex->vcol_info) + { + mem_alloc_error(sizeof(Virtual_column_info)); + MYSQL_YYABORT; + } + uint expr_len= (uint)($3 - $1) - 1; + Lex->vcol_info->expr_str.str= (char* ) sql_memdup($1 + 1, expr_len); + Lex->vcol_info->expr_str.length= expr_len; + Lex->vcol_info->expr_item= $2; } ; @@ -5276,6 +5422,29 @@ attribute: Lex->charset=$2; } } + | IDENT_sys equal TEXT_STRING_sys + { + new (YYTHD->mem_root) + engine_option_value($1, $3, true, &Lex->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ident + { + new (YYTHD->mem_root) + engine_option_value($1, $3, false, &Lex->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ulonglong_num + { + new (YYTHD->mem_root) + engine_option_value($1, $3, &Lex->option_list, + &Lex->option_list_last, YYTHD->mem_root); + } + | IDENT_sys equal DEFAULT + { + new (YYTHD->mem_root) + engine_option_value($1, &Lex->option_list, &Lex->option_list_last); + } ; now_or_signed_literal: @@ -5565,6 +5734,29 @@ key_using_alg: all_key_opt: KEY_BLOCK_SIZE opt_equal ulong_num { Lex->key_create_info.block_size= $3; } + | IDENT_sys equal TEXT_STRING_sys + { + new (YYTHD->mem_root) + engine_option_value($1, $3, true, &Lex->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ident + { + new (YYTHD->mem_root) + engine_option_value($1, $3, false, &Lex->option_list, + &Lex->option_list_last); + } + | IDENT_sys equal ulonglong_num + { + new (YYTHD->mem_root) + engine_option_value($1, $3, &Lex->option_list, + &Lex->option_list_last, YYTHD->mem_root); + } + | IDENT_sys equal DEFAULT + { + new (YYTHD->mem_root) + engine_option_value($1, &Lex->option_list, &Lex->option_list_last); + } ; normal_key_opt: @@ -6054,6 +6246,7 @@ alter_list_item: LEX *lex=Lex; lex->change= $3.str; lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + lex->option_list= NULL; } field_spec opt_place | MODIFY_SYM opt_column field_ident @@ -6064,8 +6257,10 @@ alter_list_item: lex->comment=null_lex_str; lex->charset= NULL; lex->alter_info.flags|= ALTER_CHANGE_COLUMN; + lex->vcol_info= 0; + lex->option_list= NULL; } - type opt_attribute + field_def { LEX *lex=Lex; if (add_field_to_list(lex->thd,&$3, @@ -6074,7 +6269,8 @@ alter_list_item: lex->default_value, lex->on_update_value, &lex->comment, $3.str, &lex->interval_list, lex->charset, - lex->uint_geom_type)) + lex->uint_geom_type, + lex->vcol_info, lex->option_list)) MYSQL_YYABORT; } opt_place @@ -6181,8 +6377,7 @@ alter_list_item: } | create_table_options_space_separated { - LEX *lex=Lex; - lex->alter_info.flags|= ALTER_OPTIONS; + Lex->alter_info.flags|= ALTER_OPTIONS; } | FORCE_SYM { @@ -10339,6 +10534,34 @@ show_param: { Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; } + | CLIENT_STATS_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_CLIENT_STATS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_CLIENT_STATS)) + MYSQL_YYABORT; + } + | USER_STATS_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_USER_STATS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_USER_STATS)) + MYSQL_YYABORT; + } + | TABLE_STATS_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_TABLE_STATS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_TABLE_STATS)) + MYSQL_YYABORT; + } + | INDEX_STATS_SYM + { + LEX *lex= Lex; + lex->sql_command= SQLCOM_SHOW_INDEX_STATS; + if (prepare_schema_table(YYTHD, lex, 0, SCH_INDEX_STATS)) + MYSQL_YYABORT; + } | CREATE PROCEDURE sp_name { LEX *lex= Lex; @@ -10547,6 +10770,16 @@ flush_option: { Lex->type|= REFRESH_STATUS; } | SLAVE { Lex->type|= REFRESH_SLAVE; } + | SLOW_SYM QUERY_SYM LOGS_SYM + { Lex->type |= REFRESH_SLOW_QUERY_LOG; } + | CLIENT_STATS_SYM + { Lex->type|= REFRESH_CLIENT_STATS; } + | USER_STATS_SYM + { Lex->type|= REFRESH_USER_STATS; } + | TABLE_STATS_SYM + { Lex->type|= REFRESH_TABLE_STATS; } + | INDEX_STATS_SYM + { Lex->type|= REFRESH_INDEX_STATS; } | MASTER_SYM { Lex->type|= REFRESH_MASTER; } | DES_KEY_FILE @@ -11529,6 +11762,9 @@ user: $$->user = $1; $$->host.str= (char *) "%"; $$->host.length= 1; + $$->password= null_lex_str; + $$->plugin= empty_lex_str; + $$->auth= empty_lex_str; if (check_string_char_length(&$$->user, ER(ER_USERNAME), USERNAME_CHAR_LENGTH, @@ -11541,6 +11777,9 @@ user: if (!($$=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) MYSQL_YYABORT; $$->user = $1; $$->host=$3; + $$->password= null_lex_str; + $$->plugin= empty_lex_str; + $$->auth= empty_lex_str; if (check_string_char_length(&$$->user, ER(ER_USERNAME), USERNAME_CHAR_LENGTH, @@ -11628,6 +11867,7 @@ keyword_sp: | AGAINST {} | AGGREGATE_SYM {} | ALGORITHM_SYM {} + | ALWAYS_SYM {} | ANY_SYM {} | AT_SYM {} | AUTHORS_SYM {} @@ -11645,6 +11885,7 @@ keyword_sp: | CHAIN_SYM {} | CHANGED {} | CIPHER_SYM {} + | CLIENT_STATS_SYM {} | CLIENT_SYM {} | COALESCE {} | CODE_SYM {} @@ -11697,6 +11938,7 @@ keyword_sp: | FIRST_SYM {} | FIXED_SYM {} | FRAC_SECOND_SYM {} + | GENERATED_SYM {} | GEOMETRY_SYM {} | GEOMETRYCOLLECTION {} | GET_FORMAT {} @@ -11706,6 +11948,7 @@ keyword_sp: | HOSTS_SYM {} | HOUR_SYM {} | IDENTIFIED_SYM {} + | INDEX_STATS_SYM {} | INVOKER_SYM {} | IMPORT {} | INDEXES {} @@ -11784,6 +12027,7 @@ keyword_sp: | PARTITIONING_SYM {} | PARTITIONS_SYM {} | PASSWORD {} + | PERSISTENT_SYM {} | PHASE_SYM {} | PLUGIN_SYM {} | PLUGINS_SYM {} @@ -11829,6 +12073,7 @@ keyword_sp: | SIMPLE_SYM {} | SHARE_SYM {} | SHUTDOWN {} + | SLOW_SYM {} | SNAPSHOT_SYM {} | SOUNDS_SYM {} | SOURCE_SYM {} @@ -11848,6 +12093,7 @@ keyword_sp: | SUSPEND_SYM {} | SWAPS_SYM {} | SWITCHES_SYM {} + | TABLE_STATS_SYM {} | TABLES {} | TABLE_CHECKSUM_SYM {} | TABLESPACE {} @@ -11873,9 +12119,11 @@ keyword_sp: | UNKNOWN_SYM {} | UNTIL_SYM {} | USER {} + | USER_STATS_SYM {} | USE_FRM {} | VARIABLES {} | VIEW_SYM {} + | VIRTUAL_SYM {} | VALUE_SYM {} | WARNINGS {} | WAIT_SYM {} @@ -12788,6 +13036,18 @@ grant_user: } | user IDENTIFIED_SYM BY PASSWORD TEXT_STRING { $$= $1; $1->password= $5; } + | user IDENTIFIED_SYM VIA_SYM ident_or_text + { + $$= $1; + $1->plugin= $4; + $1->auth= empty_lex_str; + } + | user IDENTIFIED_SYM VIA_SYM ident_or_text USING TEXT_STRING_sys + { + $$= $1; + $1->plugin= $4; + $1->auth= $6; + } | user { $$= $1; $1->password= null_lex_str; } ; @@ -13476,6 +13736,7 @@ sf_tail: lex->length= lex->dec= NULL; lex->interval_list.empty(); lex->type= 0; + lex->vcol_info= 0; } type /* $11 */ { /* $12 */ diff --git a/sql/structs.h b/sql/structs.h index 2546d241059..64e69fca0d0 100644 --- a/sql/structs.h +++ b/sql/structs.h @@ -68,6 +68,7 @@ typedef struct st_key_part_info { /* Info about a key part */ uint8 null_bit; /* Position to null_bit */ } KEY_PART_INFO ; +class engine_option_value; typedef struct st_key { uint key_length; /* Tot length of key */ @@ -76,6 +77,7 @@ typedef struct st_key { uint extra_length; uint usable_key_parts; /* Should normally be = key_parts */ uint block_size; + uint name_length; enum ha_key_alg algorithm; /* Note that parser is used when the table is opened for use, and @@ -88,6 +90,8 @@ typedef struct st_key { }; KEY_PART_INFO *key_part; char *name; /* Name of key */ + /* Unique name for cache; db + \0 + table_name + \0 + key_name + \0 */ + uchar *cache_name; /* Array of AVG(#records with the same field value) for 1st ... Nth key part. 0 means 'not known'. @@ -98,6 +102,9 @@ typedef struct st_key { int bdb_return_if_eq; } handler; struct st_table *table; + /** reference to the list of options or NULL */ + engine_option_value *option_list; + void *option_struct; /* structure with parsed options */ } KEY; @@ -175,7 +182,7 @@ 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, password; + LEX_STRING user, host, password, plugin, auth; } LEX_USER; /* @@ -237,6 +244,111 @@ typedef struct user_conn { USER_RESOURCES user_resources; } USER_CONN; +typedef struct st_user_stats +{ + char user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1]; + // Account name the user is mapped to when this is a user from mapped_user. + // Otherwise, the same value as user. + char priv_user[max(USERNAME_LENGTH, LIST_PROCESS_HOST_LEN) + 1]; + uint user_name_length; + uint total_connections; + uint concurrent_connections; + time_t connected_time; // in seconds + double busy_time; // in seconds + double cpu_time; // in seconds + ulonglong bytes_received; + ulonglong bytes_sent; + ulonglong binlog_bytes_written; + ha_rows rows_read, rows_sent; + ha_rows rows_updated, rows_deleted, rows_inserted; + ulonglong select_commands, update_commands, other_commands; + ulonglong commit_trans, rollback_trans; + ulonglong denied_connections, lost_connections; + ulonglong access_denied_errors; + ulonglong empty_queries; +} USER_STATS; + +/* Lookup function for hash tables with USER_STATS entries */ +extern "C" uchar *get_key_user_stats(USER_STATS *user_stats, size_t *length, + my_bool not_used __attribute__((unused))); + +/* Free all memory for a hash table with USER_STATS entries */ +extern void free_user_stats(USER_STATS* user_stats); + +/* Intialize an instance of USER_STATS */ +extern void +init_user_stats(USER_STATS *user_stats, + const char *user, + size_t user_length, + const char *priv_user, + uint total_connections, + uint concurrent_connections, + time_t connected_time, + double busy_time, + double cpu_time, + ulonglong bytes_received, + ulonglong bytes_sent, + ulonglong binlog_bytes_written, + ha_rows rows_sent, + ha_rows rows_read, + ha_rows rows_inserted, + ha_rows rows_deleted, + ha_rows rows_updated, + ulonglong select_commands, + ulonglong update_commands, + ulonglong other_commands, + ulonglong commit_trans, + ulonglong rollback_trans, + ulonglong denied_connections, + ulonglong lost_connections, + ulonglong access_denied_errors, + ulonglong empty_queries); + +/* Increment values of an instance of USER_STATS */ +extern void +add_user_stats(USER_STATS *user_stats, + uint total_connections, + uint concurrent_connections, + time_t connected_time, + double busy_time, + double cpu_time, + ulonglong bytes_received, + ulonglong bytes_sent, + ulonglong binlog_bytes_written, + ha_rows rows_sent, + ha_rows rows_read, + ha_rows rows_inserted, + ha_rows rows_deleted, + ha_rows rows_updated, + ulonglong select_commands, + ulonglong update_commands, + ulonglong other_commands, + ulonglong commit_trans, + ulonglong rollback_trans, + ulonglong denied_connections, + ulonglong lost_connections, + ulonglong access_denied_errors, + ulonglong empty_queries); + +typedef struct st_table_stats +{ + char table[NAME_LEN * 2 + 2]; // [db] + '\0' + [table] + '\0' + uint table_name_length; + ulonglong rows_read, rows_changed; + ulonglong rows_changed_x_indexes; + /* Stores enum db_type, but forward declarations cannot be done */ + int engine_type; +} TABLE_STATS; + +typedef struct st_index_stats +{ + // [db] + '\0' + [table] + '\0' + [index] + '\0' + char index[NAME_LEN * 3 + 3]; + uint index_name_length; /* Length of 'index' */ + ulonglong rows_read; +} INDEX_STATS; + + /* Bits in form->update */ #define REG_MAKE_DUPP 1 /* Make a copy of record when read */ #define REG_NEW_RECORD 2 /* Write a new record if not found */ diff --git a/sql/table.cc b/sql/table.cc index 733aa3e6887..cf079ed212a 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -18,6 +18,7 @@ #include "mysql_priv.h" #include "sql_trigger.h" +#include "create_options.h" #include <m_ctype.h> #include "my_md5.h" @@ -33,6 +34,12 @@ LEX_STRING GENERAL_LOG_NAME= {C_STRING_WITH_LEN("general_log")}; /* SLOW_LOG name */ LEX_STRING SLOW_LOG_NAME= {C_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 +*/ +LEX_STRING parse_vcol_keyword= { C_STRING_WITH_LEN("PARSE_VCOL_EXPR ") }; + /* Functions defined in this file */ void open_table_error(TABLE_SHARE *share, int error, int db_errno, @@ -660,12 +667,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, uint interval_count, interval_parts, read_length, int_length; uint db_create_options, keys, key_parts, n_length; uint key_info_length, com_length, null_bit_pos; - uint extra_rec_buf_length; + uint vcol_screen_length; + uint extra_rec_buf_length, options_len; uint i,j; bool use_hash; - char *keynames, *names, *comment_pos; + char *keynames, *names, *comment_pos, *vcol_screen_pos; uchar *record; - uchar *disk_buff, *strpos, *null_flags, *null_pos; + uchar *disk_buff, *strpos, *null_flags, *null_pos, *options; + uchar *buff= 0; ulong pos, record_offset, *rec_per_key, rec_buff_length; handler *handler_file= 0; KEY *keyinfo; @@ -781,7 +790,6 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, for (i=0 ; i < keys ; i++, keyinfo++) { - keyinfo->table= 0; // Updated in open_frm if (new_frm_ver >= 3) { keyinfo->flags= (uint) uint2korr(strpos) ^ HA_NOSAME; @@ -833,6 +841,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, strpos+= (strmov(keynames, (char *) strpos) - keynames)+1; share->reclength = uint2korr((head+16)); + share->stored_rec_length= share->reclength; if (*(head+26) == 1) share->system= 1; /* one-record-database */ #ifdef HAVE_CRYPTED_FRM @@ -850,15 +859,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if ((n_length= uint4korr(head+55))) { /* Read extra data segment */ - uchar *buff, *next_chunk, *buff_end; + uchar *next_chunk, *buff_end; DBUG_PRINT("info", ("extra segment size is %u bytes", n_length)); if (!(next_chunk= buff= (uchar*) my_malloc(n_length, MYF(MY_WME)))) goto err; if (my_pread(file, buff, n_length, record_offset + share->reclength, MYF(MY_NABP))) { - my_free(buff, MYF(0)); - goto err; + goto free_and_err; } share->connect_string.length= uint2korr(buff); if (!(share->connect_string.str= strmake_root(&share->mem_root, @@ -866,8 +874,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->connect_string. length))) { - my_free(buff, MYF(0)); - goto err; + goto free_and_err; } next_chunk+= share->connect_string.length + 2; buff_end= buff + n_length; @@ -887,8 +894,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, plugin_data(tmp_plugin, handlerton *))) { /* bad file, legacy_db_type did not match the name */ - my_free(buff, MYF(0)); - goto err; + goto free_and_err; } /* tmp_plugin is locked with a local lock. @@ -917,8 +923,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, error= 8; my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-partition"); - my_free(buff, MYF(0)); - goto err; + goto free_and_err; } plugin_unlock(NULL, share->db_plugin); share->db_plugin= ha_lock_engine(NULL, partition_hton); @@ -932,8 +937,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, /* purecov: begin inspected */ error= 8; my_error(ER_UNKNOWN_STORAGE_ENGINE, MYF(0), name.str); - my_free(buff, MYF(0)); - goto err; + goto free_and_err; /* purecov: end */ } next_chunk+= str_db_type_length + 2; @@ -949,16 +953,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, memdup_root(&share->mem_root, next_chunk + 4, partition_info_len + 1))) { - my_free(buff, MYF(0)); - goto err; + goto free_and_err; } } #else if (partition_info_len) { DBUG_PRINT("info", ("WITH_PARTITION_STORAGE_ENGINE is not defined")); - my_free(buff, MYF(0)); - goto err; + goto free_and_err; } #endif next_chunk+= 5 + partition_info_len; @@ -994,8 +996,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { DBUG_PRINT("error", ("fulltext key uses parser that is not defined in .frm")); - my_free(buff, MYF(0)); - goto err; + goto free_and_err; } parser_name.str= (char*) next_chunk; parser_name.length= strlen((char*) next_chunk); @@ -1005,12 +1006,20 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (! keyinfo->parser) { my_error(ER_PLUGIN_IS_NOT_LOADED, MYF(0), parser_name.str); - my_free(buff, MYF(0)); - goto err; + goto free_and_err; } } } - my_free(buff, MYF(0)); + if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS) + { + /* + store options position, but skip till the time we will + know number of fields + */ + options_len= uint4korr(next_chunk); + options= next_chunk + 4; + next_chunk+= options_len + 4; + } } share->key_block_size= uint2korr(head+62); @@ -1020,21 +1029,21 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->rec_buff_length= rec_buff_length; if (!(record= (uchar *) alloc_root(&share->mem_root, rec_buff_length))) - goto err; /* purecov: inspected */ + goto free_and_err; /* purecov: inspected */ share->default_values= record; if (my_pread(file, record, (size_t) share->reclength, record_offset, MYF(MY_NABP))) - goto err; /* purecov: inspected */ + goto free_and_err; /* purecov: inspected */ VOID(my_seek(file,pos,MY_SEEK_SET,MYF(0))); if (my_read(file, head,288,MYF(MY_NABP))) - goto err; + goto free_and_err; #ifdef HAVE_CRYPTED_FRM if (crypted) { crypted->decode((char*) head+256,288-256); if (sint2korr(head+284) != 0) // Should be 0 - goto err; // Wrong password + goto free_and_err; // Wrong password } #endif @@ -1046,11 +1055,15 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, int_length= uint2korr(head+274); share->null_fields= uint2korr(head+282); com_length= uint2korr(head+284); + vcol_screen_length= uint2korr(head+286); + share->vfields= 0; + share->stored_fields= share->fields; share->comment.length= (int) (head[46]); share->comment.str= strmake_root(&share->mem_root, (char*) head+47, share->comment.length); - DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length)); + DBUG_PRINT("info",("i_count: %d i_parts: %d index: %d n_length: %d int_length: %d com_length: %d vcol_screen_length: %d", interval_count,interval_parts, share->keys,n_length,int_length, com_length, vcol_screen_length)); + if (!(field_ptr = (Field **) alloc_root(&share->mem_root, @@ -1058,14 +1071,16 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, interval_count*sizeof(TYPELIB)+ (share->fields+interval_parts+ keys+3)*sizeof(char *)+ - (n_length+int_length+com_length))))) - goto err; /* purecov: inspected */ + (n_length+int_length+com_length+ + vcol_screen_length))))) + goto free_and_err; /* purecov: inspected */ share->field= field_ptr; read_length=(uint) (share->fields * field_pack_length + - pos+ (uint) (n_length+int_length+com_length)); + pos+ (uint) (n_length+int_length+com_length+ + vcol_screen_length)); if (read_string(file,(uchar**) &disk_buff,read_length)) - goto err; /* purecov: inspected */ + goto free_and_err; /* purecov: inspected */ #ifdef HAVE_CRYPTED_FRM if (crypted) { @@ -1084,11 +1099,15 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, memcpy((char*) names, strpos+(share->fields*field_pack_length), (uint) (n_length+int_length)); comment_pos= names+(n_length+int_length); - memcpy(comment_pos, disk_buff+read_length-com_length, com_length); + memcpy(comment_pos, disk_buff+read_length-com_length-vcol_screen_length, + com_length); + vcol_screen_pos= names+(n_length+int_length+com_length); + memcpy(vcol_screen_pos, disk_buff+read_length-vcol_screen_length, + vcol_screen_length); fix_type_pointers(&interval_array, &share->fieldnames, 1, &names); if (share->fieldnames.count != share->fields) - goto err; + goto free_and_err; fix_type_pointers(&interval_array, share->intervals, interval_count, &names); @@ -1102,7 +1121,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, uint count= (uint) (interval->count + 1) * sizeof(uint); if (!(interval->type_lengths= (uint *) alloc_root(&share->mem_root, count))) - goto err; + goto free_and_err; for (count= 0; count < interval->count; count++) { char *val= (char*) interval->type_names[count]; @@ -1118,7 +1137,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, /* Allocate handler */ if (!(handler_file= get_new_handler(share, thd->mem_root, share->db_type()))) - goto err; + goto free_and_err; record= share->default_values-1; /* Fieldstart = 1 */ if (share->null_field_first) @@ -1152,10 +1171,14 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, for (i=0 ; i < share->fields; i++, strpos+=field_pack_length, field_ptr++) { uint pack_flag, interval_nr, unireg_type, recpos, field_length; + uint vcol_info_length=0; + uint vcol_expr_length=0; enum_field_types field_type; CHARSET_INFO *charset=NULL; Field::geometry_type geom_type= Field::GEOM_GEOMETRY; LEX_STRING comment; + Virtual_column_info *vcol_info= 0; + bool fld_stored_in_db= TRUE; if (new_frm_ver >= 3) { @@ -1176,7 +1199,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, charset= &my_charset_bin; #else error= 4; // unsupported field type - goto err; + goto free_and_err; #endif } else @@ -1187,9 +1210,21 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { error= 5; // Unknown or unavailable charset errarg= (int) strpos[14]; - goto err; + goto free_and_err; } } + + if ((uchar)field_type == (uchar)MYSQL_TYPE_VIRTUAL) + { + DBUG_ASSERT(interval_nr); // Expect non-null expression + /* + The interval_id byte in the .frm file stores the length of the + expression statement for a virtual column. + */ + vcol_info_length= interval_nr; + interval_nr= 0; + } + if (!comment_length) { comment.str= (char*) ""; @@ -1201,6 +1236,34 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, comment.length= comment_length; comment_pos+= comment_length; } + + if (vcol_info_length) + { + /* + Get virtual column data stored in the .frm file as follows: + byte 1 = 1 (always 1 to allow for future extensions) + byte 2 = sql_type + byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored) + byte 4-... = virtual column expression (text data) + */ + vcol_info= new Virtual_column_info(); + if ((uint)vcol_screen_pos[0] != 1) + { + error= 4; + goto free_and_err; + } + field_type= (enum_field_types) (uchar) vcol_screen_pos[1]; + fld_stored_in_db= (bool) (uint) vcol_screen_pos[2]; + vcol_expr_length= vcol_info_length-(uint)FRM_VCOL_HEADER_SIZE; + if (!(vcol_info->expr_str.str= + (char *)memdup_root(&share->mem_root, + vcol_screen_pos+(uint)FRM_VCOL_HEADER_SIZE, + vcol_expr_length))) + goto free_and_err; + vcol_info->expr_str.length= vcol_expr_length; + vcol_screen_pos+= vcol_info_length; + share->vfields++; + } } else { @@ -1286,11 +1349,13 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!reg_field) // Not supported field type { error= 4; - goto err; /* purecov: inspected */ + goto free_and_err; /* purecov: inspected */ } reg_field->field_index= i; reg_field->comment=comment; + reg_field->vcol_info= vcol_info; + reg_field->stored_in_db= fld_stored_in_db; if (field_type == MYSQL_TYPE_BIT && !f_bit_as_char(pack_flag)) { if ((null_bit_pos+= field_length & 7) > 7) @@ -1313,7 +1378,9 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, share->timestamp_field_offset= i; if (use_hash) - if (my_hash_insert(&share->name_hash, (uchar*) field_ptr) ) + { + if (my_hash_insert(&share->name_hash, + (uchar*) field_ptr)) { /* Set return code 8 here to indicate that an error has @@ -1321,10 +1388,20 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, sent (OOM). */ error= 8; - goto err; + goto free_and_err; } + } + if (!reg_field->stored_in_db) + { + share->stored_fields--; + if (share->stored_rec_length>=recpos) + share->stored_rec_length= recpos-1; + } } *field_ptr=0; // End marker + /* Sanity checks: */ + DBUG_ASSERT(share->fields>=share->stored_fields); + DBUG_ASSERT(share->reclength>=share->stored_rec_length); /* Fix key->name and key_part->field */ if (key_parts) @@ -1339,6 +1416,19 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, { uint usable_parts= 0; keyinfo->name=(char*) share->keynames.type_names[key]; + keyinfo->name_length= strlen(keyinfo->name); + keyinfo->cache_name= + (uchar*) alloc_root(&share->mem_root, + share->table_cache_key.length+ + keyinfo->name_length + 1); + if (keyinfo->cache_name) // If not out of memory + { + uchar *pos= keyinfo->cache_name; + memcpy(pos, share->table_cache_key.str, share->table_cache_key.length); + memcpy(pos + share->table_cache_key.length, keyinfo->name, + keyinfo->name_length+1); + } + /* Fix fulltext keys for old .frm files */ if (share->key_info[key].flags & HA_FULLTEXT) share->key_info[key].algorithm= HA_KEY_ALG_FULLTEXT; @@ -1375,7 +1465,7 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, if (!key_part->fieldnr) { error= 4; // Wrong file - goto err; + goto free_and_err; } field= key_part->field= share->field[key_part->fieldnr-1]; key_part->type= field->key_type(); @@ -1540,6 +1630,15 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, null_length, 255); } + if (share->db_create_options & HA_OPTION_TEXT_CREATE_OPTIONS) + { + DBUG_ASSERT(options_len); + if (engine_table_options_frm_read(options, options_len, share) || + parse_engine_table_options(thd, handler_file->ht, share)) + goto free_and_err; + } + my_free(buff, MYF(MY_ALLOW_ZERO_PTR)); + if (share->found_next_number_field) { reg_field= *share->found_next_number_field; @@ -1598,6 +1697,8 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, #endif DBUG_RETURN (0); + free_and_err: + my_free(buff, MYF(MY_ALLOW_ZERO_PTR)); err: share->error= error; share->open_errno= my_errno; @@ -1611,6 +1712,297 @@ static int open_binary_frm(THD *thd, TABLE_SHARE *share, uchar *head, DBUG_RETURN(error); } /* open_binary_frm */ +/* + @brief + Clear GET_FIXED_FIELDS_FLAG in all fields of a table + + @param + table The table for whose fields the flags are to be cleared + + @note + This routine is used for error handling purposes. + + @return + none +*/ + +static void clear_field_flag(TABLE *table) +{ + Field **ptr; + DBUG_ENTER("clear_field_flag"); + + for (ptr= table->field; *ptr; ptr++) + (*ptr)->flags&= (~GET_FIXED_FIELDS_FLAG); + DBUG_VOID_RETURN; +} + + +/* + @brief + Perform semantic analysis of the defining expression for a virtual column + + @param + thd The thread object + @param + table The table containing the virtual column + @param + vcol_field The virtual field whose defining expression is to be analyzed + + @details + The function performs semantic analysis of the defining expression for + the virtual column vcol_field. The expression is used to compute the + values of this column. + + @note + The function exploits the fact that the fix_fields method sets the flag + GET_FIXED_FIELDS_FLAG for all fields in the item tree. + This flag must always be unset before returning from this function + since it is used for other purposes as well. + + @retval + TRUE An error occurred, something was wrong with the function + @retval + FALSE Otherwise +*/ + +bool fix_vcol_expr(THD *thd, + TABLE *table, + Field *vcol_field) +{ + Virtual_column_info *vcol_info= vcol_field->vcol_info; + Item* func_expr= vcol_info->expr_item; + uint dir_length, home_dir_length; + bool result= TRUE; + TABLE_LIST tables; + TABLE_LIST *save_table_list, *save_first_table, *save_last_table; + int error; + Name_resolution_context *context; + const char *save_where; + char* db_name; + char db_name_string[FN_REFLEN]; + bool save_use_only_table_context; + Field **ptr, *field; + enum_mark_columns save_mark_used_columns= thd->mark_used_columns; + DBUG_ASSERT(func_expr); + DBUG_ENTER("fix_vcol_expr"); + + /* + Set-up the TABLE_LIST object to be a list with a single table + Set the object to zero to create NULL pointers and set alias + and real name to table name and get database name from file name. + */ + + bzero((void*)&tables, sizeof(TABLE_LIST)); + tables.alias= tables.table_name= (char*) table->s->table_name.str; + tables.table= table; + tables.next_local= 0; + tables.next_name_resolution_table= 0; + strmov(db_name_string, table->s->normalized_path.str); + dir_length= dirname_length(db_name_string); + db_name_string[dir_length - 1]= 0; + home_dir_length= dirname_length(db_name_string); + db_name= &db_name_string[home_dir_length]; + tables.db= db_name; + + thd->mark_used_columns= MARK_COLUMNS_NONE; + + context= thd->lex->current_context(); + table->map= 1; //To ensure correct calculation of const item + table->get_fields_in_item_tree= TRUE; + save_table_list= context->table_list; + save_first_table= context->first_name_resolution_table; + save_last_table= context->last_name_resolution_table; + context->table_list= &tables; + context->first_name_resolution_table= &tables; + context->last_name_resolution_table= NULL; + func_expr->walk(&Item::change_context_processor, 0, (uchar*) context); + save_where= thd->where; + thd->where= "virtual column function"; + + /* Save the context before fixing the fields*/ + save_use_only_table_context= thd->lex->use_only_table_context; + thd->lex->use_only_table_context= TRUE; + /* Fix fields referenced to by the virtual column function */ + error= func_expr->fix_fields(thd, (Item**)0); + /* Restore the original context*/ + thd->lex->use_only_table_context= save_use_only_table_context; + context->table_list= save_table_list; + context->first_name_resolution_table= save_first_table; + context->last_name_resolution_table= save_last_table; + + if (unlikely(error)) + { + DBUG_PRINT("info", + ("Field in virtual column expression does not belong to the table")); + goto end; + } + thd->where= save_where; +#ifdef PARANOID + /* + Walk through the Item tree checking if all items are valid + to be part of the virtual column + */ + error= func_expr->walk(&Item::check_vcol_func_processor, 0, NULL); + if (error) + { + my_error(ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED, MYF(0), field_name); + goto end; + } +#endif + if (unlikely(func_expr->const_item())) + { + my_error(ER_CONST_EXPR_IN_VCOL, MYF(0)); + goto end; + } + /* Ensure that this virtual column is not based on another virtual field. */ + ptr= table->field; + while ((field= *(ptr++))) + { + if ((field->flags & GET_FIXED_FIELDS_FLAG) && + (field->vcol_info)) + { + my_error(ER_VCOL_BASED_ON_VCOL, MYF(0)); + goto end; + } + } + result= FALSE; + +end: + + /* Clear GET_FIXED_FIELDS_FLAG for the fields of the table */ + clear_field_flag(table); + + table->get_fields_in_item_tree= FALSE; + thd->mark_used_columns= save_mark_used_columns; + table->map= 0; //Restore old value + + DBUG_RETURN(result); +} + +/* + @brief + Unpack the definition of a virtual column from its linear representation + + @parm + thd The thread object + @param + table The table containing the virtual column + @param + field The field for the virtual + @param + vcol_expr The string representation of the defining expression + @param[out] + error_reported The flag to inform the caller that no other error + messages are to be generated + + @details + The function takes string representation 'vcol_expr' of the defining + expression for the virtual field 'field' of the table 'table' and + parses it, building an item object for it. The pointer to this item is + placed into in field->vcol_info.expr_item. After this the function performs + semantic analysis of the item by calling the the function fix_vcol_expr. + Since the defining expression is part of the table definition the item + for it is created in table->memroot within a separate Query_arena. + The free_list of this arena is saved in field->vcol_info.item_free_list + to be freed when the table defition is removed from the TABLE_SHARE cache. + + @note + Before passing 'vcol_expr" to the parser the function embraces it in + parenthesis and prepands it a special keyword. + + @retval + FALSE If a success + @retval + TRUE Otherwise +*/ +bool unpack_vcol_info_from_frm(THD *thd, + TABLE *table, + Field *field, + LEX_STRING *vcol_expr, + bool *error_reported) +{ + bool rc= FALSE; + DBUG_ENTER("unpack_vcol_info_from_frm"); + DBUG_ASSERT(vcol_expr); + + /* + Step 1: Construct the input string for the parser. + The string to be parsed has to be of the following format: + "PARSE_VCOL_EXPR (<expr_string_from_frm>)". + */ + char *vcol_expr_str; + int str_len= 0; + CHARSET_INFO *old_character_set_client; + + if (!(vcol_expr_str= (char*) alloc_root(&table->mem_root, + vcol_expr->length + + parse_vcol_keyword.length + 3))) + { + DBUG_RETURN(TRUE); + } + memcpy(vcol_expr_str, + (char*) parse_vcol_keyword.str, + parse_vcol_keyword.length); + str_len= parse_vcol_keyword.length; + memcpy(vcol_expr_str + str_len, "(", 1); + str_len++; + memcpy(vcol_expr_str + str_len, + (char*) vcol_expr->str, + vcol_expr->length); + str_len+= vcol_expr->length; + memcpy(vcol_expr_str + str_len, ")", 1); + str_len++; + memcpy(vcol_expr_str + str_len, "\0", 1); + str_len++; + Parser_state parser_state(thd, vcol_expr_str, str_len); + + /* + Step 2: Setup thd for parsing. + */ + Query_arena *backup_stmt_arena_ptr= thd->stmt_arena; + Query_arena backup_arena; + Query_arena vcol_arena(&table->mem_root, Query_arena::INITIALIZED); + thd->set_n_backup_active_arena(&vcol_arena, &backup_arena); + thd->stmt_arena= &vcol_arena; + + thd->lex->parse_vcol_expr= TRUE; + old_character_set_client= thd->variables.character_set_client; + + /* + Step 3: Use the parser to build an Item object from vcol_expr_str. + */ + if (parse_sql(thd, &parser_state, NULL)) + { + goto err; + } + /* From now on use vcol_info generated by the parser. */ + field->vcol_info= thd->lex->vcol_info; + + /* Validate the Item tree. */ + if (fix_vcol_expr(thd, table, field)) + { + *error_reported= TRUE; + field->vcol_info= 0; + goto err; + } + field->vcol_info->item_free_list= thd->free_list; + goto end; + +err: + rc= TRUE; + thd->lex->parse_vcol_expr= FALSE; + thd->free_items(); +end: + thd->stmt_arena= backup_stmt_arena_ptr; + thd->restore_active_arena(&vcol_arena, &backup_arena); + thd->variables.character_set_client= old_character_set_client; + + DBUG_RETURN(rc); +} + +/* + Read data from a binary .frm file from MySQL 3.23 - 5.0 into TABLE_SHARE +*/ /* Open a table based on a TABLE_SHARE @@ -1645,7 +2037,7 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, uint records, i, bitmap_size; bool error_reported= FALSE; uchar *record, *bitmaps; - Field **field_ptr; + Field **field_ptr, **vfield_ptr; DBUG_ENTER("open_table_from_share"); DBUG_PRINT("enter",("name: '%s.%s' form: 0x%lx", share->db.str, share->table_name.str, (long) outparam)); @@ -1799,6 +2191,34 @@ int open_table_from_share(THD *thd, TABLE_SHARE *share, const char *alias, } } + /* + Process virtual columns, if any. + */ + if (!(vfield_ptr = (Field **) alloc_root(&outparam->mem_root, + (uint) ((share->vfields+1)* + sizeof(Field*))))) + goto err; + + outparam->vfield= vfield_ptr; + + for (field_ptr= outparam->field; *field_ptr; field_ptr++) + { + if ((*field_ptr)->vcol_info) + { + if (unpack_vcol_info_from_frm(thd, + outparam, + *field_ptr, + &(*field_ptr)->vcol_info->expr_str, + &error_reported)) + { + error= 4; // in case no error is reported + goto err; + } + *(vfield_ptr++)= *field_ptr; + } + } + *vfield_ptr= 0; // End marker + #ifdef WITH_PARTITION_STORAGE_ENGINE if (share->partition_info_len && outparam->file) { @@ -1866,10 +2286,24 @@ partititon_err: } #endif + /* Check virtual columns against table's storage engine. */ + if (share->vfields && + ((outparam->file && + !outparam->file->check_if_supported_virtual_columns()) || + (!outparam->file && share->db_type() && + share->db_type()->db_type == DB_TYPE_CSV_DB))) // Workaround for CSV + { + my_error(ER_UNSUPPORTED_ACTION_ON_VIRTUAL_COLUMN, + MYF(0), + "Specified storage engine"); + error_reported= TRUE; + goto err; + } + /* Allocate bitmaps */ bitmap_size= share->column_bitmap_size; - if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*3))) + if (!(bitmaps= (uchar*) alloc_root(&outparam->mem_root, bitmap_size*4))) goto err; bitmap_init(&outparam->def_read_set, (my_bitmap_map*) bitmaps, share->fields, FALSE); @@ -1877,6 +2311,8 @@ partititon_err: (my_bitmap_map*) (bitmaps+bitmap_size), share->fields, FALSE); bitmap_init(&outparam->tmp_set, (my_bitmap_map*) (bitmaps+bitmap_size*2), share->fields, FALSE); + bitmap_init(&outparam->vcol_set, + (my_bitmap_map*) (bitmaps+bitmap_size*3), share->fields, FALSE); outparam->default_column_bitmaps(); /* The table struct is now initialized; Open the table */ @@ -1984,7 +2420,11 @@ int closefrm(register TABLE *table, bool free_share) if (table->field) { for (Field **ptr=table->field ; *ptr ; ptr++) + { + if ((*ptr)->vcol_info) + free_items((*ptr)->vcol_info->item_free_list); delete *ptr; + } table->field= 0; } delete table->file; @@ -2457,6 +2897,7 @@ File create_frm(THD *thd, const char *name, const char *db, ulong length; uchar fill[IO_SIZE]; int create_flags= O_RDWR | O_TRUNC; + DBUG_ENTER("create_frm"); if (create_info->options & HA_LEX_CREATE_TMP_TABLE) create_flags|= O_EXCL | O_NOFOLLOW; @@ -2538,7 +2979,7 @@ File create_frm(THD *thd, const char *name, const char *db, { VOID(my_close(file,MYF(0))); VOID(my_delete(name,MYF(0))); - return(-1); + DBUG_RETURN(-1); } } } @@ -2549,7 +2990,7 @@ File create_frm(THD *thd, const char *name, const char *db, else my_error(ER_CANT_CREATE_TABLE,MYF(0),table,my_errno); } - return (file); + DBUG_RETURN(file); } /* create_frm */ @@ -2568,6 +3009,7 @@ void update_create_info_from_table(HA_CREATE_INFO *create_info, TABLE *table) create_info->comment= share->comment; create_info->transactional= share->transactional; create_info->page_checksum= share->page_checksum; + create_info->option_list= share->option_list; DBUG_VOID_RETURN; } @@ -3737,11 +4179,8 @@ bool TABLE_LIST::prepare_view_securety_context(THD *thd) { DBUG_PRINT("info", ("This table is suid view => load contest")); DBUG_ASSERT(view && view_sctx); - if (acl_getroot_no_password(view_sctx, - definer.user.str, - definer.host.str, - definer.host.str, - thd->db)) + if (acl_getroot(view_sctx, definer.user.str, definer.host.str, + definer.host.str, thd->db)) { if ((thd->lex->sql_command == SQLCOM_SHOW_CREATE) || (thd->lex->sql_command == SQLCOM_SHOW_FIELDS)) @@ -4338,6 +4777,7 @@ void st_table::clear_column_bitmaps() bitmap_clear_all(&table->def_write_set); */ bzero((char*) def_read_set.bitmap, s->column_bitmap_size*2); + bzero((char*) def_read_set.bitmap, s->column_bitmap_size*4); column_bitmaps_set(&def_read_set, &def_write_set); } @@ -4423,7 +4863,14 @@ void st_table::mark_columns_used_by_index_no_reset(uint index, KEY_PART_INFO *key_part_end= (key_part + key_info[index].key_parts); for (;key_part != key_part_end; key_part++) + { bitmap_set_bit(bitmap, key_part->fieldnr-1); + if (key_part->field->vcol_info && + key_part->field->vcol_info->expr_item) + key_part->field->vcol_info-> + expr_item->walk(&Item::register_field_in_bitmap, + 1, (uchar *) bitmap); + } } @@ -4550,6 +4997,8 @@ void st_table::mark_columns_needed_for_update() file->column_bitmaps_signal(); } } + /* Mark all virtual columns needed for update */ + mark_virtual_columns_for_write(); DBUG_VOID_RETURN; } @@ -4576,9 +5025,100 @@ void st_table::mark_columns_needed_for_insert() } if (found_next_number_field) mark_auto_increment_column(); + /* Mark virtual columns for insert */ + mark_virtual_columns_for_write(); +} + + +/* + @brief Mark a column as virtual used by the query + + @param field the field for the column to be marked + + @details + The function marks the column for 'field' as virtual (computed) + in the bitmap vcol_set. + If the column is marked for the first time the expression to compute + the column is traversed and all columns that are occurred there are + marked in the read_set of the table. + + @retval + TRUE if column is marked for the first time + @retval + FALSE otherwise +*/ + +bool st_table::mark_virtual_col(Field *field) +{ + bool res; + DBUG_ASSERT(field->vcol_info); + if (!(res= bitmap_fast_test_and_set(&vcol_set, field->field_index))) + { + Item *vcol_item= field->vcol_info->expr_item; + DBUG_ASSERT(vcol_item); + vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0); + } + return res; } +/* + @brief Mark virtual columns for update/insert commands + + @details + The function marks virtual columns used in a update/insert commands + in the vcol_set bitmap. + If a virtual column is from write_set it is always marked in vcol_set. + If a stored virtual column is not from write_set but it is computed + through columns from write_set it is also marked in vcol_set, and, + besides, it is added to write_set. + + @return void + + @note + Let table t1 have columns a,b,c and let column c be a stored virtual + column computed through columns a and b. Then for the query + UPDATE t1 SET a=1 + column c will be placed into vcol_set and into write_set while + column b will be placed into read_set. + If column c was a virtual column, but not a stored virtual column + then it would not be added to any of the sets. Column b would not + be added to read_set either. +*/ + +void st_table::mark_virtual_columns_for_write(void) +{ + Field **vfield_ptr, *tmp_vfield; + bool bitmap_updated= FALSE; + + for (vfield_ptr= vfield; *vfield_ptr; vfield_ptr++) + { + tmp_vfield= *vfield_ptr; + if (bitmap_is_set(write_set, tmp_vfield->field_index)) + bitmap_updated= mark_virtual_col(tmp_vfield); + else if (tmp_vfield->stored_in_db) + { + MY_BITMAP *save_read_set; + Item *vcol_item= tmp_vfield->vcol_info->expr_item; + DBUG_ASSERT(vcol_item); + bitmap_clear_all(&tmp_set); + save_read_set= read_set; + read_set= &tmp_set; + vcol_item->walk(&Item::register_field_in_read_map, 1, (uchar *) 0); + read_set= save_read_set; + bitmap_intersect(&tmp_set, write_set); + if (!bitmap_is_clear_all(&tmp_set)) + { + bitmap_set_bit(write_set, tmp_vfield->field_index); + mark_virtual_col(tmp_vfield); + bitmap_updated= TRUE; + } + } + } + if (bitmap_updated) + file->column_bitmaps_signal(); +} + /** @brief Check if this is part of a MERGE table with attached children. @@ -4586,7 +5126,7 @@ void st_table::mark_columns_needed_for_insert() @retval TRUE children are attached @retval FALSE no MERGE part or children not attached - @detail + @details A MERGE table consists of a parent TABLE and zero or more child TABLEs. Each of these TABLEs is called a part of a MERGE table. */ @@ -4847,6 +5387,54 @@ size_t max_row_length(TABLE *table, const uchar *data) return length; } +/* + @brief Compute values for virtual columns used in query + + @param table The TABLE object + @param for_write Requests to compute only fields needed for write + + @details + The function computes the values of the virtual columns of the table and + stores them in the table record buffer. + Only fields from vcol_set are computed, and, when the flag for_write is not + set to TRUE, a virtual field is computed only if it's not stored. + The flag for_write is set to TRUE for row insert/update operations. + + @retval + 0 Success + @retval + >0 Error occurred when storing a virtual field value +*/ + +int update_virtual_fields(TABLE *table, bool for_write) +{ + DBUG_ENTER("update_virtual_fields"); + Field **vfield_ptr, *vfield; + int error= 0; + if (!table || !table->vfield) + DBUG_RETURN(0); + + /* Iterate over virtual fields in the table */ + for (vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++) + { + vfield= (*vfield_ptr); + DBUG_ASSERT(vfield->vcol_info && vfield->vcol_info->expr_item); + /* Only update those fields that are marked in the vcol_set bitmap */ + if (bitmap_is_set(&table->vcol_set, vfield->field_index) && + (for_write || !vfield->stored_in_db)) + { + /* Compute the actual value of the virtual fields */ + error= vfield->vcol_info->expr_item->save_in_field(vfield, 0); + DBUG_PRINT("info", ("field '%s' - updated", vfield->field_name)); + } + else + { + DBUG_PRINT("info", ("field '%s' - skipped", vfield->field_name)); + } + } + DBUG_RETURN(0); +} + /***************************************************************************** ** Instansiate templates *****************************************************************************/ diff --git a/sql/table.h b/sql/table.h index a24e79e26cf..57932701b93 100644 --- a/sql/table.h +++ b/sql/table.h @@ -340,6 +340,8 @@ typedef struct st_table_share #ifdef NOT_YET struct st_table *open_tables; /* link to open tables */ #endif + engine_option_value *option_list; /* text options for table */ + void *option_struct; /* structure with parsed options */ /* The following is copied to each TABLE on OPEN */ Field **field; @@ -382,6 +384,8 @@ typedef struct st_table_share ulong version, mysql_version; ulong timestamp_offset; /* Set to offset+1 of record */ ulong reclength; /* Recordlength */ + /* Stored record length. No generated-only virtual fields are included */ + ulong stored_rec_length; plugin_ref db_plugin; /* storage engine plugin */ inline handlerton *db_type() const /* table_type for handler */ @@ -402,6 +406,8 @@ typedef struct st_table_share uint key_block_size; /* create key_block_size, if used */ uint null_bytes, last_null_bit_pos; uint fields; /* Number of fields */ + /* Number of stored fields, generated-only virtual fields are not included */ + uint stored_fields; uint rec_buff_length; /* Size of table->record[] buffer */ uint keys, key_parts; uint max_key_length, max_unique_length, total_key_length; @@ -423,6 +429,7 @@ typedef struct st_table_share uint error, open_errno, errarg; /* error from open_table_def() */ uint column_bitmap_size; uchar frm_version; + uint vfields; /* Number of computed (virtual) fields */ bool null_field_first; bool system; /* Set if system table (one record) */ bool crypted; /* If .frm file is crypted */ @@ -700,6 +707,7 @@ struct st_table { Field *next_number_field; /* Set if next_number is activated */ Field *found_next_number_field; /* Set on open */ Field_timestamp *timestamp_field; + Field **vfield; /* Pointer to virtual fields*/ /* Table's triggers, 0 if there are no of them */ Table_triggers_list *triggers; @@ -709,6 +717,7 @@ struct st_table { uchar *null_flags; my_bitmap_map *bitmap_init_value; MY_BITMAP def_read_set, def_write_set, tmp_set; /* containers */ + MY_BITMAP vcol_set; /* set of used virtual columns */ MY_BITMAP *read_set, *write_set; /* Active column sets */ /* The ID of the query that opened and is using this table. Has different @@ -874,6 +883,8 @@ struct st_table { void mark_columns_needed_for_update(void); void mark_columns_needed_for_delete(void); void mark_columns_needed_for_insert(void); + bool mark_virtual_col(Field *field); + void mark_virtual_columns_for_write(void); inline void column_bitmaps_set(MY_BITMAP *read_set_arg, MY_BITMAP *write_set_arg) { @@ -933,6 +944,7 @@ typedef struct st_foreign_key_info enum enum_schema_tables { SCH_CHARSETS= 0, + SCH_CLIENT_STATS, SCH_COLLATIONS, SCH_COLLATION_CHARACTER_SET_APPLICABILITY, SCH_COLUMNS, @@ -942,6 +954,8 @@ enum enum_schema_tables SCH_FILES, SCH_GLOBAL_STATUS, SCH_GLOBAL_VARIABLES, + SCH_INDEX_STATS, + SCH_KEY_CACHES, SCH_KEY_COLUMN_USAGE, SCH_OPEN_TABLES, SCH_PARTITIONS, @@ -960,8 +974,10 @@ enum enum_schema_tables SCH_TABLE_CONSTRAINTS, SCH_TABLE_NAMES, SCH_TABLE_PRIVILEGES, + SCH_TABLE_STATS, SCH_TRIGGERS, SCH_USER_PRIVILEGES, + SCH_USER_STATS, SCH_VARIABLES, SCH_VIEWS }; diff --git a/sql/thr_malloc.cc b/sql/thr_malloc.cc index 0764fe8be33..83c4a8ee2a0 100644 --- a/sql/thr_malloc.cc +++ b/sql/thr_malloc.cc @@ -59,11 +59,13 @@ void init_sql_alloc(MEM_ROOT *mem_root, uint block_size, uint pre_alloc) } +#ifndef MYSQL_CLIENT void *sql_alloc(size_t Size) { MEM_ROOT *root= *my_pthread_getspecific_ptr(MEM_ROOT**,THR_MALLOC); return alloc_root(root,Size); } +#endif void *sql_calloc(size_t size) diff --git a/sql/tztime.cc b/sql/tztime.cc index cd6e63be039..3802c284057 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -1676,7 +1676,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) tz_leapcnt= 0; - res= table->file->index_first(table->record[0]); + res= table->file->ha_index_first(table->record[0]); while (!res) { @@ -1698,7 +1698,7 @@ my_tz_init(THD *org_thd, const char *default_tzname, my_bool bootstrap) tz_leapcnt, (ulong) tz_lsis[tz_leapcnt-1].ls_trans, tz_lsis[tz_leapcnt-1].ls_corr)); - res= table->file->index_next(table->record[0]); + res= table->file->ha_index_next(table->record[0]); } (void)table->file->ha_index_end(); @@ -1865,8 +1865,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) */ (void)table->file->ha_index_init(0, 1); - if (table->file->index_read_map(table->record[0], table->field[0]->ptr, - HA_WHOLE_KEY, HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr, + HA_WHOLE_KEY, HA_READ_KEY_EXACT)) { #ifdef EXTRA_DEBUG /* @@ -1893,8 +1893,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); - if (table->file->index_read_map(table->record[0], table->field[0]->ptr, - HA_WHOLE_KEY, HA_READ_KEY_EXACT)) + if (table->file->ha_index_read_map(table->record[0], table->field[0]->ptr, + HA_WHOLE_KEY, HA_READ_KEY_EXACT)) { sql_print_error("Can't find description of time zone '%u'", tzid); goto end; @@ -1920,8 +1920,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); - res= table->file->index_read_map(table->record[0], table->field[0]->ptr, - (key_part_map)1, HA_READ_KEY_EXACT); + res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT); while (!res) { ttid= (uint)table->field[1]->val_int(); @@ -1968,8 +1968,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) tmp_tz_info.typecnt= ttid + 1; - res= table->file->index_next_same(table->record[0], - table->field[0]->ptr, 4); + res= table->file->ha_index_next_same(table->record[0], + table->field[0]->ptr, 4); } if (res != HA_ERR_END_OF_FILE) @@ -1991,8 +1991,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) table->field[0]->store((longlong) tzid, TRUE); (void)table->file->ha_index_init(0, 1); - res= table->file->index_read_map(table->record[0], table->field[0]->ptr, - (key_part_map)1, HA_READ_KEY_EXACT); + res= table->file->ha_index_read_map(table->record[0], table->field[0]->ptr, + (key_part_map)1, HA_READ_KEY_EXACT); while (!res) { ttime= (my_time_t)table->field[1]->val_int(); @@ -2021,8 +2021,8 @@ tz_load_from_open_tables(const String *tz_name, TABLE_LIST *tz_tables) ("time_zone_transition table: tz_id: %u tt_time: %lu tt_id: %u", tzid, (ulong) ttime, ttid)); - res= table->file->index_next_same(table->record[0], - table->field[0]->ptr, 4); + res= table->file->ha_index_next_same(table->record[0], + table->field[0]->ptr, 4); } /* diff --git a/sql/unireg.cc b/sql/unireg.cc index 7087ec93804..3a153277337 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -24,11 +24,15 @@ */ #include "mysql_priv.h" +#include "create_options.h" #include <m_ctype.h> #include <assert.h> #define FCOMP 17 /* Bytes for a packed field */ +/* threshold for safe_alloca */ +#define ALLOCA_THRESHOLD 2048 + static uchar * pack_screens(List<Create_field> &create_fields, uint *info_length, uint *screens, bool small_file); static uint pack_keys(uchar *keybuff,uint key_count, KEY *key_info, @@ -107,6 +111,7 @@ bool mysql_create_frm(THD *thd, const char *file_name, ulong key_buff_length; File file; ulong filepos, data_offset; + uint options_len; uchar fileinfo[64],forminfo[288],*keybuff; TYPELIB formnames; uchar *screen_buff; @@ -183,6 +188,16 @@ bool mysql_create_frm(THD *thd, const char *file_name, create_info->extra_size+= key_info[i].parser_name->length + 1; } + options_len= engine_table_options_frm_length(create_info->option_list, + create_fields, + keys, key_info); + DBUG_PRINT("info", ("Options length: %u", options_len)); + if (options_len) + { + create_info->table_options|= HA_OPTION_TEXT_CREATE_OPTIONS; + create_info->extra_size+= (options_len + 4); + } + if ((file=create_frm(thd, file_name, db, table, reclength, fileinfo, create_info, keys)) < 0) { @@ -294,6 +309,7 @@ bool mysql_create_frm(THD *thd, const char *file_name, if (my_write(file, (uchar*) buff, 6, MYF_RW)) goto err; } + for (i= 0; i < keys; i++) { if (key_info[i].parser_name) @@ -304,6 +320,24 @@ bool mysql_create_frm(THD *thd, const char *file_name, } } + if (options_len) + { + uchar *optbuff= (uchar *)my_safe_alloca(options_len + 4, ALLOCA_THRESHOLD); + my_bool error; + DBUG_PRINT("info", ("Create options length: %u", options_len)); + if (!optbuff) + goto err; + int4store(optbuff, options_len); + engine_table_options_frm_image(optbuff + 4, + create_info->option_list, + create_fields, + keys, key_info); + error= my_write(file, optbuff, options_len + 4, MYF_RW); + my_safe_afree(optbuff, options_len + 4, ALLOCA_THRESHOLD); + if (error) + goto err; + } + VOID(my_seek(file,filepos,MY_SEEK_SET,MYF(0))); if (my_write(file, forminfo, 288, MYF_RW) || my_write(file, screen_buff, info_length, MYF_RW) || @@ -583,7 +617,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, { uint length,int_count,int_length,no_empty, int_parts; uint time_stamp_pos,null_fields; - ulong reclength, totlength, n_length, com_length; + ulong reclength, totlength, n_length, com_length, vcol_info_length; DBUG_ENTER("pack_header"); if (create_fields.elements > MAX_FIELDS) @@ -594,8 +628,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, totlength= 0L; reclength= data_offset; - no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields= - com_length=0; + no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=0; + com_length=vcol_info_length=0; n_length=2L; /* Check fields */ @@ -623,6 +657,30 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, field->field_name, tmp_len); field->comment.length= tmp_len; } + if (field->vcol_info) + { + tmp_len= + system_charset_info->cset->charpos(system_charset_info, + field->vcol_info->expr_str.str, + field->vcol_info->expr_str.str + + field->vcol_info->expr_str.length, + VIRTUAL_COLUMN_EXPRESSION_MAXLEN); + + if (tmp_len < field->vcol_info->expr_str.length) + { + my_error(ER_WRONG_STRING_LENGTH, MYF(0), + field->vcol_info->expr_str.str,"VIRTUAL COLUMN EXPRESSION", + (uint) VIRTUAL_COLUMN_EXPRESSION_MAXLEN); + DBUG_RETURN(1); + } + /* + Sum up the length of the expression string and the length of the + mandatory header to the total length of info on the defining + expressions saved in the frm file for virtual columns. + */ + vcol_info_length+= field->vcol_info->expr_str.length+ + (uint)FRM_VCOL_HEADER_SIZE; + } totlength+= field->length; com_length+= field->comment.length; @@ -642,8 +700,6 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, !time_stamp_pos) time_stamp_pos= (uint) field->offset+ (uint) data_offset + 1; length=field->pack_length; - /* Ensure we don't have any bugs when generating offsets */ - DBUG_ASSERT(reclength == field->offset + data_offset); if ((uint) field->offset+ (uint) data_offset+ length > reclength) reclength=(uint) (field->offset+ data_offset + length); n_length+= (ulong) strlen(field->field_name)+1; @@ -710,7 +766,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, /* Hack to avoid bugs with small static rows in MySQL */ reclength=max(file->min_record_length(table_options),reclength); if (info_length+(ulong) create_fields.elements*FCOMP+288+ - n_length+int_length+com_length > 65535L || int_count > 255) + n_length+int_length+com_length+vcol_info_length > 65535L || + int_count > 255) { my_message(ER_TOO_MANY_FIELDS, ER(ER_TOO_MANY_FIELDS), MYF(0)); DBUG_RETURN(1); @@ -718,7 +775,7 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, bzero((char*)forminfo,288); length=(info_length+create_fields.elements*FCOMP+288+n_length+int_length+ - com_length); + com_length+vcol_info_length); int2store(forminfo,length); forminfo[256] = (uint8) screens; int2store(forminfo+258,create_fields.elements); @@ -735,7 +792,8 @@ static bool pack_header(uchar *forminfo, enum legacy_db_type table_type, int2store(forminfo+280,22); /* Rows needed */ int2store(forminfo+282,null_fields); int2store(forminfo+284,com_length); - /* Up to forminfo+288 is free to use for additional information */ + int2store(forminfo+286,vcol_info_length); + /* forminfo+288 is free to use for additional information */ DBUG_RETURN(0); } /* pack_header */ @@ -774,7 +832,7 @@ static bool pack_fields(File file, List<Create_field> &create_fields, ulong data_offset) { reg2 uint i; - uint int_count, comment_length=0; + uint int_count, comment_length= 0, vcol_info_length=0; uchar buff[MAX_FIELD_WIDTH]; Create_field *field; DBUG_ENTER("pack_fields"); @@ -787,6 +845,7 @@ static bool pack_fields(File file, List<Create_field> &create_fields, while ((field=it++)) { uint recpos; + uint cur_vcol_expr_len= 0; buff[0]= (uchar) field->row; buff[1]= (uchar) field->col; buff[2]= (uchar) field->sc_length; @@ -809,6 +868,17 @@ static bool pack_fields(File file, List<Create_field> &create_fields, buff[14]= (uchar) field->charset->number; else buff[14]= 0; // Numerical + if (field->vcol_info) + { + /* + Use the interval_id place in the .frm file to store the length of + the additional data saved for the virtual field + */ + buff[12]= cur_vcol_expr_len= field->vcol_info->expr_str.length + + (uint)FRM_VCOL_HEADER_SIZE; + vcol_info_length+= cur_vcol_expr_len+(uint)FRM_VCOL_HEADER_SIZE; + buff[13]= (uchar) MYSQL_TYPE_VIRTUAL; + } int2store(buff+15, field->comment.length); comment_length+= field->comment.length; set_if_bigger(int_count,field->interval_id); @@ -903,6 +973,34 @@ static bool pack_fields(File file, List<Create_field> &create_fields, DBUG_RETURN(1); } } + if (vcol_info_length) + { + it.rewind(); + int_count=0; + while ((field=it++)) + { + /* + Pack each virtual field as follows: + byte 1 = 1 (always 1 to allow for future extensions) + byte 2 = sql_type + byte 3 = flags (as of now, 0 - no flags, 1 - field is physically stored) + byte 4-... = virtual column expression (text data) + */ + if (field->vcol_info && field->vcol_info->expr_str.length) + { + buff[0]= (uchar)1; + buff[1]= (uchar) field->sql_type; + buff[2]= (uchar) field->stored_in_db; + if (my_write(file, buff, 3, MYF_RW)) + DBUG_RETURN(1); + if (my_write(file, + (uchar*) field->vcol_info->expr_str.str, + field->vcol_info->expr_str.length, + MYF_RW)) + DBUG_RETURN(1); + } + } + } DBUG_RETURN(0); } diff --git a/sql/unireg.h b/sql/unireg.h index 4fbde3b8631..ec4ecf6a10f 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -212,6 +212,13 @@ #define DEFAULT_KEY_CACHE_NAME "default" +/* The length of the header part for each virtual column in the .frm file */ +#define FRM_VCOL_HEADER_SIZE 3 + +/* Maximum length of the defining expression for a virtual columns */ +#define VIRTUAL_COLUMN_EXPRESSION_MAXLEN 255 - FRM_VCOL_HEADER_SIZE + + /* Include prototypes for unireg */ #include "mysqld_error.h" |