From b705b71ee692e7c0765321c3d5205d56fb426c0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Magnus=20Bl=C3=A5udd?= Date: Mon, 4 Jan 2010 12:17:58 +0100 Subject: [PATCH 001/711] Bug#47526 unit.pl fails to execute binaries when using Test::Harness 3.10 - Rewrite unit.pl so it prefers to use TAP::Harness directly. - Also add support for --verbose --noverbose --- unittest/unit.pl | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/unittest/unit.pl b/unittest/unit.pl index 9d328985012..c1c64b3d538 100644 --- a/unittest/unit.pl +++ b/unittest/unit.pl @@ -14,8 +14,8 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -use Test::Harness qw(&runtests $verbose); use File::Find; +use Getopt::Long; use strict; @@ -35,6 +35,11 @@ unit - Run unit tests in directory =cut +my $opt_verbose; +GetOptions ( + "verbose!" => \$opt_verbose, +) or die "Failed to parse options!: $!"; + my $cmd = shift; if (defined $cmd && exists $dispatch{$cmd}) { @@ -51,6 +56,21 @@ Run all unit tests in the current directory and all subdirectories. =cut +BEGIN { + # Test::Harness have been extensively rewritten in newer perl + # versions and is now just a backward compatibility wrapper + # (with a bug causing the HARNESS_PERL_SWITCHES to be mangled) + # Prefer to use TAP::Harness directly if available + if (eval "use TAP::Harness; 1") { + eval 'sub NEW_HARNESS { 1 }'; + warn "using TAP::Harness"; + } else { + eval "use Test::Harness; 1" or die "couldn't find Test::Harness!"; + eval 'sub NEW_HARNESS { 0 }'; + } +} + + sub _find_test_files (@) { my @dirs = @_; my @files; @@ -92,8 +112,19 @@ sub run_cmd (@) { if (@files > 0) { # Removing the first './' from the file names foreach (@files) { s!^\./!! } - $ENV{'HARNESS_PERL_SWITCHES'} .= q" -e 'exec @ARGV'"; - runtests @files; + + if (NEW_HARNESS()) + { + my %args = ( exec => [ ], verbosity => $opt_verbose ); + my $harness = TAP::Harness->new( \%args ); + $harness->runtests(@files); + } + else + { + $ENV{'HARNESS_VERBOSE'} = $opt_verbose; + $ENV{'HARNESS_PERL_SWITCHES'} .= ' -e "exec @ARGV"'; + runtests(@files); + } } } From a394519ef96008079049b8a1a3136600b5073ddc Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Tue, 3 Aug 2010 14:52:20 +0300 Subject: [PATCH 002/711] addendum to the fix for bug #54476: fixed a failing test case. --- mysql-test/t/func_gconcat.test | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mysql-test/t/func_gconcat.test b/mysql-test/t/func_gconcat.test index 693fa7d1c89..54c7291bb5e 100644 --- a/mysql-test/t/func_gconcat.test +++ b/mysql-test/t/func_gconcat.test @@ -734,6 +734,9 @@ EXECUTE stmt; DEALLOCATE PREPARE stmt; DROP TABLE t1; +--echo End of 5.1 tests + + # # Bug#36785: Wrong error message when group_concat() exceeds max length # From 9a05541bf1034fc131dc51585cebf1a2e141289a Mon Sep 17 00:00:00 2001 From: "karen.langford@oracle.com" <> Date: Tue, 3 Aug 2010 18:57:39 +0200 Subject: [PATCH 003/711] Raise version number after cloning 5.1.50 --- configure.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.in b/configure.in index efd291f953c..3ec62b836f1 100644 --- a/configure.in +++ b/configure.in @@ -12,7 +12,7 @@ dnl dnl When changing the major version number please also check the switch dnl statement in mysqlbinlog::check_master_version(). You may also need dnl to update version.c in ndb. -AC_INIT([MySQL Server], [5.1.50], [], [mysql]) +AC_INIT([MySQL Server], [5.1.51], [], [mysql]) AC_CONFIG_SRCDIR([sql/mysqld.cc]) AC_CANONICAL_SYSTEM From d8f8b1cb0b0ca08c8ffbf95cb21fc508b95b5a84 Mon Sep 17 00:00:00 2001 From: Alexander Nozdrin Date: Wed, 4 Aug 2010 12:52:38 +0400 Subject: [PATCH 004/711] Fix default.conf. --- .bzr-mysql/default.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.bzr-mysql/default.conf b/.bzr-mysql/default.conf index 41a3d479084..1b6d7676984 100644 --- a/.bzr-mysql/default.conf +++ b/.bzr-mysql/default.conf @@ -1,4 +1,4 @@ [MYSQL] post_commit_to = "dbg_mysql_security@sun.com" post_push_to = "dbg_mysql_security@sun.com" -tree_name = "mysql-trunk-security" +tree_name = "mysql-5.5-security" From 6fce5c4c771dba0845429cf967df80d49482c37c Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Wed, 4 Aug 2010 03:11:33 -0700 Subject: [PATCH 005/711] Fix Bug #54582 stack overflow when opening many tables linked with foreign keys at once rb://391 approved by Heikki Z --- storage/innobase/dict/dict0load.c | 81 ++++++++++++++++++++-- storage/innobase/handler/ha_innodb.cc | 10 +++ storage/innobase/include/db0err.h | 3 + storage/innobase/include/dict0load.h | 2 + storage/innobase/include/dict0mem.h | 21 ++++++ storage/innobase/include/que0que.h | 3 + storage/innobase/row/row0mysql.c | 21 +++++- storage/innodb_plugin/ChangeLog | 8 +++ storage/innodb_plugin/dict/dict0load.c | 81 ++++++++++++++++++++-- storage/innodb_plugin/handler/ha_innodb.cc | 13 ++++ storage/innodb_plugin/include/db0err.h | 3 + storage/innodb_plugin/include/dict0load.h | 2 + storage/innodb_plugin/include/dict0mem.h | 21 ++++++ storage/innodb_plugin/include/que0que.h | 3 + storage/innodb_plugin/row/row0merge.c | 2 +- storage/innodb_plugin/row/row0mysql.c | 21 +++++- 16 files changed, 276 insertions(+), 19 deletions(-) diff --git a/storage/innobase/dict/dict0load.c b/storage/innobase/dict/dict0load.c index d5e7600f4d0..625956600c0 100644 --- a/storage/innobase/dict/dict0load.c +++ b/storage/innobase/dict/dict0load.c @@ -864,16 +864,27 @@ err_exit: err = dict_load_indexes(table, heap); + /* Initialize table foreign_child value. Its value could be + changed when dict_load_foreigns() is called below */ + table->fk_max_recusive_level = 0; + /* If the force recovery flag is set, we open the table irrespective of the error condition, since the user may want to dump data from the clustered index. However we load the foreign key information only if all indexes were loaded. */ if (err == DB_SUCCESS) { - err = dict_load_foreigns(table->name, TRUE); + err = dict_load_foreigns(table->name, TRUE, TRUE); + + if (err != DB_SUCCESS) { + dict_table_remove_from_cache(table); + table = NULL; + } } else if (!srv_force_recovery) { dict_table_remove_from_cache(table); table = NULL; } + + table->fk_max_recusive_level = 0; #if 0 if (err != DB_SUCCESS && table != NULL) { @@ -1095,8 +1106,12 @@ dict_load_foreign( /* out: DB_SUCCESS or error code */ const char* id, /* in: foreign constraint id as a null-terminated string */ - ibool check_charsets) + ibool check_charsets, /* in: TRUE=check charset compatibility */ + ibool check_recursive) + /* in: Whether to record the foreign table + parent count to avoid unlimited recursive + load of chained foreign tables */ { dict_foreign_t* foreign; dict_table_t* sys_foreign; @@ -1110,6 +1125,8 @@ dict_load_foreign( ulint len; ulint n_fields_and_type; mtr_t mtr; + dict_table_t* for_table; + dict_table_t* ref_table; ut_ad(mutex_own(&(dict_sys->mutex))); @@ -1194,11 +1211,54 @@ dict_load_foreign( dict_load_foreign_cols(id, foreign); - /* If the foreign table is not yet in the dictionary cache, we - have to load it so that we are able to make type comparisons - in the next function call. */ + ref_table = dict_table_check_if_in_cache_low( + foreign->referenced_table_name); - dict_table_get_low(foreign->foreign_table_name); + /* We could possibly wind up in a deep recursive calls if + we call dict_table_get_low() again here if there + is a chain of tables concatenated together with + foreign constraints. In such case, each table is + both a parent and child of the other tables, and + act as a "link" in such table chains. + To avoid such scenario, we would need to check the + number of ancesters the current table has. If that + exceeds DICT_FK_MAX_CHAIN_LEN, we will stop loading + the child table. + Foreign constraints are loaded in a Breath First fashion, + that is, the index on FOR_NAME is scanned first, and then + index on REF_NAME. So foreign constrains in which + current table is a child (foreign table) are loaded first, + and then those constraints where current table is a + parent (referenced) table. + Thus we could check the parent (ref_table) table's + reference count (fk_max_recusive_level) to know how deep the + recursive call is. If the parent table (ref_table) is already + loaded, and its fk_max_recusive_level is larger than + DICT_FK_MAX_CHAIN_LEN, we will stop the recursive loading + by skipping loading the child table. It will not affect foreign + constraint check for DMLs since child table will be loaded + at that time for the constraint check. */ + if (!ref_table + || ref_table->fk_max_recusive_level < DICT_FK_MAX_RECURSIVE_LOAD) { + + /* If the foreign table is not yet in the dictionary cache, we + have to load it so that we are able to make type comparisons + in the next function call. */ + + for_table = dict_table_get_low(foreign->foreign_table_name); + + if (for_table && ref_table && check_recursive) { + /* This is to record the longest chain of ancesters + this table has, if the parent has more ancesters + than this table has, record it after add 1 (for this + parent */ + if (ref_table->fk_max_recusive_level + >= for_table->fk_max_recusive_level) { + for_table->fk_max_recusive_level = + ref_table->fk_max_recusive_level + 1; + } + } + } /* Note that there may already be a foreign constraint object in the dictionary cache for this constraint: then the following @@ -1223,6 +1283,8 @@ dict_load_foreigns( /*===============*/ /* out: DB_SUCCESS or error code */ const char* table_name, /* in: table name */ + ibool check_recursive,/* in: Whether to check recursive + load of tables chained by FK */ ibool check_charsets) /* in: TRUE=check charset compatibility */ { @@ -1324,7 +1386,7 @@ loop: /* Load the foreign constraint definition to the dictionary cache */ - err = dict_load_foreign(id, check_charsets); + err = dict_load_foreign(id, check_charsets, check_recursive); if (err != DB_SUCCESS) { btr_pcur_close(&pcur); @@ -1352,6 +1414,11 @@ load_next_index: mtr_start(&mtr); + /* Switch to scan index on REF_NAME, fk_max_recusive_level + already been updated when scanning FOR_NAME index, no need to + update again */ + check_recursive = FALSE; + goto start_load; } diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index d10fcb8d31e..930785d7dcd 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -763,6 +763,16 @@ convert_error_code_to_mysql( my_error(ER_QUERY_INTERRUPTED, MYF(0)); return(-1); + } else if (error == DB_FOREIGN_EXCEED_MAX_CASCADE) { + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + HA_ERR_ROW_IS_REFERENCED, + "InnoDB: Cannot delete/update " + "rows with cascading foreign key " + "constraints that exceed max " + "depth of %d. Please " + "drop extra constraints and try " + "again", DICT_FK_MAX_RECURSIVE_LOAD); + return(-1); } else { return(-1); // Unknown error } diff --git a/storage/innobase/include/db0err.h b/storage/innobase/include/db0err.h index 2be6005622d..af3e78fe833 100644 --- a/storage/innobase/include/db0err.h +++ b/storage/innobase/include/db0err.h @@ -73,6 +73,9 @@ Created 5/24/1996 Heikki Tuuri a later version of the engine. */ #define DB_INTERRUPTED 49 /* the query has been interrupted with "KILL QUERY N;" */ +#define DB_FOREIGN_EXCEED_MAX_CASCADE 50/* Foreign key constraint related + cascading delete/update exceeds + maximum allowed depth */ /* The following are partial failure codes */ #define DB_FAIL 1000 diff --git a/storage/innobase/include/dict0load.h b/storage/innobase/include/dict0load.h index 7e19c2eb3c0..eb6083e06f9 100644 --- a/storage/innobase/include/dict0load.h +++ b/storage/innobase/include/dict0load.h @@ -82,6 +82,8 @@ dict_load_foreigns( /*===============*/ /* out: DB_SUCCESS or error code */ const char* table_name, /* in: table name */ + ibool check_recursive,/* in: Whether to check recursive + load of tables chained by FK */ ibool check_charsets);/* in: TRUE=check charsets compatibility */ /************************************************************************ diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index ac28fdb1bae..2f2a7441478 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -283,6 +283,21 @@ a foreign key constraint is enforced, therefore RESTRICT just means no flag */ #define DICT_FOREIGN_ON_DELETE_NO_ACTION 16 #define DICT_FOREIGN_ON_UPDATE_NO_ACTION 32 +/** Tables could be chained together with Foreign key constraint. When +first load the parent table, we would load all of its descedents. +This could result in rescursive calls and out of stack error eventually. +DICT_FK_MAX_RECURSIVE_LOAD defines the maximum number of recursive loads, +when exceeded, the child table will not be loaded. It will be loaded when +the foreign constraint check needs to be run. */ +#define DICT_FK_MAX_RECURSIVE_LOAD 250 + +/** Similarly, when tables are chained together with foreign key constraints +with on cascading delete/update clause, delete from parent table could +result in recursive cascading calls. This defines the maximum number of +such cascading deletes/updates allowed. When exceeded, the delete from +parent table will fail, and user has to drop excessive foreign constraint +before proceeds. */ +#define FK_MAX_CASCADE_DEL 300 /* Data structure for a database table */ struct dict_table_struct{ @@ -339,6 +354,12 @@ struct dict_table_struct{ NOT allowed until this count gets to zero; MySQL does NOT itself check the number of open handles at drop */ + unsigned fk_max_recusive_level:8; + /*!< maximum recursive level we support when + loading tables chained together with FK + constraints. If exceeds this level, we will + stop loading child table into memory along with + its parent table */ ulint n_foreign_key_checks_running; /* count of how many foreign key check operations are currently being performed diff --git a/storage/innobase/include/que0que.h b/storage/innobase/include/que0que.h index 8fbf5330c89..71f4cfdfb8f 100644 --- a/storage/innobase/include/que0que.h +++ b/storage/innobase/include/que0que.h @@ -367,6 +367,9 @@ struct que_thr_struct{ thus far */ ulint lock_state; /* lock state of thread (table or row) */ + ulint fk_cascade_depth; /*!< maximum cascading call depth + supported for foreign key constraint + related delete/updates */ }; #define QUE_THR_MAGIC_N 8476583 diff --git a/storage/innobase/row/row0mysql.c b/storage/innobase/row/row0mysql.c index 4a834c4efc2..3b76ffa76f1 100644 --- a/storage/innobase/row/row0mysql.c +++ b/storage/innobase/row/row0mysql.c @@ -555,6 +555,12 @@ handle_new_error: "forcing-recovery.html" " for help.\n", stderr); + } else if (err == DB_FOREIGN_EXCEED_MAX_CASCADE) { + fprintf(stderr, "InnoDB: Cannot delete/update rows with" + " cascading foreign key constraints that exceed max" + " depth of %lu\n" + "Please drop excessive foreign constraints" + " and try again\n", (ulong) DICT_FK_MAX_RECURSIVE_LOAD); } else { fprintf(stderr, "InnoDB: unknown error code %lu\n", (ulong) err); @@ -1406,11 +1412,15 @@ row_update_for_mysql( run_again: thr->run_node = node; thr->prev_node = node; + thr->fk_cascade_depth = 0; row_upd_step(thr); err = trx->error_state; + /* Reset fk_cascade_depth back to 0 */ + thr->fk_cascade_depth = 0; + if (err != DB_SUCCESS) { que_thr_stop_for_mysql(thr); @@ -1597,6 +1607,12 @@ row_update_cascade_for_mysql( trx_t* trx; trx = thr_get_trx(thr); + + thr->fk_cascade_depth++; + + if (thr->fk_cascade_depth > FK_MAX_CASCADE_DEL) { + return (DB_FOREIGN_EXCEED_MAX_CASCADE); + } run_again: thr->run_node = node; thr->prev_node = node; @@ -2129,7 +2145,7 @@ row_table_add_foreign_constraints( if (err == DB_SUCCESS) { /* Check that also referencing constraints are ok */ - err = dict_load_foreigns(name, TRUE); + err = dict_load_foreigns(name, FALSE, TRUE); } if (err != DB_SUCCESS) { @@ -3878,7 +3894,8 @@ end: an ALTER, not in a RENAME. */ err = dict_load_foreigns( - new_name, old_is_tmp ? trx->check_foreigns : TRUE); + new_name, FALSE, + old_is_tmp ? trx->check_foreigns : TRUE); if (err != DB_SUCCESS) { ut_print_timestamp(stderr); diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 3e802360d23..56729852a62 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,11 @@ +2010-08-03 The InnoDB Team + + * dict/dict0load.c, handler/ha_innodb.cc, include/db0err.h, + include/dict0load.h, include/dict0mem.h, include/que0que.h, + row/row0merge.c, row/row0mysql.c: + Fix Bug#54582 stack overflow when opening many tables linked + with foreign keys at once + 2010-07-27 The InnoDB Team * include/mem0pool.h, mem/mem0mem.c, mem/mem0pool.c, srv/srv0start.c: diff --git a/storage/innodb_plugin/dict/dict0load.c b/storage/innodb_plugin/dict/dict0load.c index 3c495d21786..3acc267308f 100644 --- a/storage/innodb_plugin/dict/dict0load.c +++ b/storage/innodb_plugin/dict/dict0load.c @@ -1009,16 +1009,27 @@ err_exit: err = dict_load_indexes(table, heap); + /* Initialize table foreign_child value. Its value could be + changed when dict_load_foreigns() is called below */ + table->fk_max_recusive_level = 0; + /* If the force recovery flag is set, we open the table irrespective of the error condition, since the user may want to dump data from the clustered index. However we load the foreign key information only if all indexes were loaded. */ if (err == DB_SUCCESS) { - err = dict_load_foreigns(table->name, TRUE); + err = dict_load_foreigns(table->name, TRUE, TRUE); + + if (err != DB_SUCCESS) { + dict_table_remove_from_cache(table); + table = NULL; + } } else if (!srv_force_recovery) { dict_table_remove_from_cache(table); table = NULL; } + + table->fk_max_recusive_level = 0; #if 0 if (err != DB_SUCCESS && table != NULL) { @@ -1241,8 +1252,12 @@ dict_load_foreign( /*==============*/ const char* id, /*!< in: foreign constraint id as a null-terminated string */ - ibool check_charsets) + ibool check_charsets, /*!< in: TRUE=check charset compatibility */ + ibool check_recursive) + /*!< in: Whether to record the foreign table + parent count to avoid unlimited recursive + load of chained foreign tables */ { dict_foreign_t* foreign; dict_table_t* sys_foreign; @@ -1256,6 +1271,8 @@ dict_load_foreign( ulint len; ulint n_fields_and_type; mtr_t mtr; + dict_table_t* for_table; + dict_table_t* ref_table; ut_ad(mutex_own(&(dict_sys->mutex))); @@ -1340,11 +1357,54 @@ dict_load_foreign( dict_load_foreign_cols(id, foreign); - /* If the foreign table is not yet in the dictionary cache, we - have to load it so that we are able to make type comparisons - in the next function call. */ + ref_table = dict_table_check_if_in_cache_low( + foreign->referenced_table_name); - dict_table_get_low(foreign->foreign_table_name); + /* We could possibly wind up in a deep recursive calls if + we call dict_table_get_low() again here if there + is a chain of tables concatenated together with + foreign constraints. In such case, each table is + both a parent and child of the other tables, and + act as a "link" in such table chains. + To avoid such scenario, we would need to check the + number of ancesters the current table has. If that + exceeds DICT_FK_MAX_CHAIN_LEN, we will stop loading + the child table. + Foreign constraints are loaded in a Breath First fashion, + that is, the index on FOR_NAME is scanned first, and then + index on REF_NAME. So foreign constrains in which + current table is a child (foreign table) are loaded first, + and then those constraints where current table is a + parent (referenced) table. + Thus we could check the parent (ref_table) table's + reference count (fk_max_recusive_level) to know how deep the + recursive call is. If the parent table (ref_table) is already + loaded, and its fk_max_recusive_level is larger than + DICT_FK_MAX_CHAIN_LEN, we will stop the recursive loading + by skipping loading the child table. It will not affect foreign + constraint check for DMLs since child table will be loaded + at that time for the constraint check. */ + if (!ref_table + || ref_table->fk_max_recusive_level < DICT_FK_MAX_RECURSIVE_LOAD) { + + /* If the foreign table is not yet in the dictionary cache, we + have to load it so that we are able to make type comparisons + in the next function call. */ + + for_table = dict_table_get_low(foreign->foreign_table_name); + + if (for_table && ref_table && check_recursive) { + /* This is to record the longest chain of ancesters + this table has, if the parent has more ancesters + than this table has, record it after add 1 (for this + parent */ + if (ref_table->fk_max_recusive_level + >= for_table->fk_max_recusive_level) { + for_table->fk_max_recusive_level = + ref_table->fk_max_recusive_level + 1; + } + } + } /* Note that there may already be a foreign constraint object in the dictionary cache for this constraint: then the following @@ -1369,6 +1429,8 @@ ulint dict_load_foreigns( /*===============*/ const char* table_name, /*!< in: table name */ + ibool check_recursive,/*!< in: Whether to check recursive + load of tables chained by FK */ ibool check_charsets) /*!< in: TRUE=check charset compatibility */ { @@ -1470,7 +1532,7 @@ loop: /* Load the foreign constraint definition to the dictionary cache */ - err = dict_load_foreign(id, check_charsets); + err = dict_load_foreign(id, check_charsets, check_recursive); if (err != DB_SUCCESS) { btr_pcur_close(&pcur); @@ -1498,6 +1560,11 @@ load_next_index: mtr_start(&mtr); + /* Switch to scan index on REF_NAME, fk_max_recusive_level + already been updated when scanning FOR_NAME index, no need to + update again */ + check_recursive = FALSE; + goto start_load; } diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index e0a62ed3ac5..1cba1de1590 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -767,6 +767,19 @@ convert_error_code_to_mysql( case DB_INTERRUPTED: my_error(ER_QUERY_INTERRUPTED, MYF(0)); /* fall through */ + + case DB_FOREIGN_EXCEED_MAX_CASCADE: + push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + HA_ERR_ROW_IS_REFERENCED, + "InnoDB: Cannot delete/update " + "rows with cascading foreign key " + "constraints that exceed max " + "depth of %d. Please " + "drop extra constraints and try " + "again", DICT_FK_MAX_RECURSIVE_LOAD); + + /* fall through */ + case DB_ERROR: default: return(-1); /* unspecified error */ diff --git a/storage/innodb_plugin/include/db0err.h b/storage/innodb_plugin/include/db0err.h index c841c2b4afe..c7fa6d2a444 100644 --- a/storage/innodb_plugin/include/db0err.h +++ b/storage/innodb_plugin/include/db0err.h @@ -94,6 +94,9 @@ enum db_err { DB_PRIMARY_KEY_IS_NULL, /* a column in the PRIMARY KEY was found to be NULL */ + DB_FOREIGN_EXCEED_MAX_CASCADE, /* Foreign key constraint related + cascading delete/update exceeds + maximum allowed depth */ /* The following are partial failure codes */ DB_FAIL = 1000, diff --git a/storage/innodb_plugin/include/dict0load.h b/storage/innodb_plugin/include/dict0load.h index 60b8c1fb632..f41882019d5 100644 --- a/storage/innodb_plugin/include/dict0load.h +++ b/storage/innodb_plugin/include/dict0load.h @@ -97,6 +97,8 @@ ulint dict_load_foreigns( /*===============*/ const char* table_name, /*!< in: table name */ + ibool check_recursive,/*!< in: Whether to check recursive + load of tables chained by FK */ ibool check_charsets);/*!< in: TRUE=check charsets compatibility */ /********************************************************************//** diff --git a/storage/innodb_plugin/include/dict0mem.h b/storage/innodb_plugin/include/dict0mem.h index 2fce1e00927..19782c2e76a 100644 --- a/storage/innodb_plugin/include/dict0mem.h +++ b/storage/innodb_plugin/include/dict0mem.h @@ -112,6 +112,21 @@ ROW_FORMAT=REDUNDANT. */ in table->flags. */ /* @} */ +/** Tables could be chained together with Foreign key constraint. When +first load the parent table, we would load all of its descedents. +This could result in rescursive calls and out of stack error eventually. +DICT_FK_MAX_RECURSIVE_LOAD defines the maximum number of recursive loads, +when exceeded, the child table will not be loaded. It will be loaded when +the foreign constraint check needs to be run. */ +#define DICT_FK_MAX_RECURSIVE_LOAD 250 + +/** Similarly, when tables are chained together with foreign key constraints +with on cascading delete/update clause, delete from parent table could +result in recursive cascading calls. This defines the maximum number of +such cascading deletes/updates allowed. When exceeded, the delete from +parent table will fail, and user has to drop excessive foreign constraint +before proceeds. */ +#define FK_MAX_CASCADE_DEL 300 /**********************************************************************//** Creates a table memory object. @@ -434,6 +449,12 @@ struct dict_table_struct{ NOT allowed until this count gets to zero; MySQL does NOT itself check the number of open handles at drop */ + unsigned fk_max_recusive_level:8; + /*!< maximum recursive level we support when + loading tables chained together with FK + constraints. If exceeds this level, we will + stop loading child table into memory along with + its parent table */ ulint n_foreign_key_checks_running; /*!< count of how many foreign key check operations are currently being performed diff --git a/storage/innodb_plugin/include/que0que.h b/storage/innodb_plugin/include/que0que.h index 39f8d07af89..84b83208416 100644 --- a/storage/innodb_plugin/include/que0que.h +++ b/storage/innodb_plugin/include/que0que.h @@ -381,6 +381,9 @@ struct que_thr_struct{ thus far */ ulint lock_state; /*!< lock state of thread (table or row) */ + ulint fk_cascade_depth; /*!< maximum cascading call depth + supported for foreign key constraint + related delete/updates */ }; #define QUE_THR_MAGIC_N 8476583 diff --git a/storage/innodb_plugin/row/row0merge.c b/storage/innodb_plugin/row/row0merge.c index 56a68b58225..05d77ad7f19 100644 --- a/storage/innodb_plugin/row/row0merge.c +++ b/storage/innodb_plugin/row/row0merge.c @@ -2395,7 +2395,7 @@ row_merge_rename_tables( goto err_exit; } - err = dict_load_foreigns(old_name, TRUE); + err = dict_load_foreigns(old_name, FALSE, TRUE); if (err != DB_SUCCESS) { err_exit: diff --git a/storage/innodb_plugin/row/row0mysql.c b/storage/innodb_plugin/row/row0mysql.c index feeb7fc80b7..a582de3e583 100644 --- a/storage/innodb_plugin/row/row0mysql.c +++ b/storage/innodb_plugin/row/row0mysql.c @@ -576,6 +576,13 @@ handle_new_error: "InnoDB: " REFMAN "forcing-recovery.html" " for help.\n", stderr); break; + case DB_FOREIGN_EXCEED_MAX_CASCADE: + fprintf(stderr, "InnoDB: Cannot delete/update rows with" + " cascading foreign key constraints that exceed max" + " depth of %lu\n" + "Please drop excessive foreign constraints" + " and try again\n", (ulong) DICT_FK_MAX_RECURSIVE_LOAD); + break; default: fprintf(stderr, "InnoDB: unknown error code %lu\n", (ulong) err); @@ -1381,11 +1388,15 @@ row_update_for_mysql( run_again: thr->run_node = node; thr->prev_node = node; + thr->fk_cascade_depth = 0; row_upd_step(thr); err = trx->error_state; + /* Reset fk_cascade_depth back to 0 */ + thr->fk_cascade_depth = 0; + if (err != DB_SUCCESS) { que_thr_stop_for_mysql(thr); @@ -1576,6 +1587,12 @@ row_update_cascade_for_mysql( trx_t* trx; trx = thr_get_trx(thr); + + thr->fk_cascade_depth++; + + if (thr->fk_cascade_depth > FK_MAX_CASCADE_DEL) { + return (DB_FOREIGN_EXCEED_MAX_CASCADE); + } run_again: thr->run_node = node; thr->prev_node = node; @@ -2056,7 +2073,7 @@ row_table_add_foreign_constraints( name, reject_fks); if (err == DB_SUCCESS) { /* Check that also referencing constraints are ok */ - err = dict_load_foreigns(name, TRUE); + err = dict_load_foreigns(name, FALSE, TRUE); } if (err != DB_SUCCESS) { @@ -3915,7 +3932,7 @@ end: an ALTER, not in a RENAME. */ err = dict_load_foreigns( - new_name, !old_is_tmp || trx->check_foreigns); + new_name, FALSE, !old_is_tmp || trx->check_foreigns); if (err != DB_SUCCESS) { ut_print_timestamp(stderr); From f4e68824c83c3e3d38597df393a33e1fb0a9d2a4 Mon Sep 17 00:00:00 2001 From: Jimmy Yang Date: Wed, 4 Aug 2010 03:37:44 -0700 Subject: [PATCH 006/711] Fix bug #54678, InnoDB, TRUNCATE, ALTER, I_S SELECT, crash or deadlock rb://399 approved by Sunny Bains --- storage/innodb_plugin/ChangeLog | 5 +++ storage/innodb_plugin/include/dict0dict.h | 16 +++++++++ storage/innodb_plugin/include/dict0dict.ic | 42 ++++++++++++++++++++++ storage/innodb_plugin/row/row0mysql.c | 14 ++++++++ 4 files changed, 77 insertions(+) diff --git a/storage/innodb_plugin/ChangeLog b/storage/innodb_plugin/ChangeLog index 56729852a62..ce625a93871 100644 --- a/storage/innodb_plugin/ChangeLog +++ b/storage/innodb_plugin/ChangeLog @@ -1,3 +1,8 @@ +2010-08-03 The InnoDB Team + + * include/dict0dict.h, include/dict0dict.ic, row/row0mysql.c: + Fix bug #54678, InnoDB, TRUNCATE, ALTER, I_S SELECT, crash or deadlock + 2010-08-03 The InnoDB Team * dict/dict0load.c, handler/ha_innodb.cc, include/db0err.h, diff --git a/storage/innodb_plugin/include/dict0dict.h b/storage/innodb_plugin/include/dict0dict.h index 3a1bee4cd89..5ffa59538c8 100644 --- a/storage/innodb_plugin/include/dict0dict.h +++ b/storage/innodb_plugin/include/dict0dict.h @@ -680,6 +680,22 @@ ulint dict_table_zip_size( /*================*/ const dict_table_t* table); /*!< in: table */ +/*********************************************************************//** +Obtain exclusive locks on all index trees of the table. This is to prevent +accessing index trees while InnoDB is updating internal metadata for +operations such as truncate tables. */ +UNIV_INLINE +void +dict_table_x_lock_indexes( +/*======================*/ + dict_table_t* table); /*!< in: table */ +/*********************************************************************//** +Release the exclusive locks on all index tree. */ +UNIV_INLINE +void +dict_table_x_unlock_indexes( +/*========================*/ + dict_table_t* table); /*!< in: table */ /********************************************************************//** Checks if a column is in the ordering columns of the clustered index of a table. Column prefixes are treated like whole columns. diff --git a/storage/innodb_plugin/include/dict0dict.ic b/storage/innodb_plugin/include/dict0dict.ic index 46e78df8272..1704e9c2d71 100644 --- a/storage/innodb_plugin/include/dict0dict.ic +++ b/storage/innodb_plugin/include/dict0dict.ic @@ -452,6 +452,48 @@ dict_table_zip_size( return(dict_table_flags_to_zip_size(table->flags)); } +/*********************************************************************//** +Obtain exclusive locks on all index trees of the table. This is to prevent +accessing index trees while InnoDB is updating internal metadata for +operations such as truncate tables. */ +UNIV_INLINE +void +dict_table_x_lock_indexes( +/*======================*/ + dict_table_t* table) /*!< in: table */ +{ + dict_index_t* index; + + ut_a(table); + ut_ad(mutex_own(&(dict_sys->mutex))); + + /* Loop through each index of the table and lock them */ + for (index = dict_table_get_first_index(table); + index != NULL; + index = dict_table_get_next_index(index)) { + rw_lock_x_lock(dict_index_get_lock(index)); + } +} + +/*********************************************************************//** +Release the exclusive locks on all index tree. */ +UNIV_INLINE +void +dict_table_x_unlock_indexes( +/*========================*/ + dict_table_t* table) /*!< in: table */ +{ + dict_index_t* index; + + ut_a(table); + ut_ad(mutex_own(&(dict_sys->mutex))); + + for (index = dict_table_get_first_index(table); + index != NULL; + index = dict_table_get_next_index(index)) { + rw_lock_x_unlock(dict_index_get_lock(index)); + } +} /********************************************************************//** Gets the number of fields in the internal representation of an index, including fields added by the dictionary system. diff --git a/storage/innodb_plugin/row/row0mysql.c b/storage/innodb_plugin/row/row0mysql.c index a582de3e583..9d7cb976caf 100644 --- a/storage/innodb_plugin/row/row0mysql.c +++ b/storage/innodb_plugin/row/row0mysql.c @@ -2766,6 +2766,15 @@ row_truncate_table_for_mysql( trx->table_id = table->id; + /* Lock all index trees for this table, as we will + truncate the table/index and possibly change their metadata. + All DML/DDL are blocked by table level lock, with + a few exceptions such as queries into information schema + about the table, MySQL could try to access index stats + for this kind of query, we need to use index locks to + sync up */ + dict_table_x_lock_indexes(table); + if (table->space && !table->dir_path_of_temp_table) { /* Discard and create the single-table tablespace. */ ulint space = table->space; @@ -2782,6 +2791,7 @@ row_truncate_table_for_mysql( || fil_create_new_single_table_tablespace( space, table->name, FALSE, flags, FIL_IBD_FILE_INITIAL_SIZE) != DB_SUCCESS) { + dict_table_x_unlock_indexes(table); ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: TRUNCATE TABLE %s failed to" @@ -2885,6 +2895,10 @@ next_rec: mem_heap_free(heap); + /* Done with index truncation, release index tree locks, + subsequent work relates to table level metadata change */ + dict_table_x_unlock_indexes(table); + dict_hdr_get_new_id(&new_id, NULL, NULL); info = pars_info_create(); From e19a494287037b24b272b0a88cb48baf80a1df09 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Thu, 5 Aug 2010 19:18:17 +1000 Subject: [PATCH 007/711] Fix bug# 55543 - InnoDB Plugin: Signal 6: Assertion failure in file fil/fil0fil.c line 4306 The bug is due to a double delete of a BLOB, once via: rollback -> btr_cur_pessimistic_delete() and the second time via purge. The bug is in row_upd_clust_rec_by_insert(). There we relinquish ownership of the non-updated BLOB columns in btr_cur_mark_extern_inherited_fields() before building the row entry that will be inserted and whose contents will be logged in the UNDO log. However, we don't set the BLOB column later to INHERITED so that a possible rollback will not free the original row's non-updated BLOB entries. This is because the condition that checks for that is in : if (node->upd_ext) {}. node->upd_ext is non-NULL only if a BLOB column was updated and that column is part of some key ordering (see row_upd_replace()). This results in the non-update BLOB columns being deleted during a rollback and subsequently by purge again. rb://413 --- storage/innodb_plugin/btr/btr0cur.c | 12 +++++++++--- storage/innodb_plugin/include/btr0cur.h | 5 +++-- storage/innodb_plugin/row/row0upd.c | 12 +++++++----- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/storage/innodb_plugin/btr/btr0cur.c b/storage/innodb_plugin/btr/btr0cur.c index 7fa7d42320a..8db12a0bbb8 100644 --- a/storage/innodb_plugin/btr/btr0cur.c +++ b/storage/innodb_plugin/btr/btr0cur.c @@ -3484,9 +3484,10 @@ btr_cur_set_ownership_of_extern_field( Marks not updated extern fields as not-owned by this record. The ownership is transferred to the updated record which is inserted elsewhere in the index tree. In purge only the owner of externally stored field is allowed -to free the field. */ +to free the field. +@return TRUE if BLOB ownership was transferred */ UNIV_INTERN -void +ibool btr_cur_mark_extern_inherited_fields( /*=================================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed @@ -3500,13 +3501,14 @@ btr_cur_mark_extern_inherited_fields( ulint n; ulint j; ulint i; + ibool change_ownership = FALSE; ut_ad(rec_offs_validate(rec, NULL, offsets)); ut_ad(!rec_offs_comp(offsets) || !rec_get_node_ptr_flag(rec)); if (!rec_offs_any_extern(offsets)) { - return; + return(FALSE); } n = rec_offs_n_fields(offsets); @@ -3529,10 +3531,14 @@ btr_cur_mark_extern_inherited_fields( btr_cur_set_ownership_of_extern_field( page_zip, rec, index, offsets, i, FALSE, mtr); + + change_ownership = TRUE; updated: ; } } + + return(change_ownership); } /*******************************************************************//** diff --git a/storage/innodb_plugin/include/btr0cur.h b/storage/innodb_plugin/include/btr0cur.h index 7dc2eb63cf5..e151fdcb563 100644 --- a/storage/innodb_plugin/include/btr0cur.h +++ b/storage/innodb_plugin/include/btr0cur.h @@ -468,9 +468,10 @@ btr_estimate_number_of_different_key_vals( Marks not updated extern fields as not-owned by this record. The ownership is transferred to the updated record which is inserted elsewhere in the index tree. In purge only the owner of externally stored field is allowed -to free the field. */ +to free the field. +@return TRUE if BLOB ownership was transferred */ UNIV_INTERN -void +ibool btr_cur_mark_extern_inherited_fields( /*=================================*/ page_zip_des_t* page_zip,/*!< in/out: compressed page whose uncompressed diff --git a/storage/innodb_plugin/row/row0upd.c b/storage/innodb_plugin/row/row0upd.c index 397b117c067..04c3139fcc7 100644 --- a/storage/innodb_plugin/row/row0upd.c +++ b/storage/innodb_plugin/row/row0upd.c @@ -1598,6 +1598,7 @@ row_upd_clust_rec_by_insert( dict_table_t* table; dtuple_t* entry; ulint err; + ibool change_ownership = FALSE; ut_ad(node); ut_ad(dict_index_is_clust(index)); @@ -1630,9 +1631,9 @@ row_upd_clust_rec_by_insert( index = dict_table_get_first_index(table); offsets = rec_get_offsets(rec, index, offsets_, ULINT_UNDEFINED, &heap); - btr_cur_mark_extern_inherited_fields( - btr_cur_get_page_zip(btr_cur), - rec, index, offsets, node->update, mtr); + change_ownership = btr_cur_mark_extern_inherited_fields( + btr_cur_get_page_zip(btr_cur), rec, index, offsets, + node->update, mtr); if (check_ref) { /* NOTE that the following call loses the position of pcur ! */ @@ -1661,10 +1662,11 @@ row_upd_clust_rec_by_insert( row_upd_index_entry_sys_field(entry, index, DATA_TRX_ID, trx->id); - if (node->upd_ext) { + if (change_ownership) { /* If we return from a lock wait, for example, we may have extern fields marked as not-owned in entry (marked in the - if-branch above). We must unmark them. */ + if-branch above). We must unmark them, take the ownership + back. */ btr_cur_unmark_dtuple_extern_fields(entry); From 6c0f9301ea434cce4fe2df0ad36c26694375b997 Mon Sep 17 00:00:00 2001 From: Sunny Bains Date: Thu, 5 Aug 2010 19:24:34 +1000 Subject: [PATCH 008/711] Fix Bug #55277 - Failing assertion: auto_inc > 0 Handle overflow when reading value from SELECT MAX(C) FROM T; Call ha_innobase::info() after initializing the autoinc value in ha_innobase::open(). Fix for both the builtin and plugin. rb://402 --- .../suite/innodb/r/innodb-autoinc.result | 13 ++++++++++++ mysql-test/suite/innodb/t/innodb-autoinc.test | 12 +++++++++++ storage/innobase/handler/ha_innodb.cc | 21 ++++++++++++------- storage/innodb_plugin/handler/ha_innodb.cc | 21 ++++++++++++------- 4 files changed, 53 insertions(+), 14 deletions(-) diff --git a/mysql-test/suite/innodb/r/innodb-autoinc.result b/mysql-test/suite/innodb/r/innodb-autoinc.result index a36b3a1a865..350c7ebd541 100644 --- a/mysql-test/suite/innodb/r/innodb-autoinc.result +++ b/mysql-test/suite/innodb/r/innodb-autoinc.result @@ -1244,3 +1244,16 @@ t1 CREATE TABLE `t1` ( PRIMARY KEY (`c1`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1 DROP TABLE t1; +DROP TABLE IF EXISTS t1; +Warnings: +Note 1051 Unknown table 't1' +CREATE TABLE t1(c1 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES (18446744073709551615); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `c1` bigint(20) unsigned NOT NULL AUTO_INCREMENT, + PRIMARY KEY (`c1`) +) ENGINE=InnoDB AUTO_INCREMENT=18446744073709551615 DEFAULT CHARSET=latin1 +DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/innodb-autoinc.test b/mysql-test/suite/innodb/t/innodb-autoinc.test index ef0359b78b0..10602499222 100644 --- a/mysql-test/suite/innodb/t/innodb-autoinc.test +++ b/mysql-test/suite/innodb/t/innodb-autoinc.test @@ -662,3 +662,15 @@ INSERT INTO t1 VALUES (1), (2), (-685113344), (NULL); SELECT * FROM t1; SHOW CREATE TABLE t1; DROP TABLE t1; + +## +# 55277: Failing assertion: auto_inc > 0 +# +DROP TABLE IF EXISTS t1; +CREATE TABLE t1(c1 BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY) ENGINE=InnoDB; +INSERT INTO t1 VALUES (NULL); +INSERT INTO t1 VALUES (18446744073709551615); +# Restart the server +-- source include/restart_mysqld.inc +SHOW CREATE TABLE t1; +DROP TABLE t1; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 930785d7dcd..300d9743234 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -2720,12 +2720,19 @@ ha_innobase::innobase_initialize_autoinc() err = row_search_max_autoinc(index, col_name, &read_auto_inc); switch (err) { - case DB_SUCCESS: - /* At the this stage we do not know the increment - or the offset, so use a default increment of 1. */ - auto_inc = read_auto_inc + 1; - break; + case DB_SUCCESS: { + ulonglong col_max_value; + col_max_value = innobase_get_int_col_max_value(field); + + /* At the this stage we do not know the increment + nor the offset, so use a default increment of 1. */ + + auto_inc = innobase_next_autoinc( + read_auto_inc, 1, 1, col_max_value); + + break; + } case DB_RECORD_NOT_FOUND: ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: MySQL and InnoDB data " @@ -2951,8 +2958,6 @@ retry: /* Init table lock structure */ thr_lock_data_init(&share->lock,&lock,(void*) 0); - info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); - /* Only if the table has an AUTOINC column. */ if (prebuilt->table != NULL && table->found_next_number_field != NULL) { dict_table_autoinc_lock(prebuilt->table); @@ -2969,6 +2974,8 @@ retry: dict_table_autoinc_unlock(prebuilt->table); } + info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); + DBUG_RETURN(0); } diff --git a/storage/innodb_plugin/handler/ha_innodb.cc b/storage/innodb_plugin/handler/ha_innodb.cc index 1cba1de1590..69ca4a5051e 100644 --- a/storage/innodb_plugin/handler/ha_innodb.cc +++ b/storage/innodb_plugin/handler/ha_innodb.cc @@ -3361,12 +3361,19 @@ ha_innobase::innobase_initialize_autoinc() err = row_search_max_autoinc(index, col_name, &read_auto_inc); switch (err) { - case DB_SUCCESS: - /* At the this stage we do not know the increment - or the offset, so use a default increment of 1. */ - auto_inc = read_auto_inc + 1; - break; + case DB_SUCCESS: { + ulonglong col_max_value; + col_max_value = innobase_get_int_col_max_value(field); + + /* At the this stage we do not know the increment + nor the offset, so use a default increment of 1. */ + + auto_inc = innobase_next_autoinc( + read_auto_inc, 1, 1, col_max_value); + + break; + } case DB_RECORD_NOT_FOUND: ut_print_timestamp(stderr); fprintf(stderr, " InnoDB: MySQL and InnoDB data " @@ -3661,8 +3668,6 @@ retry: dict_table_get_format(prebuilt->table)); } - info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); - /* Only if the table has an AUTOINC column. */ if (prebuilt->table != NULL && table->found_next_number_field != NULL) { dict_table_autoinc_lock(prebuilt->table); @@ -3679,6 +3684,8 @@ retry: dict_table_autoinc_unlock(prebuilt->table); } + info(HA_STATUS_NO_LOCK | HA_STATUS_VARIABLE | HA_STATUS_CONST); + DBUG_RETURN(0); } From ed736379f5ba4e681088a139cefaebb50600a44a Mon Sep 17 00:00:00 2001 From: Gleb Shchepa Date: Fri, 6 Aug 2010 23:29:37 +0400 Subject: [PATCH 009/711] Bug #55424: convert_tz crashes when fed invalid data The CONVERT_TZ function crashes the server when the timezone argument is an empty SET field value. 1) The CONVERT_TZ may find a timezone string in the tz_names hash. 2) A string representation of the empty SET is a String of zero length with the NULL pointer. 3) If the key argument length is zero, hash functions do comparison using the length of the record being compared against. I.e. a zero-length String buffer is an invalid argument for hash search functions, and if String points to NULL buffer, hashcmp() fails with SEGV accessing that memory. The my_tz_find function has been modified to treat empty Strings as invalid timezone values to skip unnecessary hash search. --- mysql-test/r/timezone2.result | 12 ++++++++++++ mysql-test/t/timezone2.test | 9 +++++++++ sql/sql_string.h | 2 +- sql/tztime.cc | 2 +- 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/timezone2.result b/mysql-test/r/timezone2.result index 2948bb8ecec..e2e337628ce 100644 --- a/mysql-test/r/timezone2.result +++ b/mysql-test/r/timezone2.result @@ -296,4 +296,16 @@ CONVERT_TZ(NOW(), 'UTC', 'Europe/Moscow') IS NULL UPDATE t1 SET t = CONVERT_TZ(t, 'UTC', 'Europe/Moscow'); UNLOCK TABLES; DROP TABLE t1; +# +# Bug #55424: convert_tz crashes when fed invalid data +# +CREATE TABLE t1 (a SET('x') NOT NULL); +INSERT INTO t1 VALUES (''); +SELECT CONVERT_TZ(1, a, 1) FROM t1; +CONVERT_TZ(1, a, 1) +NULL +SELECT CONVERT_TZ(1, 1, a) FROM t1; +CONVERT_TZ(1, 1, a) +NULL +DROP TABLE t1; End of 5.1 tests diff --git a/mysql-test/t/timezone2.test b/mysql-test/t/timezone2.test index 15ddceb8d68..c4445da107c 100644 --- a/mysql-test/t/timezone2.test +++ b/mysql-test/t/timezone2.test @@ -273,5 +273,14 @@ UNLOCK TABLES; DROP TABLE t1; +--echo # +--echo # Bug #55424: convert_tz crashes when fed invalid data +--echo # + +CREATE TABLE t1 (a SET('x') NOT NULL); +INSERT INTO t1 VALUES (''); +SELECT CONVERT_TZ(1, a, 1) FROM t1; +SELECT CONVERT_TZ(1, 1, a) FROM t1; +DROP TABLE t1; --echo End of 5.1 tests diff --git a/sql/sql_string.h b/sql/sql_string.h index d62908e5d66..bb7d69aeccc 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -97,7 +97,7 @@ public: inline uint32 alloced_length() const { return Alloced_length;} inline char& operator [] (uint32 i) const { return Ptr[i]; } inline void length(uint32 len) { str_length=len ; } - inline bool is_empty() { return (str_length == 0); } + inline bool is_empty() const { return (str_length == 0); } inline void mark_as_const() { Alloced_length= 0;} inline const char *ptr() const { return Ptr; } inline char *c_ptr() diff --git a/sql/tztime.cc b/sql/tztime.cc index c7a4ad049ec..7ebb8eb392a 100644 --- a/sql/tztime.cc +++ b/sql/tztime.cc @@ -2259,7 +2259,7 @@ my_tz_find(THD *thd, const String *name) DBUG_PRINT("enter", ("time zone name='%s'", name ? ((String *)name)->c_ptr_safe() : "NULL")); - if (!name) + if (!name || name->is_empty()) DBUG_RETURN(0); VOID(pthread_mutex_lock(&tz_LOCK)); From 881a76699ee68bbfddec0c413c0caf769d32f3c1 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Mon, 9 Aug 2010 11:32:50 +0300 Subject: [PATCH 010/711] WL#1054: Pluggable authentication support Merged the implementation to a new base tree. --- Makefile.am | 4 +- client/CMakeLists.txt | 2 + client/client_priv.h | 2 + client/mysql.cc | 18 +- client/mysqltest.cc | 26 +- config.h.cmake | 2 + configure.in | 3 +- include/Makefile.am | 7 +- include/errmsg.h | 3 +- include/my_global.h | 12 +- include/my_no_pthread.h | 1 + include/my_sys.h | 6 +- include/mysql.h | 54 +- include/mysql.h.pp | 37 +- include/mysql/client_plugin.h | 146 + include/mysql/client_plugin.h.pp | 41 + include/mysql/plugin.h | 3 +- include/mysql/plugin_auth.h | 118 + include/mysql/plugin_auth.h.pp | 205 ++ include/mysql/plugin_auth_common.h | 105 + include/mysql_com.h | 15 +- include/sql_common.h | 63 +- libmysql/CMakeLists.txt | 3 +- libmysql/Makefile.shared | 14 +- libmysql/client_settings.h | 5 +- libmysql/errmsg.c | 3 +- libmysql/libmysql.c | 101 +- libmysqld/CMakeLists.txt | 1 + libmysqld/Makefile.am | 2 +- libmysqld/embedded_priv.h | 2 + libmysqld/lib_sql.cc | 80 +- libmysqld/libmysqld.c | 8 +- mysql-test/include/have_plugin_auth.inc | 4 + mysql-test/mysql-test-run.pl | 41 + mysql-test/r/1st.result | 1 + mysql-test/r/change_user.result | 36 + mysql-test/r/connect.result | 3 + mysql-test/r/events_bugs.result | 1 + mysql-test/r/grant.result | 136 +- mysql-test/r/grant2.result | 6 +- mysql-test/r/grant_cache_no_prot.result | 2 + mysql-test/r/grant_cache_ps_prot.result | 2 + mysql-test/r/information_schema.result | 4 +- mysql-test/r/log_tables_upgrade.result | 1 + mysql-test/r/mysql_upgrade.result | 6 + mysql-test/r/mysqlcheck.result | 2 + mysql-test/r/plugin_auth.result | 212 ++ mysql-test/r/ps.result | 6 +- mysql-test/r/sp_notembedded.result | 6 + mysql-test/r/system_mysql_db.result | 3 + mysql-test/suite/federated/federated.result | 2 +- .../suite/funcs_1/r/innodb_trig_03e.result | 1 + .../suite/funcs_1/r/is_columns_mysql.result | 14 + .../funcs_1/r/is_key_column_usage.result | 4 + .../suite/funcs_1/r/is_statistics.result | 4 + .../funcs_1/r/is_statistics_mysql.result | 4 + .../funcs_1/r/is_table_constraints.result | 1 + .../r/is_table_constraints_mysql.result | 1 + .../suite/funcs_1/r/is_tables_mysql.result | 23 + .../suite/funcs_1/r/is_user_privileges.result | 1655 +++++++++- .../suite/funcs_1/r/memory_trig_03e.result | 1 + .../suite/funcs_1/r/myisam_trig_03e.result | 1 + .../funcs_1/r/processlist_priv_no_prot.result | 1 + .../funcs_1/r/processlist_priv_ps.result | 1 + .../suite/funcs_1/t/is_user_privileges.test | 40 + .../perfschema/r/column_privilege.result | 1 + .../suite/perfschema/r/pfs_upgrade.result | 10 +- .../suite/perfschema/r/privilege.result | 1 + mysql-test/suite/rpl/r/rpl_do_grant.result | 4 + .../suite/rpl/r/rpl_ignore_table.result | 1 + mysql-test/suite/rpl/r/rpl_stm_000001.result | 1 + .../sys_vars/r/external_user_basic.result | 3 + .../suite/sys_vars/r/proxy_user_basic.result | 3 + .../suite/sys_vars/t/external_user_basic.test | 1 + .../suite/sys_vars/t/proxy_user_basic.test | 1 + mysql-test/t/change_user.test | 48 + mysql-test/t/grant.test | 6 +- mysql-test/t/grant2.test | 4 +- mysql-test/t/plugin_auth-master.opt | 2 + mysql-test/t/plugin_auth.test | 298 ++ mysql-test/t/system_mysql_db_fix40123.test | 2 +- mysql-test/t/system_mysql_db_fix50030.test | 2 +- mysql-test/t/system_mysql_db_fix50117.test | 2 +- plugin/auth/CMakeLists.txt | 32 + plugin/auth/Makefile.am | 16 + plugin/auth/auth_socket.c | 93 + plugin/auth/dialog.c | 325 ++ plugin/auth/plug.in | 12 + plugin/auth/test_plugin.c | 200 ++ scripts/mysql_system_tables.sql | 6 +- scripts/mysql_system_tables_data.sql | 14 +- scripts/mysql_system_tables_fix.sql | 5 + sql-common/Makefile.am | 2 +- sql-common/client.c | 1115 +++++-- sql-common/client_plugin.c | 453 +++ sql/CMakeLists.txt | 1 + sql/Makefile.am | 12 +- sql/client_settings.h | 4 +- sql/ha_ndbcluster.cc | 2 +- sql/ha_ndbcluster_binlog.cc | 2 +- sql/lex.h | 5 +- sql/mysqld.cc | 3 + sql/password.c | 12 +- sql/protocol.cc | 18 - sql/protocol.h | 1 - sql/set_var.cc | 6 +- sql/share/errmsg-utf8.txt | 29 + sql/sp_head.h | 1 + sql/sql_acl.cc | 2848 +++++++++++++++-- sql/sql_acl.h | 58 +- sql/sql_audit.h | 2 +- sql/sql_builtin.cc.in | 4 +- sql/sql_class.cc | 56 +- sql/sql_class.h | 14 +- sql/sql_connect.cc | 459 +-- sql/sql_connect.h | 4 + sql/sql_insert.cc | 4 +- sql/sql_lex.cc | 1 + sql/sql_lex.h | 3 + sql/sql_parse.cc | 177 +- sql/sql_plugin.cc | 12 +- sql/sql_yacc.yy | 54 +- sql/structs.h | 2 +- sql/sys_vars.cc | 8 + sql/sys_vars.h | 61 + sql/table.cc | 20 +- tests/mysql_client_test.c | 1 + 127 files changed, 8248 insertions(+), 1616 deletions(-) create mode 100644 include/mysql/client_plugin.h create mode 100644 include/mysql/client_plugin.h.pp create mode 100644 include/mysql/plugin_auth.h create mode 100644 include/mysql/plugin_auth.h.pp create mode 100644 include/mysql/plugin_auth_common.h create mode 100644 mysql-test/include/have_plugin_auth.inc create mode 100644 mysql-test/r/plugin_auth.result create mode 100644 mysql-test/suite/sys_vars/r/external_user_basic.result create mode 100644 mysql-test/suite/sys_vars/r/proxy_user_basic.result create mode 100644 mysql-test/suite/sys_vars/t/external_user_basic.test create mode 100644 mysql-test/suite/sys_vars/t/proxy_user_basic.test create mode 100644 mysql-test/t/plugin_auth-master.opt create mode 100644 mysql-test/t/plugin_auth.test create mode 100644 plugin/auth/CMakeLists.txt create mode 100644 plugin/auth/Makefile.am create mode 100644 plugin/auth/auth_socket.c create mode 100644 plugin/auth/dialog.c create mode 100644 plugin/auth/plug.in create mode 100644 plugin/auth/test_plugin.c create mode 100644 sql-common/client_plugin.c diff --git a/Makefile.am b/Makefile.am index 7ab9a9aa061..ef815238998 100644 --- a/Makefile.am +++ b/Makefile.am @@ -267,7 +267,9 @@ test-full-qa: API_PREPROCESSOR_HEADER = $(top_srcdir)/include/mysql/plugin.h \ $(top_srcdir)/include/mysql.h \ $(top_srcdir)/include/mysql/psi/psi_abi_v1.h \ - $(top_srcdir)/include/mysql/psi/psi_abi_v2.h + $(top_srcdir)/include/mysql/psi/psi_abi_v2.h \ + $(top_srcdir)/include/mysql/client_plugin.h \ + $(top_srcdir)/include/mysql/plugin_auth.h # # Rules for checking that the abi/api has not changed. diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index d0bb8d43edd..5be39396edf 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -69,3 +69,5 @@ ADD_EXECUTABLE(echo echo.c) SET_TARGET_PROPERTIES (mysqlcheck mysqldump mysqlimport mysql_upgrade mysqlshow mysqlslap PROPERTIES HAS_CXX TRUE) +ADD_DEFINITIONS(-DHAVE_DLOPEN) + diff --git a/client/client_priv.h b/client/client_priv.h index e7911fc31f7..1a81768adc4 100644 --- a/client/client_priv.h +++ b/client/client_priv.h @@ -84,6 +84,8 @@ enum options_client OPT_DEBUG_INFO, OPT_DEBUG_CHECK, OPT_COLUMN_TYPES, OPT_ERROR_LOG_FILE, OPT_WRITE_BINLOG, OPT_DUMP_DATE, OPT_INIT_COMMAND, + OPT_PLUGIN_DIR, + OPT_DEFAULT_PLUGIN, OPT_MAX_CLIENT_OPTION }; diff --git a/client/mysql.cc b/client/mysql.cc index 01a786c13af..a985c135361 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -169,6 +169,7 @@ static int wait_time = 5; static STATUS status; static ulong select_limit,max_join_size,opt_connect_timeout=0; static char mysql_charsets_dir[FN_REFLEN+1]; +static char *opt_plugin_dir= 0, *opt_default_auth; static const char *xmlmeta[] = { "&", "&", "<", "<", @@ -1567,6 +1568,13 @@ static struct my_option my_long_options[] = {"show-warnings", OPT_SHOW_WARNINGS, "Show warnings after every statement.", &show_warnings, &show_warnings, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0}, + {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.", + (uchar**) &opt_plugin_dir, (uchar**) &opt_plugin_dir, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, + {"default_auth", OPT_PLUGIN_DIR, + "Default authentication client-side plugin to use.", + (uchar**) &opt_default_auth, (uchar**) &opt_default_auth, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; @@ -4298,9 +4306,15 @@ sql_real_connect(char *host,char *database,char *user,char *password, mysql_options(&mysql, MYSQL_SET_CHARSET_NAME, default_charset); + if (opt_plugin_dir && *opt_plugin_dir) + mysql_options(&mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); + + if (opt_default_auth && *opt_default_auth) + mysql_options(&mysql, MYSQL_DEFAULT_AUTH, opt_default_auth); + if (!mysql_real_connect(&mysql, host, user, password, - database, opt_mysql_port, opt_mysql_unix_port, - connect_flag | CLIENT_MULTI_STATEMENTS)) + database, opt_mysql_port, opt_mysql_unix_port, + connect_flag | CLIENT_MULTI_STATEMENTS)) { if (!silent || (mysql_errno(&mysql) != CR_CONN_HOST_ERROR && diff --git a/client/mysqltest.cc b/client/mysqltest.cc index 20f0f6d3164..fabe0e1ea3e 100644 --- a/client/mysqltest.cc +++ b/client/mysqltest.cc @@ -36,6 +36,7 @@ #include "client_priv.h" #include #include +#include #include #include #include @@ -190,6 +191,8 @@ static ulonglong timer_now(void); static ulong connection_retry_sleep= 100000; /* Microseconds */ +static char *opt_plugin_dir= 0; + /* Precompiled re's */ static my_regex_t ps_re; /* the query can be run using PS protocol */ static my_regex_t sp_re; /* the query can be run as a SP */ @@ -3728,13 +3731,15 @@ void do_change_user(struct st_command *command) } if (!ds_user.length) + { dynstr_set(&ds_user, mysql->user); - if (!ds_passwd.length) - dynstr_set(&ds_passwd, mysql->passwd); + if (!ds_passwd.length) + dynstr_set(&ds_passwd, mysql->passwd); - if (!ds_db.length) - dynstr_set(&ds_db, mysql->db); + if (!ds_db.length) + dynstr_set(&ds_db, mysql->db); + } DBUG_PRINT("info",("connection: '%s' user: '%s' password: '%s' database: '%s'", cur_con->name, ds_user.str, ds_passwd.str, ds_db.str)); @@ -5064,6 +5069,7 @@ void do_connect(struct st_command *command) static DYNAMIC_STRING ds_port; static DYNAMIC_STRING ds_sock; static DYNAMIC_STRING ds_options; + static DYNAMIC_STRING ds_default_auth; #ifdef HAVE_SMEM static DYNAMIC_STRING ds_shm; #endif @@ -5075,7 +5081,8 @@ void do_connect(struct st_command *command) { "database", ARG_STRING, FALSE, &ds_database, "Database to select after connect" }, { "port", ARG_STRING, FALSE, &ds_port, "Port to connect to" }, { "socket", ARG_STRING, FALSE, &ds_sock, "Socket to connect with" }, - { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" } + { "options", ARG_STRING, FALSE, &ds_options, "Options to use while connecting" }, + { "default_auth", ARG_STRING, FALSE, &ds_default_auth, "Default authentication to use" } }; DBUG_ENTER("do_connect"); @@ -5222,6 +5229,12 @@ void do_connect(struct st_command *command) if (ds_database.length == 0) dynstr_set(&ds_database, opt_db); + if (opt_plugin_dir && *opt_plugin_dir) + mysql_options(&con_slot->mysql, MYSQL_PLUGIN_DIR, opt_plugin_dir); + + if (ds_default_auth.length) + mysql_options(&con_slot->mysql, MYSQL_DEFAULT_AUTH, ds_default_auth.str); + /* Special database to allow one to connect without a database name */ if (ds_database.length && !strcmp(ds_database.str,"*NO-ONE*")) dynstr_set(&ds_database, ""); @@ -6000,6 +6013,9 @@ static struct my_option my_long_options[] = "Number of seconds before connection timeout.", &opt_connect_timeout, &opt_connect_timeout, 0, GET_UINT, REQUIRED_ARG, 120, 0, 3600 * 12, 0, 0, 0}, + {"plugin_dir", OPT_PLUGIN_DIR, "Directory for client-side plugins.", + (uchar**) &opt_plugin_dir, (uchar**) &opt_plugin_dir, 0, + GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0}, { 0, 0, 0, 0, 0, 0, GET_NO_ARG, NO_ARG, 0, 0, 0, 0, 0, 0} }; diff --git a/config.h.cmake b/config.h.cmake index c484edb65a5..40ea1fd4dff 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -603,6 +603,8 @@ #cmakedefine PLUGINDIR "@PLUGINDIR@" #cmakedefine DEFAULT_SYSCONFDIR "@DEFAULT_SYSCONFDIR@" +#cmakedefine SO_EXT "@CMAKE_SHARED_MODULE_SUFFIX@" + #define PACKAGE "mysql" #define PACKAGE_BUGREPORT "" #define PACKAGE_NAME "MySQL Server" diff --git a/configure.in b/configure.in index 7121a521f87..80c436940a8 100644 --- a/configure.in +++ b/configure.in @@ -1657,9 +1657,8 @@ case "$with_mysqld_ldflags " in ;; *) - # Check for dlopen, needed for user definable functions + # Check for dlopen, needed for user definable functions and plugins # This must be checked after threads on AIX - # We only need this for mysqld, not for the clients. my_save_LIBS="$LIBS" LIBS="" diff --git a/include/Makefile.am b/include/Makefile.am index e30588de065..9dbd818d592 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -24,7 +24,9 @@ HEADERS_ABI = mysql.h mysql_com.h mysql_time.h \ my_list.h my_alloc.h typelib.h mysql/plugin.h \ mysql/plugin_audit.h mysql/plugin_ftparser.h pkginclude_HEADERS = $(HEADERS_ABI) my_dbug.h m_string.h my_sys.h \ - my_xml.h mysql_embed.h mysql/services.h \ + my_xml.h mysql_embed.h mysql/plugin_auth.h \ + mysql/client_plugin.h mysql/plugin_auth_common.h \ + mysql/services.h \ mysql/service_my_snprintf.h mysql/service_thd_alloc.h \ my_pthread.h my_no_pthread.h \ decimal.h errmsg.h my_global.h my_net.h \ @@ -53,7 +55,8 @@ pkgpsiinclude_HEADERS = mysql/psi/psi.h mysql/psi/mysql_thread.h \ EXTRA_DIST = mysql.h.pp mysql/plugin.h.pp probes_mysql.d.base \ CMakeLists.txt \ mysql/psi/psi_abi_v1.h.pp \ - mysql/psi/psi_abi_v2.h.pp + mysql/psi/psi_abi_v2.h.pp \ + mysql/plugin_auth.h.pp mysql/client_plugin.h.pp # Remove built files and the symlinked directories CLEANFILES = $(BUILT_SOURCES) readline openssl probes_mysql.d probes_mysql_nodtrace.h diff --git a/include/errmsg.h b/include/errmsg.h index c55c94af169..f1d7dd65f97 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -101,7 +101,8 @@ extern const char *client_errors[]; /* Error messages */ #define CR_STMT_CLOSED 2056 #define CR_NEW_STMT_METADATA 2057 #define CR_ALREADY_CONNECTED 2058 -#define CR_ERROR_LAST /*Copy last error nr:*/ 2058 +#define CR_AUTH_PLUGIN_CANNOT_LOAD 2059 +#define CR_ERROR_LAST /*Copy last error nr:*/ 2059 /* Add error numbers before CR_ERROR_LAST and change it accordingly. */ #endif /* ERRMSG_INCLUDED */ diff --git a/include/my_global.h b/include/my_global.h index 1c615cc5ca2..378afe79904 100644 --- a/include/my_global.h +++ b/include/my_global.h @@ -452,6 +452,16 @@ extern "C" int madvise(void *addr, size_t len, int behav); #define LINT_INIT(var) #endif +#ifndef SO_EXT +#ifdef _WIN32 +#define SO_EXT ".dll" +#elif defined(__APPLE__) +#define SO_EXT ".dylib" +#else +#define SO_EXT ".so" +#endif +#endif + /* Suppress uninitialized variable warning without generating code. @@ -1365,7 +1375,7 @@ do { doubleget_union _tmp; \ #endif #ifndef HAVE_DLERROR -#define dlerror() "" +#define dlerror() "No support for dynamic loading (static build?)" #endif diff --git a/include/my_no_pthread.h b/include/my_no_pthread.h index a805a9151f8..633a5b94a6c 100644 --- a/include/my_no_pthread.h +++ b/include/my_no_pthread.h @@ -47,6 +47,7 @@ #define rw_wrlock(A) #define rw_unlock(A) #define rwlock_destroy(A) +#define safe_mutex_assert_owner(mp) #define mysql_mutex_init(A, B, C) do {} while (0) #define mysql_mutex_lock(A) do {} while (0) diff --git a/include/my_sys.h b/include/my_sys.h index 95689535be5..23c9b2da55f 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -197,7 +197,7 @@ extern void my_large_free(uchar *ptr); #define my_alloca(SZ) alloca((size_t) (SZ)) #define my_afree(PTR) {} #else -#define my_alloca(SZ) my_malloc(SZ,MYF(0)) +#define my_alloca(SZ) my_malloc(SZ,MYF(MY_FAE)) #define my_afree(PTR) my_free(PTR) #endif /* HAVE_ALLOCA */ @@ -824,6 +824,10 @@ extern void set_prealloc_root(MEM_ROOT *root, char *ptr); extern void reset_root_defaults(MEM_ROOT *mem_root, size_t block_size, size_t prealloc_size); extern char *strdup_root(MEM_ROOT *root,const char *str); +static inline char *safe_strdup_root(MEM_ROOT *root, const char *str) +{ + return str ? strdup_root(root, str) : 0; +} extern char *strmake_root(MEM_ROOT *root,const char *str,size_t len); extern void *memdup_root(MEM_ROOT *root,const void *str, size_t len); extern int get_defaults_options(int argc, char **argv, diff --git a/include/mysql.h b/include/mysql.h index dc6e4eb19a6..7d949597702 100644 --- a/include/mysql.h +++ b/include/mysql.h @@ -167,9 +167,15 @@ enum mysql_option MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION, MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH, MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT, - MYSQL_OPT_SSL_VERIFY_SERVER_CERT + MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_AUTH }; +/** + @todo remove the "extension", move st_mysql_options completely + out of mysql.h +*/ +struct st_mysql_options_extention; + struct st_mysql_options { unsigned int connect_timeout, read_timeout, write_timeout; unsigned int port, protocol; @@ -203,7 +209,7 @@ struct st_mysql_options { void (*local_infile_end)(void *); int (*local_infile_error)(void *, char *, unsigned int); void *local_infile_userdata; - void *extension; + struct st_mysql_options_extention *extension; }; enum mysql_status @@ -638,38 +644,6 @@ enum enum_stmt_attr_type }; -typedef struct st_mysql_methods -{ - my_bool (*read_query_result)(MYSQL *mysql); - my_bool (*advanced_command)(MYSQL *mysql, - enum enum_server_command command, - const unsigned char *header, - unsigned long header_length, - const unsigned char *arg, - unsigned long arg_length, - my_bool skip_check, - MYSQL_STMT *stmt); - MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields, - unsigned int fields); - MYSQL_RES * (*use_result)(MYSQL *mysql); - void (*fetch_lengths)(unsigned long *to, - MYSQL_ROW column, unsigned int field_count); - void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results); -#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) - MYSQL_FIELD * (*list_fields)(MYSQL *mysql); - my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt); - int (*stmt_execute)(MYSQL_STMT *stmt); - int (*read_binary_rows)(MYSQL_STMT *stmt); - int (*unbuffered_fetch)(MYSQL *mysql, char **row); - void (*free_embedded_thd)(MYSQL *mysql); - const char *(*read_statistics)(MYSQL *mysql); - my_bool (*next_result)(MYSQL *mysql); - int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd); - int (*read_rows_from_cursor)(MYSQL_STMT *stmt); -#endif -} MYSQL_METHODS; - - MYSQL_STMT * STDCALL mysql_stmt_init(MYSQL *mysql); int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned long length); @@ -732,18 +706,6 @@ int STDCALL mysql_drop_db(MYSQL *mysql, const char *DB); #endif #define HAVE_MYSQL_REAL_CONNECT -/* - The following functions are mainly exported because of mysqlbinlog; - They are not for general usage -*/ - -#define simple_command(mysql, command, arg, length, skip_check) \ - (*(mysql)->methods->advanced_command)(mysql, command, 0, \ - 0, arg, length, skip_check, NULL) -#define stmt_command(mysql, command, arg, length, stmt) \ - (*(mysql)->methods->advanced_command)(mysql, command, 0, \ - 0, arg, length, 1, stmt) - #ifdef __cplusplus } #endif diff --git a/include/mysql.h.pp b/include/mysql.h.pp index 531062aee80..7200a04d304 100644 --- a/include/mysql.h.pp +++ b/include/mysql.h.pp @@ -130,13 +130,13 @@ void create_random_string(char *to, unsigned int length, struct rand_struct *ran void hash_password(unsigned long *to, const char *password, unsigned int password_len); void make_scrambled_password_323(char *to, const char *password); void scramble_323(char *to, const char *message, const char *password); -my_bool check_scramble_323(const char *, const char *message, +my_bool check_scramble_323(const unsigned char *reply, const char *message, unsigned long *salt); void get_salt_from_password_323(unsigned long *res, const char *password); void make_password_from_salt_323(char *to, const unsigned long *salt); void make_scrambled_password(char *to, const char *password); void scramble(char *to, const char *message, const char *password); -my_bool check_scramble(const char *reply, const char *message, +my_bool check_scramble(const unsigned char *reply, const char *message, const unsigned char *hash_stage2); void get_salt_from_password(unsigned char *res, const char *password); void make_password_from_salt(char *to, const unsigned char *hash_stage2); @@ -262,8 +262,9 @@ enum mysql_option MYSQL_OPT_USE_REMOTE_CONNECTION, MYSQL_OPT_USE_EMBEDDED_CONNECTION, MYSQL_OPT_GUESS_CONNECTION, MYSQL_SET_CLIENT_IP, MYSQL_SECURE_AUTH, MYSQL_REPORT_DATA_TRUNCATION, MYSQL_OPT_RECONNECT, - MYSQL_OPT_SSL_VERIFY_SERVER_CERT + MYSQL_OPT_SSL_VERIFY_SERVER_CERT, MYSQL_PLUGIN_DIR, MYSQL_DEFAULT_AUTH }; +struct st_mysql_options_extention; struct st_mysql_options { unsigned int connect_timeout, read_timeout, write_timeout; unsigned int port, protocol; @@ -293,7 +294,7 @@ struct st_mysql_options { void (*local_infile_end)(void *); int (*local_infile_error)(void *, char *, unsigned int); void *local_infile_userdata; - void *extension; + struct st_mysql_options_extention *extension; }; enum mysql_status { @@ -547,34 +548,6 @@ enum enum_stmt_attr_type STMT_ATTR_CURSOR_TYPE, STMT_ATTR_PREFETCH_ROWS }; -typedef struct st_mysql_methods -{ - my_bool (*read_query_result)(MYSQL *mysql); - my_bool (*advanced_command)(MYSQL *mysql, - enum enum_server_command command, - const unsigned char *header, - unsigned long header_length, - const unsigned char *arg, - unsigned long arg_length, - my_bool skip_check, - MYSQL_STMT *stmt); - MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields, - unsigned int fields); - MYSQL_RES * (*use_result)(MYSQL *mysql); - void (*fetch_lengths)(unsigned long *to, - MYSQL_ROW column, unsigned int field_count); - void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results); - MYSQL_FIELD * (*list_fields)(MYSQL *mysql); - my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt); - int (*stmt_execute)(MYSQL_STMT *stmt); - int (*read_binary_rows)(MYSQL_STMT *stmt); - int (*unbuffered_fetch)(MYSQL *mysql, char **row); - void (*free_embedded_thd)(MYSQL *mysql); - const char *(*read_statistics)(MYSQL *mysql); - my_bool (*next_result)(MYSQL *mysql); - int (*read_change_user_result)(MYSQL *mysql, char *buff, const char *passwd); - int (*read_rows_from_cursor)(MYSQL_STMT *stmt); -} MYSQL_METHODS; MYSQL_STMT * mysql_stmt_init(MYSQL *mysql); int mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query, unsigned long length); diff --git a/include/mysql/client_plugin.h b/include/mysql/client_plugin.h new file mode 100644 index 00000000000..9631b090b14 --- /dev/null +++ b/include/mysql/client_plugin.h @@ -0,0 +1,146 @@ +#ifndef MYSQL_CLIENT_PLUGIN_INCLUDED +/* Copyright (C) 2010 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 */ + +/** + @file + + MySQL Client Plugin API + + This file defines the API for plugins that work on the client side +*/ +#define MYSQL_CLIENT_PLUGIN_INCLUDED + +#include +#include + +/* known plugin types */ +#define MYSQL_CLIENT_reserved1 0 +#define MYSQL_CLIENT_reserved2 1 +#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN 2 + +#define MYSQL_CLIENT_AUTHENTICATION_PLUGIN_INTERFACE_VERSION 0x0100 + +#define MYSQL_CLIENT_MAX_PLUGINS 3 + +#define mysql_declare_client_plugin(X) \ + MYSQL_PLUGIN_EXPORT struct st_mysql_client_plugin_ ## X \ + _mysql_client_plugin_declaration_ = { \ + MYSQL_CLIENT_ ## X ## _PLUGIN, \ + MYSQL_CLIENT_ ## X ## _PLUGIN_INTERFACE_VERSION, +#define mysql_end_client_plugin } + +/* generic plugin header structure */ +#define MYSQL_CLIENT_PLUGIN_HEADER \ + int type; \ + unsigned int interface_version; \ + const char *name; \ + const char *author; \ + const char *desc; \ + unsigned int version[3]; \ + int (*init)(char *, size_t, int, va_list); \ + int (*deinit)(); + +struct st_mysql_client_plugin +{ + MYSQL_CLIENT_PLUGIN_HEADER +}; + +struct st_mysql; + +/******** authentication plugin specific declarations *********/ +#include + +struct st_mysql_client_plugin_AUTHENTICATION +{ + MYSQL_CLIENT_PLUGIN_HEADER + int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql); +}; + +/******** using plugins ************/ + +/** + loads a plugin and initializes it + + @param mysql MYSQL structure. only MYSQL_PLUGIN_DIR option value is used, + and last_errno/last_error, for error reporting + @param name a name of the plugin to load + @param type type of plugin that should be loaded, -1 to disable type check + @param argc number of arguments to pass to the plugin initialization + function + @param ... arguments for the plugin initialization function + + @retval + a pointer to the loaded plugin, or NULL in case of a failure +*/ +struct st_mysql_client_plugin * +mysql_load_plugin(struct st_mysql *mysql, const char *name, int type, + int argc, ...); + +/** + loads a plugin and initializes it, taking va_list as an argument + + This is the same as mysql_load_plugin, but take va_list instead of + a list of arguments. + + @param mysql MYSQL structure. only MYSQL_PLUGIN_DIR option value is used, + and last_errno/last_error, for error reporting + @param name a name of the plugin to load + @param type type of plugin that should be loaded, -1 to disable type check + @param argc number of arguments to pass to the plugin initialization + function + @param args arguments for the plugin initialization function + + @retval + a pointer to the loaded plugin, or NULL in case of a failure +*/ +struct st_mysql_client_plugin * +mysql_load_plugin_v(struct st_mysql *mysql, const char *name, int type, + int argc, va_list args); + +/** + finds an already loaded plugin by name, or loads it, if necessary + + @param mysql MYSQL structure. only MYSQL_PLUGIN_DIR option value is used, + and last_errno/last_error, for error reporting + @param name a name of the plugin to load + @param type type of plugin that should be loaded + + @retval + a pointer to the plugin, or NULL in case of a failure +*/ +struct st_mysql_client_plugin * +mysql_client_find_plugin(struct st_mysql *mysql, const char *name, int type); + +/** + adds a plugin structure to the list of loaded plugins + + This is useful if an application has the necessary functionality + (for example, a special load data handler) statically linked into + the application binary. It can use this function to register the plugin + directly, avoiding the need to factor it out into a shared object. + + @param mysql MYSQL structure. It is only used for error reporting + @param plugin an st_mysql_client_plugin structure to register + + @retval + a pointer to the plugin, or NULL in case of a failure +*/ +struct st_mysql_client_plugin * +mysql_client_register_plugin(struct st_mysql *mysql, + struct st_mysql_client_plugin *plugin); + +#endif + diff --git a/include/mysql/client_plugin.h.pp b/include/mysql/client_plugin.h.pp new file mode 100644 index 00000000000..20d353422dd --- /dev/null +++ b/include/mysql/client_plugin.h.pp @@ -0,0 +1,41 @@ +#include +#include +struct st_mysql_client_plugin +{ + int type; unsigned int interface_version; const char *name; const char *author; const char *desc; unsigned int version[3]; int (*init)(char *, size_t, int, va_list); int (*deinit)(); +}; +struct st_mysql; +#include +typedef struct st_plugin_vio_info +{ + enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET, + MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol; + int socket; +} MYSQL_PLUGIN_VIO_INFO; +typedef struct st_plugin_vio +{ + int (*read_packet)(struct st_plugin_vio *vio, + unsigned char **buf); + int (*write_packet)(struct st_plugin_vio *vio, + const unsigned char *packet, + int packet_len); + void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info); +} MYSQL_PLUGIN_VIO; +struct st_mysql_client_plugin_AUTHENTICATION +{ + int type; unsigned int interface_version; const char *name; const char *author; const char *desc; unsigned int version[3]; int (*init)(char *, size_t, int, va_list); int (*deinit)(); + int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, struct st_mysql *mysql); +}; +typedef char *(*mysql_authentication_dialog_ask_t)(struct st_mysql *mysql, + int type, const char *prompt, char *buf, int buf_len); +struct st_mysql_client_plugin * +mysql_load_plugin(struct st_mysql *mysql, const char *name, int type, + int argc, ...); +struct st_mysql_client_plugin * +mysql_load_plugin_v(struct st_mysql *mysql, const char *name, int type, + int argc, va_list args); +struct st_mysql_client_plugin * +mysql_client_find_plugin(struct st_mysql *mysql, const char *name, int type); +struct st_mysql_client_plugin * +mysql_client_register_plugin(struct st_mysql *mysql, + struct st_mysql_client_plugin *plugin); diff --git a/include/mysql/plugin.h b/include/mysql/plugin.h index 19cf0ed050d..01ca76983a6 100644 --- a/include/mysql/plugin.h +++ b/include/mysql/plugin.h @@ -83,7 +83,8 @@ typedef struct st_mysql_xid MYSQL_XID; #define MYSQL_INFORMATION_SCHEMA_PLUGIN 4 /* The I_S plugin type */ #define MYSQL_AUDIT_PLUGIN 5 /* The Audit plugin type */ #define MYSQL_REPLICATION_PLUGIN 6 /* The replication plugin type */ -#define MYSQL_MAX_PLUGIN_TYPE_NUM 7 /* The number of plugin types */ +#define MYSQL_AUTHENTICATION_PLUGIN 7 /* The authentication plugin type */ +#define MYSQL_MAX_PLUGIN_TYPE_NUM 8 /* The number of plugin types */ /* We use the following strings to define licenses for plugins */ #define PLUGIN_LICENSE_PROPRIETARY 0 diff --git a/include/mysql/plugin_auth.h b/include/mysql/plugin_auth.h new file mode 100644 index 00000000000..8fed53c59fa --- /dev/null +++ b/include/mysql/plugin_auth.h @@ -0,0 +1,118 @@ +#ifndef MYSQL_PLUGIN_AUTH_INCLUDED +/* Copyright (C) 2010 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 */ + +/** + @file + + Authentication Plugin API. + + This file defines the API for server authentication plugins. +*/ + +#define MYSQL_PLUGIN_AUTH_INCLUDED + +#include + +#define MYSQL_AUTHENTICATION_INTERFACE_VERSION 0x0100 + +#include + +/** + Provides server plugin access to authentication information +*/ +typedef struct st_mysql_server_auth_info +{ + /** + User name as sent by the client and shown in USER(). + NULL if the client packet with the user name was not received yet. + */ + char *user_name; + + /** + Length of user_name + */ + unsigned int user_name_length; + + /** + A corresponding column value from the mysql.user table for the + matching account name + */ + const char *auth_string; + + /** + Length of auth_string + */ + unsigned long auth_string_length; + + /** + Matching account name as found in the mysql.user table. + A plugin can override it with another name that will be + used by MySQL for authorization, and shown in CURRENT_USER() + */ + char authenticated_as[MYSQL_USERNAME_LENGTH+1]; + + + /** + The unique user name that was used by the plugin to authenticate. + Plugins should put null-terminated UTF-8 here. + Available through the @@EXTERNAL_USER variable. + */ + char external_user[512]; + + /** + This only affects the "Authentication failed. Password used: %s" + error message. has the following values : + 0 : %s will be NO. + 1 : %s will be YES. + 2 : there will be no %s. + Set it as appropriate or ignore at will. + */ + int password_used; + + /** + Set to the name of the connected client if it can be resolved, or to + the address otherwise + */ + const char *host_or_ip; + + /** + Length of host_or_ip + */ + unsigned int host_or_ip_length; + +} MYSQL_SERVER_AUTH_INFO; + +/** + Server authentication plugin descriptor +*/ +struct st_mysql_auth +{ + int interface_version; /**< version plugin uses */ + /** + A plugin that a client must use for authentication with this server + plugin. Can be NULL to mean "any plugin". + */ + const char *client_auth_plugin; + /** + Function provided by the plugin which should perform authentication (using + the vio functions if necessary) and return 0 if successful. The plugin can + also fill the info.authenticated_as field if a different username should be + used for authorization. + */ + int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info); +}; +#endif + diff --git a/include/mysql/plugin_auth.h.pp b/include/mysql/plugin_auth.h.pp new file mode 100644 index 00000000000..550e2e852a7 --- /dev/null +++ b/include/mysql/plugin_auth.h.pp @@ -0,0 +1,205 @@ +#include +#include +#include +#include +#include +extern struct my_snprintf_service_st { + size_t (*my_snprintf_type)(char*, size_t, const char*, ...); + size_t (*my_vsnprintf_type)(char *, size_t, const char*, va_list); +} *my_snprintf_service; +size_t my_snprintf(char* to, size_t n, const char* fmt, ...); +size_t my_vsnprintf(char *to, size_t n, const char* fmt, va_list ap); +#include +#include +struct st_mysql_lex_string +{ + char *str; + size_t length; +}; +typedef struct st_mysql_lex_string MYSQL_LEX_STRING; +extern struct thd_alloc_service_st { + void *(*thd_alloc_func)(void*, unsigned int); + void *(*thd_calloc_func)(void*, unsigned int); + char *(*thd_strdup_func)(void*, const char *); + char *(*thd_strmake_func)(void*, const char *, unsigned int); + void *(*thd_memdup_func)(void*, const void*, unsigned int); + MYSQL_LEX_STRING *(*thd_make_lex_string_func)(void*, MYSQL_LEX_STRING *, + const char *, unsigned int, int); +} *thd_alloc_service; +void *thd_alloc(void* thd, unsigned int size); +void *thd_calloc(void* thd, unsigned int size); +char *thd_strdup(void* thd, const char *str); +char *thd_strmake(void* thd, const char *str, unsigned int size); +void *thd_memdup(void* thd, const void* str, unsigned int size); +MYSQL_LEX_STRING *thd_make_lex_string(void* thd, MYSQL_LEX_STRING *lex_str, + const char *str, unsigned int size, + int allocate_lex_string); +struct st_mysql_xid { + long formatID; + long gtrid_length; + long bqual_length; + char data[128]; +}; +typedef struct st_mysql_xid MYSQL_XID; +enum enum_mysql_show_type +{ + SHOW_UNDEF, SHOW_BOOL, SHOW_INT, SHOW_LONG, + SHOW_LONGLONG, SHOW_CHAR, SHOW_CHAR_PTR, + SHOW_ARRAY, SHOW_FUNC, SHOW_DOUBLE, + SHOW_always_last +}; +struct st_mysql_show_var { + const char *name; + char *value; + enum enum_mysql_show_type type; +}; +typedef int (*mysql_show_var_func)(void*, struct st_mysql_show_var*, char *); +struct st_mysql_sys_var; +struct st_mysql_value; +typedef int (*mysql_var_check_func)(void* thd, + struct st_mysql_sys_var *var, + void *save, struct st_mysql_value *value); +typedef void (*mysql_var_update_func)(void* thd, + struct st_mysql_sys_var *var, + void *var_ptr, const void *save); +struct st_mysql_plugin +{ + int type; + void *info; + const char *name; + const char *author; + const char *descr; + int license; + int (*init)(void *); + int (*deinit)(void *); + unsigned int version; + struct st_mysql_show_var *status_vars; + struct st_mysql_sys_var **system_vars; + void * __reserved1; +}; +#include "plugin_ftparser.h" +#include "plugin.h" +enum enum_ftparser_mode +{ + MYSQL_FTPARSER_SIMPLE_MODE= 0, + MYSQL_FTPARSER_WITH_STOPWORDS= 1, + MYSQL_FTPARSER_FULL_BOOLEAN_INFO= 2 +}; +enum enum_ft_token_type +{ + FT_TOKEN_EOF= 0, + FT_TOKEN_WORD= 1, + FT_TOKEN_LEFT_PAREN= 2, + FT_TOKEN_RIGHT_PAREN= 3, + FT_TOKEN_STOPWORD= 4 +}; +typedef struct st_mysql_ftparser_boolean_info +{ + enum enum_ft_token_type type; + int yesno; + int weight_adjust; + char wasign; + char trunc; + char prev; + char *quot; +} MYSQL_FTPARSER_BOOLEAN_INFO; +typedef struct st_mysql_ftparser_param +{ + int (*mysql_parse)(struct st_mysql_ftparser_param *, + char *doc, int doc_len); + int (*mysql_add_word)(struct st_mysql_ftparser_param *, + char *word, int word_len, + MYSQL_FTPARSER_BOOLEAN_INFO *boolean_info); + void *ftparser_state; + void *mysql_ftparam; + struct charset_info_st *cs; + char *doc; + int length; + int flags; + enum enum_ftparser_mode mode; +} MYSQL_FTPARSER_PARAM; +struct st_mysql_ftparser +{ + int interface_version; + int (*parse)(MYSQL_FTPARSER_PARAM *param); + int (*init)(MYSQL_FTPARSER_PARAM *param); + int (*deinit)(MYSQL_FTPARSER_PARAM *param); +}; +struct st_mysql_daemon +{ + int interface_version; +}; +struct st_mysql_information_schema +{ + int interface_version; +}; +struct st_mysql_storage_engine +{ + int interface_version; +}; +struct handlerton; + struct Mysql_replication { + int interface_version; + }; +struct st_mysql_value +{ + int (*value_type)(struct st_mysql_value *); + const char *(*val_str)(struct st_mysql_value *, char *buffer, int *length); + int (*val_real)(struct st_mysql_value *, double *realbuf); + int (*val_int)(struct st_mysql_value *, long long *intbuf); + int (*is_unsigned)(struct st_mysql_value *); +}; +int thd_in_lock_tables(const void* thd); +int thd_tablespace_op(const void* thd); +long long thd_test_options(const void* thd, long long test_options); +int thd_sql_command(const void* thd); +const char *thd_proc_info(void* thd, const char *info); +void **thd_ha_data(const void* thd, const struct handlerton *hton); +int thd_tx_isolation(const void* thd); +char *thd_security_context(void* thd, char *buffer, unsigned int length, + unsigned int max_query_len); +void thd_inc_row_count(void* thd); +int mysql_tmpfile(const char *prefix); +int thd_killed(const void* thd); +unsigned long thd_get_thread_id(const void* thd); +void thd_get_xid(const void* thd, MYSQL_XID *xid); +void mysql_query_cache_invalidate4(void* thd, + const char *key, unsigned int key_length, + int using_trx); +void *thd_get_ha_data(const void* thd, const struct handlerton *hton); +void thd_set_ha_data(void* thd, const struct handlerton *hton, + const void *ha_data); +#include +typedef struct st_plugin_vio_info +{ + enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET, + MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol; + int socket; +} MYSQL_PLUGIN_VIO_INFO; +typedef struct st_plugin_vio +{ + int (*read_packet)(struct st_plugin_vio *vio, + unsigned char **buf); + int (*write_packet)(struct st_plugin_vio *vio, + const unsigned char *packet, + int packet_len); + void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info); +} MYSQL_PLUGIN_VIO; +typedef struct st_mysql_server_auth_info +{ + const char *user_name; + unsigned int user_name_length; + const char *auth_string; + unsigned long auth_string_length; + char authenticated_as[48 +1]; + char external_user[512]; + int password_used; + const char *host_or_ip; + unsigned int host_or_ip_length; +} MYSQL_SERVER_AUTH_INFO; +struct st_mysql_auth +{ + int interface_version; + const char *client_auth_plugin; + int (*authenticate_user)(MYSQL_PLUGIN_VIO *vio, MYSQL_SERVER_AUTH_INFO *info); +}; diff --git a/include/mysql/plugin_auth_common.h b/include/mysql/plugin_auth_common.h new file mode 100644 index 00000000000..4ad92d01bfb --- /dev/null +++ b/include/mysql/plugin_auth_common.h @@ -0,0 +1,105 @@ +#ifndef MYSQL_PLUGIN_AUTH_COMMON_INCLUDED +/* Copyright (C) 2010 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 */ + +/** + @file + + This file defines constants and data structures that are the same for + both client- and server-side authentication plugins. +*/ +#define MYSQL_PLUGIN_AUTH_COMMON_INCLUDED + +/** the max allowed length for a user name */ +#define MYSQL_USERNAME_LENGTH 48 + +/** + return values of the plugin authenticate_user() method. +*/ + +/** + Authentication failed. Additionally, all other CR_xxx values + (libmysql error code) can be used too. + + The client plugin may set the error code and the error message directly + in the MYSQL structure and return CR_ERROR. If a CR_xxx specific error + code was returned, an error message in the MYSQL structure will be + overwritten. If CR_ERROR is returned without setting the error in MYSQL, + CR_UNKNOWN_ERROR will be user. +*/ +#define CR_ERROR 0 +/** + Authentication (client part) was successful. It does not mean that the + authentication as a whole was successful, usually it only means + that the client was able to send the user name and the password to the + server. If CR_OK is returned, the libmysql reads the next packet expecting + it to be one of OK, ERROR, or CHANGE_PLUGIN packets. +*/ +#define CR_OK -1 +/** + Authentication was successful. + It means that the client has done its part successfully and also that + a plugin has read the last packet (one of OK, ERROR, CHANGE_PLUGIN). + In this case, libmysql will not read a packet from the server, + but it will use the data at mysql->net.read_pos. + + A plugin may return this value if the number of roundtrips in the + authentication protocol is not known in advance, and the client plugin + needs to read one packet more to determine if the authentication is finished + or not. +*/ +#define CR_OK_HANDSHAKE_COMPLETE -2 + +typedef struct st_plugin_vio_info +{ + enum { MYSQL_VIO_INVALID, MYSQL_VIO_TCP, MYSQL_VIO_SOCKET, + MYSQL_VIO_PIPE, MYSQL_VIO_MEMORY } protocol; + int socket; /**< it's set, if the protocol is SOCKET or TCP */ +#ifdef _WIN32 + HANDLE handle; /**< it's set, if the protocol is PIPE or MEMORY */ +#endif +} MYSQL_PLUGIN_VIO_INFO; + +/** + Provides plugin access to communication channel +*/ +typedef struct st_plugin_vio +{ + /** + Plugin provides a pointer reference and this function sets it to the + contents of any incoming packet. Returns the packet length, or -1 if + the plugin should terminate. + */ + int (*read_packet)(struct st_plugin_vio *vio, + unsigned char **buf); + + /** + Plugin provides a buffer with data and the length and this + function sends it as a packet. Returns 0 on success, 1 on failure. + */ + int (*write_packet)(struct st_plugin_vio *vio, + const unsigned char *packet, + int packet_len); + + /** + Fills in a st_plugin_vio_info structure, providing the information + about the connection. + */ + void (*info)(struct st_plugin_vio *vio, struct st_plugin_vio_info *info); + +} MYSQL_PLUGIN_VIO; + +#endif + diff --git a/include/mysql_com.h b/include/mysql_com.h index 90fe4ac1995..d4223211710 100644 --- a/include/mysql_com.h +++ b/include/mysql_com.h @@ -162,9 +162,17 @@ enum enum_server_command #define CLIENT_MULTI_RESULTS (1UL << 17) /* Enable/disable multi-results */ #define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */ +#define CLIENT_PLUGIN_AUTH (1UL << 19) /* Client supports plugin authentication */ + #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30) #define CLIENT_REMEMBER_OPTIONS (1UL << 31) +#ifdef HAVE_COMPRESS +#define CAN_CLIENT_COMPRESS CLIENT_COMPRESS +#else +#define CAN_CLIENT_COMPRESS 0 +#endif + /* Gather all possible capabilites (flags) supported by the server */ #define CLIENT_ALL_FLAGS (CLIENT_LONG_PASSWORD | \ CLIENT_FOUND_ROWS | \ @@ -186,7 +194,8 @@ enum enum_server_command CLIENT_MULTI_RESULTS | \ CLIENT_PS_MULTI_RESULTS | \ CLIENT_SSL_VERIFY_SERVER_CERT | \ - CLIENT_REMEMBER_OPTIONS) + CLIENT_REMEMBER_OPTIONS | \ + CLIENT_PLUGIN_AUTH) /* Switch off the flags that are optional and depending on build flags @@ -518,14 +527,14 @@ void create_random_string(char *to, unsigned int length, struct rand_struct *ran void hash_password(unsigned long *to, const char *password, unsigned int password_len); void make_scrambled_password_323(char *to, const char *password); void scramble_323(char *to, const char *message, const char *password); -my_bool check_scramble_323(const char *, const char *message, +my_bool check_scramble_323(const unsigned char *reply, const char *message, unsigned long *salt); void get_salt_from_password_323(unsigned long *res, const char *password); void make_password_from_salt_323(char *to, const unsigned long *salt); void make_scrambled_password(char *to, const char *password); void scramble(char *to, const char *message, const char *password); -my_bool check_scramble(const char *reply, const char *message, +my_bool check_scramble(const unsigned char *reply, const char *message, const unsigned char *hash_stage2); void get_salt_from_password(unsigned char *res, const char *password); void make_password_from_salt(char *to, const unsigned char *hash_stage2); diff --git a/include/sql_common.h b/include/sql_common.h index 5fd8778d62b..a9a3168b691 100644 --- a/include/sql_common.h +++ b/include/sql_common.h @@ -16,14 +16,60 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#define SQL_COMMON_INCLUDED + +#ifdef __cplusplus +extern "C" { +#endif + +#include extern const char *unknown_sqlstate; extern const char *cant_connect_sqlstate; extern const char *not_error_sqlstate; -#ifdef __cplusplus -extern "C" { +struct st_mysql_options_extention { + char *plugin_dir; + char *default_auth; +}; + +typedef struct st_mysql_methods +{ + my_bool (*read_query_result)(MYSQL *mysql); + my_bool (*advanced_command)(MYSQL *mysql, + enum enum_server_command command, + const unsigned char *header, + unsigned long header_length, + const unsigned char *arg, + unsigned long arg_length, + my_bool skip_check, + MYSQL_STMT *stmt); + MYSQL_DATA *(*read_rows)(MYSQL *mysql,MYSQL_FIELD *mysql_fields, + unsigned int fields); + MYSQL_RES * (*use_result)(MYSQL *mysql); + void (*fetch_lengths)(unsigned long *to, + MYSQL_ROW column, unsigned int field_count); + void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results); + int (*read_change_user_result)(MYSQL *mysql); +#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) + MYSQL_FIELD * (*list_fields)(MYSQL *mysql); + my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt); + int (*stmt_execute)(MYSQL_STMT *stmt); + int (*read_binary_rows)(MYSQL_STMT *stmt); + int (*unbuffered_fetch)(MYSQL *mysql, char **row); + void (*free_embedded_thd)(MYSQL *mysql); + const char *(*read_statistics)(MYSQL *mysql); + my_bool (*next_result)(MYSQL *mysql); + int (*read_rows_from_cursor)(MYSQL_STMT *stmt); #endif +} MYSQL_METHODS; + +#define simple_command(mysql, command, arg, length, skip_check) \ + (*(mysql)->methods->advanced_command)(mysql, command, 0, \ + 0, arg, length, skip_check, NULL) +#define stmt_command(mysql, command, arg, length, stmt) \ + (*(mysql)->methods->advanced_command)(mysql, command, 0, \ + 0, arg, length, 1, stmt) extern CHARSET_INFO *default_client_charset_info; MYSQL_FIELD *unpack_fields(MYSQL_DATA *data,MEM_ROOT *alloc,uint fields, @@ -45,6 +91,19 @@ void set_stmt_errmsg(MYSQL_STMT *stmt, NET *net); void set_stmt_error(MYSQL_STMT *stmt, int errcode, const char *sqlstate, const char *err); void set_mysql_error(MYSQL *mysql, int errcode, const char *sqlstate); +void set_mysql_extended_error(MYSQL *mysql, int errcode, const char *sqlstate, + const char *format, ...); + +/* client side of the pluggable authentication */ +struct st_plugin_vio_info; +void mpvio_info(Vio *vio, struct st_plugin_vio_info *info); +int run_plugin_auth(MYSQL *mysql, char *data, uint data_len, + const char *data_plugin, const char *db); +int mysql_client_plugin_init(); +void mysql_client_plugin_deinit(); +struct st_mysql_client_plugin; +extern struct st_mysql_client_plugin *mysql_client_builtins[]; + #ifdef __cplusplus } #endif diff --git a/libmysql/CMakeLists.txt b/libmysql/CMakeLists.txt index 2ae09c1707a..f5fa1f7a009 100644 --- a/libmysql/CMakeLists.txt +++ b/libmysql/CMakeLists.txt @@ -140,6 +140,7 @@ SET(CLIENT_SOURCES errmsg.c ../sql-common/client.c ../sql-common/my_time.c + ../sql-common/client_plugin.c ../sql/net_serv.cc ../sql-common/pack.c ../sql/password.c @@ -148,7 +149,7 @@ ADD_CONVENIENCE_LIBRARY(clientlib ${CLIENT_SOURCES}) DTRACE_INSTRUMENT(clientlib) ADD_DEPENDENCIES(clientlib GenError) -SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES}) +SET(LIBS clientlib dbug strings vio mysys ${ZLIB_LIBRARY} ${SSL_LIBRARIES} ${LIBDL}) # Merge several convenience libraries into one big mysqlclient # and link them together into shared library. diff --git a/libmysql/Makefile.shared b/libmysql/Makefile.shared index 887af62229a..5a7236f1e6d 100644 --- a/libmysql/Makefile.shared +++ b/libmysql/Makefile.shared @@ -23,6 +23,7 @@ MYSQLDATAdir = $(localstatedir) MYSQLSHAREdir = $(pkgdatadir) MYSQLBASEdir= $(prefix) +pkgplugindir = $(pkglibdir)/plugin ## We'll use CLIENT_EXTRA_LDFLAGS for threaded and non-threaded ## until someone complains that they need separate options. LDADD = @CLIENT_EXTRA_LDFLAGS@ $(target) @@ -70,26 +71,27 @@ mysysobjects1 = my_init.lo my_static.lo my_malloc.lo \ my_rename.lo my_chsize.lo my_sync.lo \ my_getsystime.lo my_symlink2.lo mf_same.lo sqlobjects = net.lo -sql_cmn_objects = pack.lo client.lo my_time.lo +sql_cmn_objects = pack.lo client.lo my_time.lo client_plugin.lo # Not needed in the minimum library mysysobjects2 = my_lib.lo mf_qsort.lo mysysobjects = $(mysysobjects1) $(mysysobjects2) target_libadd = $(mysysobjects) $(mystringsobjects) $(dbugobjects) \ $(sql_cmn_objects) $(vio_objects) $(sqlobjects) -target_ldflags = -version-info @SHARED_LIB_VERSION@ @LD_VERSION_SCRIPT@ +target_ldflags = -version-info @SHARED_LIB_VERSION@ @LD_VERSION_SCRIPT@ @LIBDL@ vio_objects= vio.lo viosocket.lo viossl.lo viosslfactories.lo BUILT_SOURCES = link_sources CLEANFILES = $(target_libadd) $(SHLIBOBJS) \ $(target) $(BUILT_SOURCES) -DEFS = -DDEFAULT_CHARSET_HOME="\"$(MYSQLBASEdir)\"" \ - -DMYSQL_DATADIR="\"$(MYSQLDATAdir)\"" \ +DEFS = -DDEFAULT_CHARSET_HOME='"$(MYSQLBASEdir)"' \ + -DMYSQL_DATADIR='"$(MYSQLDATAdir)"' \ -DDEFAULT_HOME_ENV=MYSQL_HOME \ + -DPLUGINDIR='"$(pkgplugindir)"' \ -DDEFAULT_GROUP_SUFFIX_ENV=MYSQL_GROUP_SUFFIX \ - -DDEFAULT_SYSCONFDIR="\"$(sysconfdir)\"" \ - -DSHAREDIR="\"$(MYSQLSHAREdir)\"" -DDISABLE_DTRACE \ + -DDEFAULT_SYSCONFDIR='"$(sysconfdir)"' \ + -DSHAREDIR='"$(MYSQLSHAREdir)"' -DDISABLE_DTRACE \ $(target_defs) if HAVE_YASSL diff --git a/libmysql/client_settings.h b/libmysql/client_settings.h index aaec08d1b1e..5204d03e5af 100644 --- a/libmysql/client_settings.h +++ b/libmysql/client_settings.h @@ -28,7 +28,8 @@ extern char * mysql_unix_port; CLIENT_PROTOCOL_41 | \ CLIENT_SECURE_CONNECTION | \ CLIENT_MULTI_RESULTS | \ - CLIENT_PS_MULTI_RESULTS) + CLIENT_PS_MULTI_RESULTS | \ + CLIENT_PLUGIN_AUTH) sig_handler my_pipe_sig_handler(int sig); void read_user_name(char *name); @@ -67,7 +68,7 @@ int cli_stmt_execute(MYSQL_STMT *stmt); int cli_read_binary_rows(MYSQL_STMT *stmt); int cli_unbuffered_fetch(MYSQL *mysql, char **row); const char * cli_read_statistics(MYSQL *mysql); -int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd); +int cli_read_change_user_result(MYSQL *mysql); #ifdef EMBEDDED_LIBRARY int init_embedded_server(int argc, char **argv, char **groups); diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index febbded6af2..823f83026c9 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -84,7 +84,8 @@ const char *client_errors[]= "Lost connection to MySQL server at '%s', system error: %d", "Statement closed indirectly because of a preceeding %s() call", "The number of columns in the result set differs from the number of bound buffers. You must reset the statement, rebind the result set columns, and execute the statement again", - "This handle is already connected. Use a separate handle for each connection." + "This handle is already connected. Use a separate handle for each connection.", + "Authentication plugin '%s' cannot be loaded: %s", "" }; diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 550b1b7b107..c90af040c5f 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -126,6 +126,8 @@ int STDCALL mysql_server_init(int argc __attribute__((unused)), if (my_init()) /* Will init threads */ return 1; init_client_errs(); + if (mysql_client_plugin_init()) + return 1; if (!mysql_port) { char *env; @@ -196,6 +198,8 @@ void STDCALL mysql_server_end() if (!mysql_client_init) return; + mysql_client_plugin_deinit(); + #ifdef EMBEDDED_LIBRARY end_embedded_server(); #endif @@ -345,44 +349,14 @@ mysql_connect(MYSQL *mysql,const char *host, Change user and database **************************************************************************/ -int cli_read_change_user_result(MYSQL *mysql, char *buff, const char *passwd) -{ - NET *net= &mysql->net; - ulong pkt_length; - - pkt_length= cli_safe_read(mysql); - - if (pkt_length == packet_error) - return 1; - - if (pkt_length == 1 && net->read_pos[0] == 254 && - mysql->server_capabilities & CLIENT_SECURE_CONNECTION) - { - /* - By sending this very specific reply server asks us to send scrambled - password in old format. The reply contains scramble_323. - */ - scramble_323(buff, mysql->scramble, passwd); - if (my_net_write(net, (uchar*) buff, SCRAMBLE_LENGTH_323 + 1) || - net_flush(net)) - { - set_mysql_error(mysql, CR_SERVER_LOST, unknown_sqlstate); - return 1; - } - /* Read what server thinks about out new auth message report */ - if (cli_safe_read(mysql) == packet_error) - return 1; - } - return 0; -} - my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, const char *passwd, const char *db) { - char buff[USERNAME_LENGTH+SCRAMBLED_PASSWORD_CHAR_LENGTH+NAME_LEN+2]; - char *end= buff; int rc; CHARSET_INFO *saved_cs= mysql->charset; + char *saved_user= mysql->user; + char *saved_passwd= mysql->passwd; + char *saved_db= mysql->db; DBUG_ENTER("mysql_change_user"); @@ -396,49 +370,11 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, /* Use an empty string instead of NULL. */ - if (!user) - user=""; - if (!passwd) - passwd=""; + mysql->user= (char*)(user ? user : ""); + mysql->passwd= (char*)(passwd ? passwd : ""); + mysql->db= 0; - /* - Store user into the buffer. - Advance position as strmake returns a pointer to the closing NUL. - */ - end= strmake(end, user, USERNAME_LENGTH) + 1; - - /* write scrambled password according to server capabilities */ - if (passwd[0]) - { - if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) - { - *end++= SCRAMBLE_LENGTH; - scramble(end, mysql->scramble, passwd); - end+= SCRAMBLE_LENGTH; - } - else - { - scramble_323(end, mysql->scramble, passwd); - end+= SCRAMBLE_LENGTH_323 + 1; - } - } - else - *end++= '\0'; /* empty password */ - /* Add database if needed */ - end= strmake(end, db ? db : "", NAME_LEN) + 1; - - /* Add character set number. */ - - if (mysql->server_capabilities & CLIENT_SECURE_CONNECTION) - { - int2store(end, (ushort) mysql->charset->number); - end+= 2; - } - - /* Write authentication package */ - simple_command(mysql,COM_CHANGE_USER, (uchar*) buff, (ulong) (end-buff), 1); - - rc= (*mysql->methods->read_change_user_result)(mysql, buff, passwd); + rc= run_plugin_auth(mysql, 0, 0, 0, db); /* The server will close all statements no matter was the attempt @@ -448,18 +384,21 @@ my_bool STDCALL mysql_change_user(MYSQL *mysql, const char *user, if (rc == 0) { /* Free old connect information */ - my_free(mysql->user); - my_free(mysql->passwd); - my_free(mysql->db); + my_free(saved_user); + my_free(saved_passwd); + my_free(saved_db); /* alloc new connect information */ - mysql->user= my_strdup(user,MYF(MY_WME)); - mysql->passwd=my_strdup(passwd,MYF(MY_WME)); - mysql->db= db ? my_strdup(db,MYF(MY_WME)) : 0; + mysql->user= my_strdup(mysql->user, MYF(MY_WME)); + mysql->passwd= my_strdup(mysql->passwd, MYF(MY_WME)); + mysql->db= db ? my_strdup(db, MYF(MY_WME)) : 0; } else { mysql->charset= saved_cs; + mysql->user= saved_user; + mysql->passwd= saved_passwd; + mysql->db= saved_db; } DBUG_RETURN(rc); diff --git a/libmysqld/CMakeLists.txt b/libmysqld/CMakeLists.txt index a7efcb024ec..a658ee9d55e 100644 --- a/libmysqld/CMakeLists.txt +++ b/libmysqld/CMakeLists.txt @@ -42,6 +42,7 @@ SET(SQL_EMBEDDED_SOURCES emb_qcache.cc libmysqld.c lib_sql.cc ../libmysql/libmysql.c ../libmysql/errmsg.c ../client/get_password.c ../sql-common/client.c ../sql-common/my_time.c ../sql-common/my_user.c ../sql-common/pack.c + ../sql-common/client_plugin.c ../sql/password.c ../sql/discover.cc ../sql/derror.cc ../sql/field.cc ../sql/field_conv.cc ../sql/filesort.cc ../sql/gstream.cc diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 1ffa349bcfe..e2521bfcac7 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -43,7 +43,7 @@ pkglib_LIBRARIES = libmysqld.a SUBDIRS = . examples libmysqld_sources= libmysqld.c lib_sql.cc emb_qcache.cc libmysqlsources = errmsg.c get_password.c libmysql.c client.c pack.c \ - my_time.c + my_time.c client_plugin.c noinst_HEADERS = embedded_priv.h emb_qcache.h diff --git a/libmysqld/embedded_priv.h b/libmysqld/embedded_priv.h index 369b344d4bd..c246693594b 100644 --- a/libmysqld/embedded_priv.h +++ b/libmysqld/embedded_priv.h @@ -15,6 +15,8 @@ /* Prototypes for the embedded version of MySQL */ +#include + C_MODE_START void lib_connection_phase(NET *net, int phase); void init_embedded_mysql(MYSQL *mysql, int client_flag); diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc index 94927c590cf..a1a57e70499 100644 --- a/libmysqld/lib_sql.cc +++ b/libmysqld/lib_sql.cc @@ -35,7 +35,6 @@ C_MODE_START #include #undef ER #include "errmsg.h" -#include #include "embedded_priv.h" extern unsigned int mysql_server_last_errno; @@ -414,11 +413,10 @@ static MYSQL_RES * emb_store_result(MYSQL *mysql) return mysql_store_result(mysql); } -int emb_read_change_user_result(MYSQL *mysql, - char *buff __attribute__((unused)), - const char *passwd __attribute__((unused))) +int emb_read_change_user_result(MYSQL *mysql) { - return mysql_errno(mysql); + mysql->net.read_pos= (uchar*)""; // fake an OK packet + return mysql_errno(mysql) ? packet_error : 1 /* length of the OK packet */; } MYSQL_METHODS embedded_methods= @@ -429,6 +427,7 @@ MYSQL_METHODS embedded_methods= emb_store_result, emb_fetch_lengths, emb_flush_use_result, + emb_read_change_user_result, emb_list_fields, emb_read_prepare_result, emb_stmt_execute, @@ -437,7 +436,6 @@ MYSQL_METHODS embedded_methods= emb_free_embedded_thd, emb_read_statistics, emb_read_query_result, - emb_read_change_user_result, emb_read_rows_from_cursor }; @@ -601,6 +599,7 @@ void init_embedded_mysql(MYSQL *mysql, int client_flag) THD *thd = (THD *)mysql->thd; thd->mysql= mysql; mysql->server_version= server_version; + mysql->client_flag= client_flag; init_alloc_root(&mysql->field_alloc, 8192, 0); } @@ -664,14 +663,20 @@ err: int check_embedded_connection(MYSQL *mysql, const char *db) { int result; + LEX_STRING db_str = { (char*)db, db ? strlen(db) : 0 }; THD *thd= (THD*)mysql->thd; thd_init_client_charset(thd, mysql->charset->number); thd->update_charset(); Security_context *sctx= thd->security_ctx; sctx->host_or_ip= sctx->host= (char*) my_localhost; strmake(sctx->priv_host, (char*) my_localhost, MAX_HOSTNAME-1); - sctx->priv_user= sctx->user= my_strdup(mysql->user, MYF(0)); - result= check_user(thd, COM_CONNECT, NULL, 0, db, true); + strmake(sctx->priv_user, mysql->user, USERNAME_LENGTH-1); + sctx->user= my_strdup(mysql->user, MYF(0)); + sctx->proxy_user[0]= 0; + sctx->master_access= GLOBAL_ACLS; // Full rights + /* Change database if necessary */ + if (!(result= (db && db[0] && mysql_change_db(thd, &db_str, FALSE)))) + my_ok(thd); thd->protocol->end_statement(); emb_read_query_result(mysql); return result; @@ -680,14 +685,15 @@ int check_embedded_connection(MYSQL *mysql, const char *db) #else int check_embedded_connection(MYSQL *mysql, const char *db) { + /* + we emulate a COM_CHANGE_USER user here, + it's easier than to emulate the complete 3-way handshake + */ + char buf[USERNAME_LENGTH + SCRAMBLE_LENGTH + 1 + 2*NAME_LEN + 2], *end; + NET *net= &mysql->net; THD *thd= (THD*)mysql->thd; Security_context *sctx= thd->security_ctx; - int result; - char scramble_buff[SCRAMBLE_LENGTH]; - int passwd_len; - thd_init_client_charset(thd, mysql->charset->number); - thd->update_charset(); if (mysql->options.client_ip) { sctx->host= my_strdup(mysql->options.client_ip, MYF(0)); @@ -698,37 +704,43 @@ int check_embedded_connection(MYSQL *mysql, const char *db) sctx->host_or_ip= sctx->host; if (acl_check_host(sctx->host, sctx->ip)) - { - result= ER_HOST_NOT_PRIVILEGED; goto err; - } - sctx->user= my_strdup(mysql->user, MYF(0)); + /* construct a COM_CHANGE_USER packet */ + end= strmake(buf, mysql->user, USERNAME_LENGTH) + 1; + + memset(thd->scramble, 55, SCRAMBLE_LENGTH); // dummy scramble + thd->scramble[SCRAMBLE_LENGTH]= 0; + if (mysql->passwd && mysql->passwd[0]) { - memset(thd->scramble, 55, SCRAMBLE_LENGTH); // dummy scramble - thd->scramble[SCRAMBLE_LENGTH]= 0; - scramble(scramble_buff, thd->scramble, mysql->passwd); - passwd_len= SCRAMBLE_LENGTH; + *end++= SCRAMBLE_LENGTH; + scramble(end, thd->scramble, mysql->passwd); + end+= SCRAMBLE_LENGTH; } else - passwd_len= 0; + *end++= 0; - if((result= check_user(thd, COM_CONNECT, - scramble_buff, passwd_len, db, true))) - goto err; + end= strmake(end, db ? db : "", NAME_LEN) + 1; + int2store(end, (ushort) mysql->charset->number); + end+= 2; + + /* acl_authenticate() takes the data from thd->net->read_pos */ + thd->net.read_pos= (uchar*)buf; + + if (acl_authenticate(thd, 0, end - buf)) + { + x_free(thd->security_ctx->user); + goto err; + } return 0; err: - { - NET *net= &mysql->net; - strmake(net->last_error, thd->stmt_da->message(), - sizeof(net->last_error)-1); - memcpy(net->sqlstate, - mysql_errno_to_sqlstate(thd->stmt_da->sql_errno()), - sizeof(net->sqlstate)-1); - } - return result; + strmake(net->last_error, thd->main_da.message(), sizeof(net->last_error)-1); + memcpy(net->sqlstate, + mysql_errno_to_sqlstate(thd->main_da.sql_errno()), + sizeof(net->sqlstate)-1); + return 1; } #endif diff --git a/libmysqld/libmysqld.c b/libmysqld/libmysqld.c index 603fc3bc2f0..f6f0e1ba3db 100644 --- a/libmysqld/libmysqld.c +++ b/libmysqld/libmysqld.c @@ -17,7 +17,6 @@ #include #include #include -#include "embedded_priv.h" #include #include #include @@ -28,6 +27,7 @@ #include #include #include +#include "embedded_priv.h" #include "client_settings.h" #ifdef HAVE_PWD_H #include @@ -165,7 +165,11 @@ mysql_real_connect(MYSQL *mysql,const char *host, const char *user, client_flag|=CLIENT_CAPABILITIES; if (client_flag & CLIENT_MULTI_STATEMENTS) client_flag|= CLIENT_MULTI_RESULTS; - client_flag&= ~CLIENT_COMPRESS; + /* + no compression in embedded as we don't send any data, + and no pluggable auth, as we cannot do a client-server dialog + */ + client_flag&= ~(CLIENT_COMPRESS | CLIENT_PLUGIN_AUTH); if (db) client_flag|=CLIENT_CONNECT_WITH_DB; diff --git a/mysql-test/include/have_plugin_auth.inc b/mysql-test/include/have_plugin_auth.inc new file mode 100644 index 00000000000..41f11419c64 --- /dev/null +++ b/mysql-test/include/have_plugin_auth.inc @@ -0,0 +1,4 @@ +disable_query_log; +--require r/true.require +select (PLUGIN_LIBRARY LIKE 'auth_test_plugin%') as `TRUE` FROM INFORMATION_SCHEMA.PLUGINS + WHERE PLUGIN_NAME='test_plugin_server'; diff --git a/mysql-test/mysql-test-run.pl b/mysql-test/mysql-test-run.pl index 1a574fe6e6b..a23a8ee7261 100755 --- a/mysql-test/mysql-test-run.pl +++ b/mysql-test/mysql-test-run.pl @@ -126,6 +126,9 @@ my $path_vardir_trace; # unix formatted opt_vardir for trace files my $opt_tmpdir; # Path to use for tmp/ dir my $opt_tmpdir_pid; +my $auth_filename; # the name of the authentication test plugin +my $auth_plugin; # the path to the authentication test plugin + END { if ( defined $opt_tmpdir_pid and $opt_tmpdir_pid == $$ ) { @@ -1025,6 +1028,22 @@ sub command_line_setup { "$basedir/sql/share/charsets", "$basedir/share/charsets"); + # Look for client test plugin + if (IS_WINDOWS) + { + $auth_filename = "auth_test_plugin.dll"; + } + else + { + $auth_filename = "auth_test_plugin.so"; + } + $auth_plugin= + mtr_file_exists(vs_config_dirs('plugin/auth/',$auth_filename), + "$basedir/plugin/auth/.libs/" . $auth_filename, + "$basedir/lib/mysql/plugin/" . $auth_filename, + "$basedir/lib/plugin/" . $auth_filename); + + if (using_extern()) { # Connect to the running mysqld and find out what it supports @@ -1896,6 +1915,24 @@ sub environment_setup { $ENV{'UDF_EXAMPLE_LIB_OPT'}= "--plugin-dir=". ($lib_udf_example ? dirname($lib_udf_example) : ""); + # -------------------------------------------------------------------------- + # Add the path where mysqld will find the auth test plugin (dialog.so/dll) + # -------------------------------------------------------------------------- + if ($auth_plugin) + { + $ENV{'PLUGIN_AUTH'}= basename($auth_plugin); + $ENV{'PLUGIN_AUTH_OPT'}= "--plugin-dir=".dirname($auth_plugin); + + $ENV{'PLUGIN_AUTH_LOAD'}="--plugin_load=test_plugin_server=".$auth_filename; + } + else + { + $ENV{'PLUGIN_AUTH'}= ""; + $ENV{'PLUGIN_AUTH_OPT'}="--plugin-dir="; + $ENV{'PLUGIN_AUTH_LOAD'}=""; + } + + # -------------------------------------------------------------------------- # Add the path where mysqld will find ha_example.so # -------------------------------------------------------------------------- @@ -4918,6 +4955,10 @@ sub start_mysqltest ($) { mtr_add_arg($args, "--tmpdir=%s", $opt_tmpdir); mtr_add_arg($args, "--character-sets-dir=%s", $path_charsetsdir); mtr_add_arg($args, "--logdir=%s/log", $opt_vardir); + if ($auth_plugin) + { + mtr_add_arg($args, "--plugin_dir=%s", dirname($auth_plugin)); + } # Log line number and time for each line in .test file mtr_add_arg($args, "--mark-progress") diff --git a/mysql-test/r/1st.result b/mysql-test/r/1st.result index ae9989ce563..e8562662bfd 100644 --- a/mysql-test/r/1st.result +++ b/mysql-test/r/1st.result @@ -21,6 +21,7 @@ ndb_binlog_index plugin proc procs_priv +proxy_priv servers slow_log tables_priv diff --git a/mysql-test/r/change_user.result b/mysql-test/r/change_user.result index 1ed7fcbb8fa..3021b5472bf 100644 --- a/mysql-test/r/change_user.result +++ b/mysql-test/r/change_user.result @@ -1,3 +1,39 @@ +grant select on test.* to test_nopw; +grant select on test.* to test_oldpw identified by password "09301740536db389"; +grant select on test.* to test_newpw identified by "newpw"; +select user(), current_user(), database(); +user() current_user() database() +root@localhost root@localhost test +select concat('<', user(), '>'), concat('<', current_user(), '>'), database(); +concat('<', user(), '>') concat('<', current_user(), '>') database() + test +select concat('<', user(), '>'), concat('<', current_user(), '>'), database(); +concat('<', user(), '>') concat('<', current_user(), '>') database() + NULL +select concat('<', user(), '>'), concat('<', current_user(), '>'), database(); +concat('<', user(), '>') concat('<', current_user(), '>') database() + NULL +select concat('<', user(), '>'), concat('<', current_user(), '>'), database(); +concat('<', user(), '>') concat('<', current_user(), '>') database() + NULL +select concat('<', user(), '>'), concat('<', current_user(), '>'), database(); +concat('<', user(), '>') concat('<', current_user(), '>') database() + NULL +select concat('<', user(), '>'), concat('<', current_user(), '>'), database(); +concat('<', user(), '>') concat('<', current_user(), '>') database() + test +select concat('<', user(), '>'), concat('<', current_user(), '>'), database(); +concat('<', user(), '>') concat('<', current_user(), '>') database() + test +select concat('<', user(), '>'), concat('<', current_user(), '>'), database(); +concat('<', user(), '>') concat('<', current_user(), '>') database() + test +select concat('<', user(), '>'), concat('<', current_user(), '>'), database(); +concat('<', user(), '>') concat('<', current_user(), '>') database() + test +drop user test_nopw; +drop user test_oldpw; +drop user test_newpw; Bug#20023 SELECT @@session.sql_big_selects; @@session.sql_big_selects diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 5e6c013bb38..bbd0273c1c6 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -15,6 +15,7 @@ ndb_binlog_index plugin proc procs_priv +proxy_priv servers slow_log tables_priv @@ -48,6 +49,7 @@ ndb_binlog_index plugin proc procs_priv +proxy_priv servers slow_log tables_priv @@ -89,6 +91,7 @@ ndb_binlog_index plugin proc procs_priv +proxy_priv servers slow_log tables_priv diff --git a/mysql-test/r/events_bugs.result b/mysql-test/r/events_bugs.result index a5003c936e8..a27c7dc18ee 100644 --- a/mysql-test/r/events_bugs.result +++ b/mysql-test/r/events_bugs.result @@ -568,6 +568,7 @@ USE test; SHOW GRANTS FOR CURRENT_USER; Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION SET GLOBAL event_scheduler = ON; CREATE TABLE events_test.event_log (id int KEY AUTO_INCREMENT, ev_nm char(40), ev_cnt int, ev_tm timestamp) diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index 65ebbd71c39..84cac386b57 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -13,8 +13,48 @@ GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3 GRANT SELECT ON `mysqltest`.* TO 'mysqltest_1'@'localhost' grant delete on mysqltest.* to mysqltest_1@localhost; select * from mysql.user where user="mysqltest_1"; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N SPECIFIED EDH-RSA-DES-CBC3-SHA 0 0 0 0 +Host localhost +User mysqltest_1 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type SPECIFIED +ssl_cipher EDH-RSA-DES-CBC3-SHA +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' REQUIRE CIPHER 'EDH-RSA-DES-CBC3-SHA' @@ -44,15 +84,95 @@ delete from mysql.user where user='mysqltest_1'; flush privileges; grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 10; select * from mysql.user where user="mysqltest_1"; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 10 0 0 0 +Host localhost +User mysqltest_1 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 10 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 20 max_connections_per_hour 30; select * from mysql.user where user="mysqltest_1"; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 10 20 30 0 +Host localhost +User mysqltest_1 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 10 +max_updates 20 +max_connections 30 +max_user_connections 0 +plugin +authentication_string show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' WITH MAX_QUERIES_PER_HOUR 10 MAX_UPDATES_PER_HOUR 20 MAX_CONNECTIONS_PER_HOUR 30 @@ -164,6 +284,7 @@ Warnings: Warning 1364 Field 'ssl_cipher' doesn't have a default value Warning 1364 Field 'x509_issuer' doesn't have a default value Warning 1364 Field 'x509_subject' doesn't have a default value +Warning 1364 Field 'authentication_string' doesn't have a default value insert into mysql.db (host, db, user, select_priv) values ('localhost', 'a%', 'test11', 'Y'), ('localhost', 'ab%', 'test11', 'Y'); alter table mysql.db order by db asc; @@ -625,16 +746,19 @@ show grants for root@localhost; Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION GRANT SELECT ON `ÂÄ`.* TO 'root'@'localhost' +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION flush privileges; show grants for root@localhost; Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION GRANT SELECT ON `ÂÄ`.* TO 'root'@'localhost' +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION drop database ÂÄ; revoke all privileges on ÂÄ.* from root@localhost; show grants for root@localhost; Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION set names latin1; create user mysqltest_7@; set password for mysqltest_7@ = password('systpass'); diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index 3032ca854bd..b5e82794658 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -11,7 +11,7 @@ grant create user on *.* to mysqltest_1@localhost; create user mysqltest_2@localhost; grant select on `my\_1`.* to mysqltest_2@localhost; grant select on `my\_1`.* to mysqltest_2@localhost identified by 'pass'; -ERROR 42000: You must have privileges to update tables in the mysql database to be able to change passwords for others +ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysql' grant update on mysql.* to mysqltest_1@localhost; grant select on `my\_1`.* to mysqltest_2@localhost identified by 'pass'; grant select on `my\_1`.* to mysqltest_3@localhost; @@ -287,6 +287,7 @@ Warnings: Warning 1364 Field 'ssl_cipher' doesn't have a default value Warning 1364 Field 'x509_issuer' doesn't have a default value Warning 1364 Field 'x509_subject' doesn't have a default value +Warning 1364 Field 'authentication_string' doesn't have a default value create user mysqltest_A@'%'; rename user mysqltest_B@'%' to mysqltest_C@'%'; drop user mysqltest_C@'%'; @@ -334,7 +335,7 @@ delete from mysql.user where user like 'mysqltest\_1'; flush privileges; drop database mysqltest_1; set password = password("changed"); -ERROR 42000: Access denied for user ''@'localhost' to database 'mysql' +ERROR 42000: Can't find any matching row in the user table lock table mysql.user write; flush privileges; grant all on *.* to 'mysqltest_1'@'localhost'; @@ -354,6 +355,7 @@ Warnings: Warning 1364 Field 'ssl_cipher' doesn't have a default value Warning 1364 Field 'x509_issuer' doesn't have a default value Warning 1364 Field 'x509_subject' doesn't have a default value +Warning 1364 Field 'authentication_string' doesn't have a default value INSERT INTO mysql.db (host, db, user, select_priv) VALUES ('%','TESTDB','mysqltest_1','Y'); FLUSH PRIVILEGES; diff --git a/mysql-test/r/grant_cache_no_prot.result b/mysql-test/r/grant_cache_no_prot.result index 32bb9cce90e..019edb72086 100644 --- a/mysql-test/r/grant_cache_no_prot.result +++ b/mysql-test/r/grant_cache_no_prot.result @@ -7,9 +7,11 @@ flush status; show grants for current_user; Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION show grants; Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION create database if not exists mysqltest; create table mysqltest.t1 (a int,b int,c int); create table mysqltest.t2 (a int,b int,c int); diff --git a/mysql-test/r/grant_cache_ps_prot.result b/mysql-test/r/grant_cache_ps_prot.result index 281468ee2e1..e95a858fd9a 100644 --- a/mysql-test/r/grant_cache_ps_prot.result +++ b/mysql-test/r/grant_cache_ps_prot.result @@ -7,9 +7,11 @@ flush status; show grants for current_user; Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION show grants; Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION create database if not exists mysqltest; create table mysqltest.t1 (a int,b int,c int); create table mysqltest.t2 (a int,b int,c int); diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 21b42577a11..aa47b8c437e 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -88,6 +88,7 @@ host plugin proc procs_priv +proxy_priv servers slow_log tables_priv @@ -684,6 +685,7 @@ Alter_routine_priv select,insert,update,references max_questions select,insert,update,references max_connections select,insert,update,references max_user_connections select,insert,update,references +authentication_string select,insert,update,references use test; create function sub1(i int) returns int return i+1; @@ -870,7 +872,7 @@ AND table_name not like 'ndb%' AND table_name not like 'innodb_%' GROUP BY TABLE_SCHEMA; table_schema count(*) information_schema 30 -mysql 22 +mysql 23 create table t1 (i int, j int); create trigger trg1 before insert on t1 for each row begin diff --git a/mysql-test/r/log_tables_upgrade.result b/mysql-test/r/log_tables_upgrade.result index 5d9be85a48a..10cc6d71eae 100644 --- a/mysql-test/r/log_tables_upgrade.result +++ b/mysql-test/r/log_tables_upgrade.result @@ -29,6 +29,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.proxy_priv OK mysql.renamed_general_log OK mysql.servers OK mysql.slow_log diff --git a/mysql-test/r/mysql_upgrade.result b/mysql-test/r/mysql_upgrade.result index 58f6ffd4040..3d5a6cfebdb 100644 --- a/mysql-test/r/mysql_upgrade.result +++ b/mysql-test/r/mysql_upgrade.result @@ -17,6 +17,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.proxy_priv OK mysql.servers OK mysql.slow_log Error : You can't use locks with log tables. @@ -49,6 +50,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.proxy_priv OK mysql.servers OK mysql.slow_log Error : You can't use locks with log tables. @@ -81,6 +83,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.proxy_priv OK mysql.servers OK mysql.slow_log Error : You can't use locks with log tables. @@ -115,6 +118,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.proxy_priv OK mysql.servers OK mysql.slow_log Error : You can't use locks with log tables. @@ -153,6 +157,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.proxy_priv OK mysql.servers OK mysql.slow_log Error : You can't use locks with log tables. @@ -194,6 +199,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.proxy_priv OK mysql.servers OK mysql.slow_log Error : You can't use locks with log tables. diff --git a/mysql-test/r/mysqlcheck.result b/mysql-test/r/mysqlcheck.result index 06175955d7f..c51d71510f4 100644 --- a/mysql-test/r/mysqlcheck.result +++ b/mysql-test/r/mysqlcheck.result @@ -18,6 +18,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.proxy_priv OK mysql.servers OK mysql.slow_log note : The storage engine for the table doesn't support optimize @@ -43,6 +44,7 @@ mysql.ndb_binlog_index OK mysql.plugin OK mysql.proc OK mysql.procs_priv OK +mysql.proxy_priv OK mysql.servers OK mysql.slow_log note : The storage engine for the table doesn't support optimize diff --git a/mysql-test/r/plugin_auth.result b/mysql-test/r/plugin_auth.result new file mode 100644 index 00000000000..8a851af0ecb --- /dev/null +++ b/mysql-test/r/plugin_auth.result @@ -0,0 +1,212 @@ +SELECT PLUGIN_STATUS, PLUGIN_TYPE, PLUGIN_DESCRIPTION +FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME='test_plugin_server'; +PLUGIN_STATUS ACTIVE +PLUGIN_TYPE AUTHENTICATION +PLUGIN_DESCRIPTION plugin API test plugin +CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; +CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd'; +SELECT plugin,authentication_string FROM mysql.user WHERE User='plug'; +plugin authentication_string +test_plugin_server plug_dest +## test plugin auth +ERROR 28000: Access denied for user 'plug'@'localhost' (using password: YES) +GRANT PROXY ON plug_dest TO plug; +select USER(),CURRENT_USER(); +USER() CURRENT_USER() +plug@localhost plug_dest@% +## test SET PASSWORD +SET PASSWORD = PASSWORD('plug_dest'); +Warnings: +Note 1698 SET PASSWORD has no significance for users authenticating via plugins +## test bad credentials +ERROR 28000: Access denied for user 'plug'@'localhost' (using password: YES) +## test bad default plugin : should get CR_AUTH_PLUGIN_CANNOT_LOAD +## test correct default plugin +select USER(),CURRENT_USER(); +USER() CURRENT_USER() +plug@localhost plug@% +## test no_auto_create_user sql mode with plugin users +SET @@sql_mode=no_auto_create_user; +GRANT INSERT ON TEST.* TO grant_user IDENTIFIED WITH 'test_plugin_server'; +SET @@sql_mode=default; +DROP USER grant_user; +## test utf-8 user name +CREATE USER `Ÿ` IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; +GRANT PROXY ON plug_dest TO `Ÿ`; +select USER(),CURRENT_USER(); +USER() CURRENT_USER() +Ÿ@localhost plug_dest@% +DROP USER `Ÿ`; +## test GRANT ... IDENTIFIED WITH/BY ... +CREATE DATABASE test_grant_db; +# create new user via GRANT WITH +GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user +IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; +GRANT PROXY ON plug_dest TO new_grant_user; +select USER(),CURRENT_USER(); +USER() CURRENT_USER() +new_grant_user@localhost plug_dest@% +USE test_grant_db; +CREATE TABLE t1 (a INT); +DROP TABLE t1; +REVOKE ALL PRIVILEGES ON test_grant_db.* FROM new_grant_user; +# try re-create existing user via GRANT IDENTIFIED BY +GRANT ALL PRIVILEGES ON test_grant_db.* TO new_grant_user +IDENTIFIED BY 'unused_password'; +# make sure password doesn't take precendence +ERROR 28000: Access denied for user 'new_grant_user'@'localhost' (using password: YES) +#make sure plugin auth still available +select USER(),CURRENT_USER(); +USER() CURRENT_USER() +new_grant_user@localhost plug_dest@% +USE test_grant_db; +CREATE TABLE t1 (a INT); +DROP TABLE t1; +DROP USER new_grant_user; +# try re-create existing user via GRANT IDENTIFIED WITH +GRANT ALL PRIVILEGES ON test_grant_db.* TO plug +IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; +ERROR HY000: GRANT with IDENTIFIED WITH is illegal because the user plug already exists +GRANT ALL PRIVILEGES ON test_grant_db.* TO plug_dest +IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; +ERROR HY000: GRANT with IDENTIFIED WITH is illegal because the user plug_dest already exists +REVOKE SELECT on test_grant_db.* FROM joro +INDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'' at line 2 +REVOKE SELECT on test_grant_db.* FROM joro +INDENTIFIED BY 'plug_dest_passwd'; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INDENTIFIED BY 'plug_dest_passwd'' at line 2 +REVOKE SELECT on test_grant_db.* FROM joro +INDENTIFIED BY PASSWORD 'plug_dest_passwd'; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INDENTIFIED BY PASSWORD 'plug_dest_passwd'' at line 2 +DROP DATABASE test_grant_db; +## GRANT PROXY tests +CREATE USER grant_plug IDENTIFIED WITH 'test_plugin_server' +AS 'grant_plug_dest'; +CREATE USER grant_plug_dest IDENTIFIED BY 'grant_plug_dest_passwd'; +CREATE USER grant_plug_dest2 IDENTIFIED BY 'grant_plug_dest_passwd2'; +# ALL PRIVILEGES doesn't include PROXY +GRANT ALL PRIVILEGES ON *.* TO grant_plug; +ERROR 28000: Access denied for user 'grant_plug'@'localhost' (using password: YES) +GRANT ALL PRIVILEGES,PROXY ON grant_plug_dest TO grant_plug; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'PROXY ON grant_plug_dest TO grant_plug' at line 1 +this should fail : can't combine PROXY +GRANT ALL SELECT,PROXY ON grant_plug_dest TO grant_plug; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELECT,PROXY ON grant_plug_dest TO grant_plug' at line 1 +# this should fail : no such grant +REVOKE PROXY ON grant_plug_dest FROM grant_plug; +ERROR 42000: There is no such grant defined for user 'grant_plug' on host '%' +in grant_plug_dest_con +## testing what an ordinary user can grant +this should fail : no rights to grant all +GRANT PROXY ON ''@'' TO grant_plug; +ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost' +this should fail : not the same user +GRANT PROXY ON grant_plug TO grant_plug_dest; +ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost' +this should fail : same user, but on a different host +GRANT PROXY ON grant_plug_dest TO grant_plug; +ERROR 28000: Access denied for user 'grant_plug_dest'@'localhost' +this should work : same user +GRANT PROXY ON grant_plug_dest@localhost TO grant_plug_dest2; +REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug_dest2; +this should work : same user +GRANT PROXY ON grant_plug_dest@localhost TO grant_plug WITH GRANT OPTION; +REVOKE PROXY ON grant_plug_dest@localhost FROM grant_plug; +this should fail : can't create users +GRANT PROXY ON grant_plug_dest@localhost TO grant_plug@localhost; +ERROR 42000: You are not allowed to create a user with GRANT +in default connection +# test what root can grant +should work : root has PROXY to all users +GRANT PROXY ON ''@'' TO grant_plug; +REVOKE PROXY ON ''@'' FROM grant_plug; +should work : root has PROXY to all users +GRANT PROXY ON ''@'' TO proxy_admin IDENTIFIED BY 'test' +WITH GRANT OPTION; +need USAGE : PROXY doesn't contain it. +GRANT USAGE on *.* TO proxy_admin; +in proxy_admin_con; +should work : proxy_admin has proxy to ''@'' +GRANT PROXY ON future_user TO grant_plug; +in default connection +SHOW GRANTS FOR grant_plug; +Grants for grant_plug@% +GRANT ALL PRIVILEGES ON *.* TO 'grant_plug'@'%' WITH GRANT OPTION +GRANT PROXY ON 'future_user'@'%' TO 'grant_plug'@'%' +REVOKE PROXY ON future_user FROM grant_plug; +SHOW GRANTS FOR grant_plug; +Grants for grant_plug@% +GRANT ALL PRIVILEGES ON *.* TO 'grant_plug'@'%' WITH GRANT OPTION +## testing drop user +CREATE USER test_drop@localhost; +GRANT PROXY ON future_user TO test_drop@localhost; +SHOW GRANTS FOR test_drop@localhost; +Grants for test_drop@localhost +GRANT USAGE ON *.* TO 'test_drop'@'localhost' +GRANT PROXY ON 'future_user'@'%' TO 'test_drop'@'localhost' +DROP USER test_drop@localhost; +SELECT * FROM mysql.proxy_priv WHERE Host = 'test_drop' AND User = 'localhost'; +Host User Proxied_Host Proxied_User With_Grant +DROP USER proxy_admin; +DROP USER grant_plug,grant_plug_dest,grant_plug_dest2; +## END GRANT PROXY tests +## cleanup +DROP USER plug; +DROP USER plug_dest; +## @@proxy_user tests +CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; +CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd'; +GRANT PROXY ON plug_dest TO plug; +SELECT USER(),CURRENT_USER(),@@LOCAL.proxy_user; +USER() CURRENT_USER() @@LOCAL.proxy_user +root@localhost root@localhost NULL +SELECT @@GLOBAL.proxy_user; +ERROR HY000: Variable 'proxy_user' is a SESSION variable +SELECT @@LOCAL.proxy_user; +@@LOCAL.proxy_user +NULL +SET GLOBAL proxy_user = 'test'; +ERROR HY000: Variable 'proxy_user' is a read only variable +SET LOCAL proxy_user = 'test'; +ERROR HY000: Variable 'proxy_user' is a read only variable +SELECT @@LOCAL.proxy_user; +@@LOCAL.proxy_user +NULL +# in connection plug_con +SELECT @@LOCAL.proxy_user; +@@LOCAL.proxy_user +'plug'@'%' +# in connection default +## cleanup +DROP USER plug; +DROP USER plug_dest; +## END @@proxy_user tests +## @@external_user tests +CREATE USER plug IDENTIFIED WITH 'test_plugin_server' AS 'plug_dest'; +CREATE USER plug_dest IDENTIFIED BY 'plug_dest_passwd'; +GRANT PROXY ON plug_dest TO plug; +SELECT USER(),CURRENT_USER(),@@LOCAL.external_user; +USER() CURRENT_USER() @@LOCAL.external_user +root@localhost root@localhost NULL +SELECT @@GLOBAL.external_user; +ERROR HY000: Variable 'external_user' is a SESSION variable +SELECT @@LOCAL.external_user; +@@LOCAL.external_user +NULL +SET GLOBAL external_user = 'test'; +ERROR HY000: Variable 'external_user' is a read only variable +SET LOCAL external_user = 'test'; +ERROR HY000: Variable 'external_user' is a read only variable +SELECT @@LOCAL.external_user; +@@LOCAL.external_user +NULL +# in connection plug_con +SELECT @@LOCAL.external_user; +@@LOCAL.external_user +'plug'@'%' +# in connection default +## cleanup +DROP USER plug; +DROP USER plug_dest; +## END @@external_user tests diff --git a/mysql-test/r/ps.result b/mysql-test/r/ps.result index 0e75ebd57ec..cf3594f32a8 100644 --- a/mysql-test/r/ps.result +++ b/mysql-test/r/ps.result @@ -1194,13 +1194,13 @@ SET @aux= "SELECT COUNT(*) prepare my_stmt from @aux; execute my_stmt; COUNT(*) -40 +42 execute my_stmt; COUNT(*) -40 +42 execute my_stmt; COUNT(*) -40 +42 deallocate prepare my_stmt; drop procedure if exists p1| drop table if exists t1| diff --git a/mysql-test/r/sp_notembedded.result b/mysql-test/r/sp_notembedded.result index e4170cc3a49..7da95416e29 100644 --- a/mysql-test/r/sp_notembedded.result +++ b/mysql-test/r/sp_notembedded.result @@ -9,9 +9,11 @@ end| call bug4902()| Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION call bug4902()| Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION drop procedure bug4902| drop procedure if exists bug4902_2| create procedure bug4902_2() @@ -206,9 +208,11 @@ create procedure 15298_2 () sql security definer show grants; call 15298_1(); Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION call 15298_2(); Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION drop user mysqltest_1@localhost; drop procedure 15298_1; drop procedure 15298_2; @@ -245,6 +249,8 @@ max_updates, max_connections, max_user_connections) VALUES('%', 'mysqltest_1', password(''), 'Y', 'Y', 'Y', 'Y', 'Y', 'Y', 'N', 'N', 'N', 'N', 'N', 'N', 'Y', 'Y', 'N', 'N', 'Y', 'Y', 'N', 'N', 'N', 'N', 'N', 'Y', 'Y', 'N', '', '', '', '', '0', '0', '0', '0'); +Warnings: +Warning 1364 Field 'authentication_string' doesn't have a default value FLUSH PRIVILEGES; CREATE PROCEDURE p1(i INT) BEGIN END; DROP PROCEDURE p1; diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index 679e50fcad9..e82cf229912 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -14,6 +14,7 @@ ndb_binlog_index plugin proc procs_priv +proxy_priv servers slow_log tables_priv @@ -119,6 +120,8 @@ user CREATE TABLE `user` ( `max_updates` int(11) unsigned NOT NULL DEFAULT '0', `max_connections` int(11) unsigned NOT NULL DEFAULT '0', `max_user_connections` int(11) unsigned NOT NULL DEFAULT '0', + `plugin` char(60) COLLATE utf8_bin NOT NULL DEFAULT '', + `authentication_string` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (`Host`,`User`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges' show create table func; diff --git a/mysql-test/suite/federated/federated.result b/mysql-test/suite/federated/federated.result index db4ffc38213..4cd7b416621 100644 --- a/mysql-test/suite/federated/federated.result +++ b/mysql-test/suite/federated/federated.result @@ -60,7 +60,7 @@ CREATE TABLE federated.t1 ( ENGINE="FEDERATED" DEFAULT CHARSET=latin1 CONNECTION='mysql://user:pass@127.0.0.1:SLAVE_PORT/federated/t1'; SELECT * FROM federated.t1; -ERROR HY000: Unable to connect to foreign data source: Access denied for user 'user'@'localhost' (using password: YES) +ERROR HY000: Unable to connect to foreign data source: Access denied for user 'user'@'localhost' (using password: NO) DROP TABLE federated.t1; CREATE TABLE federated.t1 ( `id` int(20) NOT NULL, diff --git a/mysql-test/suite/funcs_1/r/innodb_trig_03e.result b/mysql-test/suite/funcs_1/r/innodb_trig_03e.result index bc3f7daf5bc..3c77ca3dedc 100644 --- a/mysql-test/suite/funcs_1/r/innodb_trig_03e.result +++ b/mysql-test/suite/funcs_1/r/innodb_trig_03e.result @@ -580,6 +580,7 @@ root@localhost show grants; Grants for root@localhost GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION +GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION drop trigger trg1_1; use priv_db; diff --git a/mysql-test/suite/funcs_1/r/is_columns_mysql.result b/mysql-test/suite/funcs_1/r/is_columns_mysql.result index 1ab2b3513f0..767f9e47f13 100644 --- a/mysql-test/suite/funcs_1/r/is_columns_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_columns_mysql.result @@ -134,6 +134,11 @@ def mysql procs_priv Routine_name 4 NO char 64 192 NULL NULL utf8 utf8_general_ def mysql procs_priv Routine_type 5 NULL NO enum 9 27 NULL NULL utf8 utf8_bin enum('FUNCTION','PROCEDURE') PRI select,insert,update,references def mysql procs_priv Timestamp 8 CURRENT_TIMESTAMP NO timestamp NULL NULL NULL NULL NULL NULL timestamp on update CURRENT_TIMESTAMP select,insert,update,references def mysql procs_priv User 3 NO char 16 48 NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references +def mysql proxy_priv Host 1 NO char 60 180 NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references +def mysql proxy_priv Proxied_Host 3 NO char 16 48 NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references +def mysql proxy_priv Proxied_User 4 NO char 60 180 NULL NULL utf8 utf8_bin char(60) PRI select,insert,update,references +def mysql proxy_priv User 2 NO char 16 48 NULL NULL utf8 utf8_bin char(16) PRI select,insert,update,references +def mysql proxy_priv With_Grant 5 0 NO tinyint NULL NULL 3 0 NULL NULL tinyint(1) select,insert,update,references def mysql servers Db 3 NO char 64 192 NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references def mysql servers Host 2 NO char 64 192 NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references def mysql servers Owner 9 NO char 64 192 NULL NULL utf8 utf8_general_ci char(64) select,insert,update,references @@ -178,6 +183,7 @@ def mysql time_zone_transition_type Time_zone_id 1 NULL NO int NULL NULL 10 0 NU def mysql time_zone_transition_type Transition_type_id 2 NULL NO int NULL NULL 10 0 NULL NULL int(10) unsigned PRI select,insert,update,references def mysql user Alter_priv 17 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user Alter_routine_priv 28 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references +def mysql user authentication_string 42 NULL NO text 65535 65535 NULL NULL utf8 utf8_bin text select,insert,update,references def mysql user Create_priv 8 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user Create_routine_priv 27 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user Create_tablespace_priv 32 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references @@ -199,6 +205,7 @@ def mysql user max_questions 37 0 NO int NULL NULL 10 0 NULL NULL int(11) unsign def mysql user max_updates 38 0 NO int NULL NULL 10 0 NULL NULL int(11) unsigned select,insert,update,references def mysql user max_user_connections 40 0 NO int NULL NULL 10 0 NULL NULL int(11) unsigned select,insert,update,references def mysql user Password 3 NO char 41 41 NULL NULL latin1 latin1_bin char(41) select,insert,update,references +def mysql user plugin 41 NO char 60 180 NULL NULL utf8 utf8_bin char(60) select,insert,update,references def mysql user Process_priv 12 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user References_priv 15 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references def mysql user Reload_priv 10 N NO enum 1 3 NULL NULL utf8 utf8_general_ci enum('N','Y') select,insert,update,references @@ -418,6 +425,11 @@ NULL mysql proc modified timestamp NULL NULL NULL NULL timestamp 3.0000 mysql procs_priv Grantor char 77 231 utf8 utf8_bin char(77) 3.0000 mysql procs_priv Proc_priv set 27 81 utf8 utf8_general_ci set('Execute','Alter Routine','Grant') NULL mysql procs_priv Timestamp timestamp NULL NULL NULL NULL timestamp +3.0000 mysql proxy_priv Host char 60 180 utf8 utf8_bin char(60) +3.0000 mysql proxy_priv User char 16 48 utf8 utf8_bin char(16) +3.0000 mysql proxy_priv Proxied_Host char 16 48 utf8 utf8_bin char(16) +3.0000 mysql proxy_priv Proxied_User char 60 180 utf8 utf8_bin char(60) +NULL mysql proxy_priv With_Grant tinyint NULL NULL NULL NULL tinyint(1) 3.0000 mysql servers Server_name char 64 192 utf8 utf8_general_ci char(64) 3.0000 mysql servers Host char 64 192 utf8 utf8_general_ci char(64) 3.0000 mysql servers Db char 64 192 utf8 utf8_general_ci char(64) @@ -500,3 +512,5 @@ NULL mysql user max_questions int NULL NULL NULL NULL int(11) unsigned NULL mysql user max_updates int NULL NULL NULL NULL int(11) unsigned NULL mysql user max_connections int NULL NULL NULL NULL int(11) unsigned NULL mysql user max_user_connections int NULL NULL NULL NULL int(11) unsigned +3.0000 mysql user plugin char 60 180 utf8 utf8_bin char(60) +1.0000 mysql user authentication_string text 65535 65535 utf8 utf8_bin text diff --git a/mysql-test/suite/funcs_1/r/is_key_column_usage.result b/mysql-test/suite/funcs_1/r/is_key_column_usage.result index a81452b7927..2e50a0c36bf 100644 --- a/mysql-test/suite/funcs_1/r/is_key_column_usage.result +++ b/mysql-test/suite/funcs_1/r/is_key_column_usage.result @@ -106,6 +106,10 @@ def mysql PRIMARY def mysql procs_priv Db def mysql PRIMARY def mysql procs_priv User def mysql PRIMARY def mysql procs_priv Routine_name def mysql PRIMARY def mysql procs_priv Routine_type +def mysql PRIMARY def mysql proxy_priv Host +def mysql PRIMARY def mysql proxy_priv User +def mysql PRIMARY def mysql proxy_priv Proxied_Host +def mysql PRIMARY def mysql proxy_priv Proxied_User def mysql PRIMARY def mysql servers Server_name def mysql PRIMARY def mysql tables_priv Host def mysql PRIMARY def mysql tables_priv Db diff --git a/mysql-test/suite/funcs_1/r/is_statistics.result b/mysql-test/suite/funcs_1/r/is_statistics.result index 873b328dc2d..0c43883789b 100644 --- a/mysql-test/suite/funcs_1/r/is_statistics.result +++ b/mysql-test/suite/funcs_1/r/is_statistics.result @@ -118,6 +118,10 @@ def mysql procs_priv mysql PRIMARY def mysql procs_priv mysql PRIMARY def mysql procs_priv mysql PRIMARY def mysql procs_priv mysql Grantor +def mysql proxy_priv mysql PRIMARY +def mysql proxy_priv mysql PRIMARY +def mysql proxy_priv mysql PRIMARY +def mysql proxy_priv mysql PRIMARY def mysql servers mysql PRIMARY def mysql tables_priv mysql PRIMARY def mysql tables_priv mysql PRIMARY diff --git a/mysql-test/suite/funcs_1/r/is_statistics_mysql.result b/mysql-test/suite/funcs_1/r/is_statistics_mysql.result index a1fa0cac9f2..584bbeb7af5 100644 --- a/mysql-test/suite/funcs_1/r/is_statistics_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_statistics_mysql.result @@ -40,6 +40,10 @@ def mysql procs_priv 0 mysql PRIMARY 2 Db A #CARD# NULL NULL BTREE def mysql procs_priv 0 mysql PRIMARY 3 User A #CARD# NULL NULL BTREE def mysql procs_priv 0 mysql PRIMARY 4 Routine_name A #CARD# NULL NULL BTREE def mysql procs_priv 0 mysql PRIMARY 5 Routine_type A #CARD# NULL NULL BTREE +def mysql proxy_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE +def mysql proxy_priv 0 mysql PRIMARY 2 User A #CARD# NULL NULL BTREE +def mysql proxy_priv 0 mysql PRIMARY 3 Proxied_Host A #CARD# NULL NULL BTREE +def mysql proxy_priv 0 mysql PRIMARY 4 Proxied_User A #CARD# NULL NULL BTREE def mysql servers 0 mysql PRIMARY 1 Server_name A #CARD# NULL NULL BTREE def mysql tables_priv 1 mysql Grantor 1 Grantor A #CARD# NULL NULL BTREE def mysql tables_priv 0 mysql PRIMARY 1 Host A #CARD# NULL NULL BTREE diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints.result b/mysql-test/suite/funcs_1/r/is_table_constraints.result index 7f1c83a8ea5..d4d2c38c9ba 100644 --- a/mysql-test/suite/funcs_1/r/is_table_constraints.result +++ b/mysql-test/suite/funcs_1/r/is_table_constraints.result @@ -73,6 +73,7 @@ def mysql PRIMARY mysql ndb_binlog_index def mysql PRIMARY mysql plugin def mysql PRIMARY mysql proc def mysql PRIMARY mysql procs_priv +def mysql PRIMARY mysql proxy_priv def mysql PRIMARY mysql servers def mysql PRIMARY mysql tables_priv def mysql PRIMARY mysql time_zone diff --git a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result index 8b7ac6994c1..38e9c9034c9 100644 --- a/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_table_constraints_mysql.result @@ -23,6 +23,7 @@ def mysql PRIMARY mysql ndb_binlog_index PRIMARY KEY def mysql PRIMARY mysql plugin PRIMARY KEY def mysql PRIMARY mysql proc PRIMARY KEY def mysql PRIMARY mysql procs_priv PRIMARY KEY +def mysql PRIMARY mysql proxy_priv PRIMARY KEY def mysql PRIMARY mysql servers PRIMARY KEY def mysql PRIMARY mysql tables_priv PRIMARY KEY def mysql PRIMARY mysql time_zone PRIMARY KEY diff --git a/mysql-test/suite/funcs_1/r/is_tables_mysql.result b/mysql-test/suite/funcs_1/r/is_tables_mysql.result index 0945401ba43..ae512327807 100644 --- a/mysql-test/suite/funcs_1/r/is_tables_mysql.result +++ b/mysql-test/suite/funcs_1/r/is_tables_mysql.result @@ -336,6 +336,29 @@ user_comment Procedure privileges Separator ----------------------------------------------------- TABLE_CATALOG def TABLE_SCHEMA mysql +TABLE_NAME proxy_priv +TABLE_TYPE BASE TABLE +ENGINE MyISAM +VERSION 10 +ROW_FORMAT Fixed +TABLE_ROWS #TBLR# +AVG_ROW_LENGTH #ARL# +DATA_LENGTH #DL# +MAX_DATA_LENGTH #MDL# +INDEX_LENGTH #IL# +DATA_FREE #DF# +AUTO_INCREMENT NULL +CREATE_TIME #CRT# +UPDATE_TIME #UT# +CHECK_TIME #CT# +TABLE_COLLATION utf8_bin +CHECKSUM NULL +CREATE_OPTIONS #CO# +TABLE_COMMENT #TC# +user_comment User proxy privileges +Separator ----------------------------------------------------- +TABLE_CATALOG def +TABLE_SCHEMA mysql TABLE_NAME servers TABLE_TYPE BASE TABLE ENGINE MyISAM diff --git a/mysql-test/suite/funcs_1/r/is_user_privileges.result b/mysql-test/suite/funcs_1/r/is_user_privileges.result index 8f68f8c802d..1ec1ffc4ce1 100644 --- a/mysql-test/suite/funcs_1/r/is_user_privileges.result +++ b/mysql-test/suite/funcs_1/r/is_user_privileges.result @@ -69,46 +69,436 @@ GRANT UPDATE ON *.* TO 'testuser2'@'localhost'; SELECT * FROM information_schema.user_privileges WHERE grantee LIKE '''testuser%''' ORDER BY grantee, table_catalog, privilege_type; -GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE -'testuser1'@'localhost' def USAGE NO -'testuser2'@'localhost' def INSERT NO -'testuser2'@'localhost' def UPDATE NO -'testuser3'@'localhost' def USAGE NO +GRANTEE 'testuser1'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE USAGE +IS_GRANTABLE NO +GRANTEE 'testuser2'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE INSERT +IS_GRANTABLE NO +GRANTEE 'testuser2'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE UPDATE +IS_GRANTABLE NO +GRANTEE 'testuser3'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE USAGE +IS_GRANTABLE NO SELECT * FROM mysql.user WHERE user LIKE 'testuser%' ORDER BY host, user; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 -localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 -localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 +Host localhost +User testuser1 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string +Host localhost +User testuser2 +Password +Select_priv N +Insert_priv Y +Update_priv Y +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string +Host localhost +User testuser3 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string # # Add GRANT OPTION db_datadict.* to testuser1; GRANT UPDATE ON db_datadict.* TO 'testuser1'@'localhost' WITH GRANT OPTION; SELECT * FROM information_schema.user_privileges WHERE grantee LIKE '''testuser%''' ORDER BY grantee, table_catalog, privilege_type; -GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE -'testuser1'@'localhost' def USAGE NO -'testuser2'@'localhost' def INSERT NO -'testuser2'@'localhost' def UPDATE NO -'testuser3'@'localhost' def USAGE NO +GRANTEE 'testuser1'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE USAGE +IS_GRANTABLE NO +GRANTEE 'testuser2'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE INSERT +IS_GRANTABLE NO +GRANTEE 'testuser2'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE UPDATE +IS_GRANTABLE NO +GRANTEE 'testuser3'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE USAGE +IS_GRANTABLE NO SELECT * FROM mysql.user WHERE user LIKE 'testuser%' ORDER BY host, user; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 -localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 -localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 +Host localhost +User testuser1 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string +Host localhost +User testuser2 +Password +Select_priv N +Insert_priv Y +Update_priv Y +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string +Host localhost +User testuser3 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string # Establish connection testuser1 (user=testuser1) SELECT * FROM information_schema.user_privileges WHERE grantee LIKE '''testuser%''' ORDER BY grantee, table_catalog, privilege_type; -GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE -'testuser1'@'localhost' def USAGE NO +GRANTEE 'testuser1'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE USAGE +IS_GRANTABLE NO SELECT * FROM mysql.user WHERE user LIKE 'testuser%' ORDER BY host, user; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost testuser1 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 -localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 -localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 +Host localhost +User testuser1 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string +Host localhost +User testuser2 +Password +Select_priv N +Insert_priv Y +Update_priv Y +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string +Host localhost +User testuser3 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string SHOW GRANTS; Grants for testuser1@localhost GRANT USAGE ON *.* TO 'testuser1'@'localhost' @@ -123,46 +513,436 @@ GRANT SELECT ON *.* TO 'testuser1'@'localhost'; SELECT * FROM information_schema.user_privileges WHERE grantee LIKE '''testuser%''' ORDER BY grantee, table_catalog, privilege_type; -GRANTEE TABLE_CATALOG PRIVILEGE_TYPE IS_GRANTABLE -'testuser1'@'localhost' def SELECT NO -'testuser2'@'localhost' def INSERT NO -'testuser2'@'localhost' def UPDATE NO -'testuser3'@'localhost' def USAGE NO +GRANTEE 'testuser1'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE SELECT +IS_GRANTABLE NO +GRANTEE 'testuser2'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE INSERT +IS_GRANTABLE NO +GRANTEE 'testuser2'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE UPDATE +IS_GRANTABLE NO +GRANTEE 'testuser3'@'localhost' +TABLE_CATALOG def +PRIVILEGE_TYPE USAGE +IS_GRANTABLE NO SELECT * FROM mysql.user WHERE user LIKE 'testuser%' ORDER BY host, user; -Host User Password Select_priv Insert_priv Update_priv Delete_priv Create_priv Drop_priv Reload_priv Shutdown_priv Process_priv File_priv Grant_priv References_priv Index_priv Alter_priv Show_db_priv Super_priv Create_tmp_table_priv Lock_tables_priv Execute_priv Repl_slave_priv Repl_client_priv Create_view_priv Show_view_priv Create_routine_priv Alter_routine_priv Create_user_priv Event_priv Trigger_priv Create_tablespace_priv ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_connections max_user_connections -localhost testuser1 Y N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 -localhost testuser2 N Y Y N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 -localhost testuser3 N N N N N N N N N N N N N N N N N N N N N N N N N N N N N 0 0 0 0 +Host localhost +User testuser1 +Password +Select_priv Y +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string +Host localhost +User testuser2 +Password +Select_priv N +Insert_priv Y +Update_priv Y +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string +Host localhost +User testuser3 +Password +Select_priv N +Insert_priv N +Update_priv N +Delete_priv N +Create_priv N +Drop_priv N +Reload_priv N +Shutdown_priv N +Process_priv N +File_priv N +Grant_priv N +References_priv N +Index_priv N +Alter_priv N +Show_db_priv N +Super_priv N +Create_tmp_table_priv N +Lock_tables_priv N +Execute_priv N +Repl_slave_priv N +Repl_client_priv N +Create_view_priv N +Show_view_priv N +Create_routine_priv N +Alter_routine_priv N +Create_user_priv N +Event_priv N +Trigger_priv N +Create_tablespace_priv N +ssl_type +ssl_cipher +x509_issuer +x509_subject +max_questions 0 +max_updates 0 +max_connections 0 +max_user_connections 0 +plugin +authentication_string GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION; # # Here is shown correctly for testuser1; +--vertical_results eval $my_select1; eval $my_select2; +--horizontal_results GRANT SELECT ON *.* TO 'testuser1'@'localhost' WITH GRANT OPTION; --echo # --echo # Here