From 1fabc5de0adaf326e86f01fb93e0e895b12bebe8 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Dec 2004 13:35:34 +0300 Subject: [PATCH 01/13] Bug#7219 information_schema: errors in "columns" changed field names in 'collations' table(discussed with PGulutzan) mysql-test/r/information_schema.result: result fix mysql-test/t/information_schema.test: test for bug2719 --- mysql-test/r/information_schema.result | 28 ++++++++++++++++---------- mysql-test/t/information_schema.test | 8 ++++++++ sql/sql_show.cc | 22 ++++++++++---------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 0bd3ba14636..c9cb30f8365 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -172,7 +172,7 @@ CHARACTER_SET_NAME DEFAULT_COLLATE_NAME DESCRIPTION MAXLEN latin1 latin1_swedish_ci ISO 8859-1 West European 1 select * from information_schema.COLLATIONS where COLLATION_NAME like 'latin1%'; -COLLATION_NAME CHARSET ID DEFAULT COMPILED SORTLEN +COLLATION_NAME CHARACTER_SET_NAME ID IS_DEFAULT IS_COMPILED SORTLEN latin1_german1_ci latin1 5 0 latin1_swedish_ci latin1 8 Yes Yes 1 latin1_danish_ci latin1 15 0 @@ -192,7 +192,7 @@ latin1_general_ci latin1 48 0 latin1_general_cs latin1 49 0 latin1_spanish_ci latin1 94 0 SHOW COLLATION * LIKE 'latin1%'; -COLLATION_NAME CHARSET ID DEFAULT COMPILED SORTLEN +COLLATION_NAME CHARACTER_SET_NAME ID IS_DEFAULT IS_COMPILED SORTLEN latin1_german1_ci latin1 5 0 latin1_swedish_ci latin1 8 Yes Yes 1 latin1_danish_ci latin1 15 0 @@ -222,7 +222,7 @@ latin1_general_ci latin1_general_cs latin1_spanish_ci SHOW COLLATION * WHERE COLLATION_NAME like 'latin1%'; -COLLATION_NAME CHARSET ID DEFAULT COMPILED SORTLEN +COLLATION_NAME CHARACTER_SET_NAME ID IS_DEFAULT IS_COMPILED SORTLEN latin1_german1_ci latin1 5 0 latin1_swedish_ci latin1 8 Yes Yes 1 latin1_danish_ci latin1 15 0 @@ -467,20 +467,20 @@ drop table t1; SHOW CREATE TABLE INFORMATION_SCHEMA.character_sets; Table Create Table character_sets CREATE TEMPORARY TABLE `character_sets` ( - `CHARACTER_SET_NAME` varchar(30) NOT NULL default '', - `DEFAULT_COLLATE_NAME` varchar(60) NOT NULL default '', + `CHARACTER_SET_NAME` varchar(64) NOT NULL default '', + `DEFAULT_COLLATE_NAME` varchar(64) NOT NULL default '', `DESCRIPTION` varchar(60) NOT NULL default '', `MAXLEN` bigint(3) NOT NULL default '0' -) ENGINE=HEAP DEFAULT CHARSET=utf8 MAX_ROWS=2267 +) ENGINE=HEAP DEFAULT CHARSET=utf8 MAX_ROWS=1818 set names latin2; SHOW CREATE TABLE INFORMATION_SCHEMA.character_sets; Table Create Table character_sets CREATE TEMPORARY TABLE `character_sets` ( - `CHARACTER_SET_NAME` varchar(30) NOT NULL default '', - `DEFAULT_COLLATE_NAME` varchar(60) NOT NULL default '', + `CHARACTER_SET_NAME` varchar(64) NOT NULL default '', + `DEFAULT_COLLATE_NAME` varchar(64) NOT NULL default '', `DESCRIPTION` varchar(60) NOT NULL default '', `MAXLEN` bigint(3) NOT NULL default '0' -) ENGINE=HEAP DEFAULT CHARSET=utf8 MAX_ROWS=2267 +) ENGINE=HEAP DEFAULT CHARSET=utf8 MAX_ROWS=1818 set names latin1; create table t1 select * from information_schema.CHARACTER_SETS where CHARACTER_SET_NAME like "latin1"; @@ -491,8 +491,8 @@ alter table t1 default character set utf8; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `CHARACTER_SET_NAME` varchar(30) NOT NULL default '', - `DEFAULT_COLLATE_NAME` varchar(60) NOT NULL default '', + `CHARACTER_SET_NAME` varchar(64) NOT NULL default '', + `DEFAULT_COLLATE_NAME` varchar(64) NOT NULL default '', `DESCRIPTION` varchar(60) NOT NULL default '', `MAXLEN` bigint(3) NOT NULL default '0' ) ENGINE=MyISAM DEFAULT CHARSET=utf8 @@ -641,3 +641,9 @@ drop view t3; drop table t4; select * from information_schema.table_names; ERROR 42S02: Unknown table 'table_names' in information_schema +select column_type from information_schema.columns +where table_schema="information_schema" and table_name="COLUMNS" and +(column_name="character_set_name" or column_name="collation_name"); +column_type +varchar(64) +varchar(64) diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index f7d4cdd43b2..b575e48e72f 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -331,3 +331,11 @@ drop table t4; # --error 1109 select * from information_schema.table_names; + +# +# Bug#2719 information_schema: errors in "columns" +# +select column_type from information_schema.columns +where table_schema="information_schema" and table_name="COLUMNS" and +(column_name="character_set_name" or column_name="collation_name"); + diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 826bd2038f9..889bc71aa53 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -3453,7 +3453,7 @@ ST_FIELD_INFO schema_fields_info[]= { {"CATALOG_NAME", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, {"SCHEMA_NAME", NAME_LEN, MYSQL_TYPE_STRING, 0, 0, "Database"}, - {"DEFAULT_CHARACTER_SET_NAME", 60, MYSQL_TYPE_STRING, 0, 0, 0}, + {"DEFAULT_CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, {"SQL_PATH", FN_REFLEN, MYSQL_TYPE_STRING, 0, 1, 0}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -3500,8 +3500,8 @@ ST_FIELD_INFO columns_fields_info[]= {"CHARACTER_OCTET_LENGTH", 21 , MYSQL_TYPE_LONG, 0, 0, 0}, {"NUMERIC_PRECISION", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, {"NUMERIC_SCALE", 21 , MYSQL_TYPE_LONG, 0, 1, 0}, - {"CHARACTER_SET_NAME", 40, MYSQL_TYPE_STRING, 0, 1, 0}, - {"COLLATION_NAME", 40, MYSQL_TYPE_STRING, 0, 1, "Collation"}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 1, 0}, + {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"}, {"COLUMN_TYPE", 65535, MYSQL_TYPE_STRING, 0, 0, "Type"}, {"COLUMN_KEY", 3, MYSQL_TYPE_STRING, 0, 0, "Key"}, {"EXTRA", 20, MYSQL_TYPE_STRING, 0, 0, "Extra"}, @@ -3513,8 +3513,8 @@ ST_FIELD_INFO columns_fields_info[]= ST_FIELD_INFO charsets_fields_info[]= { - {"CHARACTER_SET_NAME", 30, MYSQL_TYPE_STRING, 0, 0, "Charset"}, - {"DEFAULT_COLLATE_NAME", 60, MYSQL_TYPE_STRING, 0, 0, "Default collation"}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"}, + {"DEFAULT_COLLATE_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Default collation"}, {"DESCRIPTION", 60, MYSQL_TYPE_STRING, 0, 0, "Description"}, {"MAXLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Maxlen"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} @@ -3523,11 +3523,11 @@ ST_FIELD_INFO charsets_fields_info[]= ST_FIELD_INFO collation_fields_info[]= { - {"COLLATION_NAME", 30, MYSQL_TYPE_STRING, 0, 0, "Collation"}, - {"CHARSET", 30, MYSQL_TYPE_STRING, 0, 0, "Charset"}, + {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Collation"}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, "Charset"}, {"ID", 11, MYSQL_TYPE_LONG, 0, 0, "Id"}, - {"DEFAULT", 30 ,MYSQL_TYPE_STRING, 0, 0, "Default"}, - {"COMPILED", 30 ,MYSQL_TYPE_STRING, 0, 0, "Compiled"}, + {"IS_DEFAULT", 3, MYSQL_TYPE_STRING, 0, 0, "Default"}, + {"IS_COMPILED", 3, MYSQL_TYPE_STRING, 0, 0, "Compiled"}, {"SORTLEN", 3 ,MYSQL_TYPE_LONG, 0, 0, "Sortlen"}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; @@ -3535,8 +3535,8 @@ ST_FIELD_INFO collation_fields_info[]= ST_FIELD_INFO coll_charset_app_fields_info[]= { - {"COLLATION_NAME", 30, MYSQL_TYPE_STRING, 0, 0, 0}, - {"CHARACTER_SET_NAME", 30, MYSQL_TYPE_STRING, 0, 0, 0}, + {"COLLATION_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, + {"CHARACTER_SET_NAME", 64, MYSQL_TYPE_STRING, 0, 0, 0}, {0, 0, MYSQL_TYPE_STRING, 0, 0, 0} }; From 09ce0b330b3af16075958c306db984af5cd8b3b6 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Dec 2004 10:46:24 +0000 Subject: [PATCH 02/13] WL#925 - Privileges for stored routines Implement fine-grained control over access to stored procedures Privileges are cached (same way as existing table/column privs) mysql-test/include/system_db_struct.inc: WL#925 - Privileges for stored routines New system table: procs_priv mysql-test/r/connect.result: WL#925 - Privileges for stored routines New system table: procs_priv mysql-test/r/grant.result: WL#925 - Privileges for stored routines user table has additional privilege attributes SHOW PRIVILEGES amended mysql-test/r/grant2.result: Fix result mysql-test/r/information_schema.result: WL#925 - Privileges for stored routines New system table procs_priv New user privileges mysql-test/r/show_check.result: Fix result mysql-test/r/sp-security.result: WL#925 - Privileges for stored routines Fix existing tests to work with new privileges New tests for new privileges mysql-test/r/sp.result: WL#925 - Privileges for stored routines Fix SHOW PRIVILEGES results mysql-test/r/system_mysql_db.result: WL#925 - Privileges for stored routines New system table: procs_priv user and db tables have new privilege attributes mysql-test/t/grant2.test: Fix test mysql-test/t/show_check.test: Fix test mysql-test/t/sp-security.test: WL#925 - Privileges for stored routines Allow existing tests to run with new privilege checks New tests for privileges mysql-test/t/system_mysql_db_fix.test: WL#925 - Privileges for stored routines New system table: procs_priv scripts/mysql_create_system_tables.sh: WL#925 - Privileges for stored routines db and user has new privilege attributes new system table: procs_priv scripts/mysql_fix_privilege_tables.sql: WL#925 - Privileges for stored routines new system table: procs_priv scripts/mysql_install_db.sh: WL#925 - Privileges for stored routines Amend comment sql/item_func.cc: WL#925 - Privileges for stored routines Privilege check for stored FUNCTION routine sql/lex.h: WL#925 - Privileges for stored routines new token ROUTINE sql/mysql_priv.h: WL#925 - Privileges for stored routines New function: check_procedure_access() sql/mysqld.cc: WL#925 - Privileges for stored routines system option automatic-sp-privileges sql/set_var.cc: WL#925 - Privileges for stored routines system option automatic-sp-privileges sql/share/errmsg.txt: WL#925 - Privileges for stored routines rename errormessage to conform: ER_SP_ACCESS_DENIED_ERROR -> ER_PROCACCESS_DENIED_ERROR New error messages ER_NONEXISTING_PROC_GRANT, ER_PROC_AUTO_GRANT_FAIL, ER_PROC_AUTO_REVOKE_FAIL sql/sp.cc: WL#925 - Privileges for stored routines new function: sp_exists_routine() sql/sp.h: WL#925 - Privileges for stored routines new function: sp_exists_routine() sql/sql_acl.cc: WL#925 - Privileges for stored routines Implementation for SP privileges. Privileges are cached in memory hash. New functions: mysql_procedure_grant() check_grant_procedure() sp_revoke_privileges() sp_grant_privileges() sql/sql_acl.h: WL#925 - Privileges for stored routines New privilege bits: CREATE_PROC_ACL, ALTER_PROC_ACL Alter confusing bit-segments to be shifted New macros: fix_rights_for_procedure() get_rights_for_procedure() New functions: mysql_procedure_grant() check_grant_procedure() sp_grant_privileges() sp_revoke_privileges() sql/sql_lex.h: WL#925 - Privileges for stored routines new all_privileges attribute in LEX sql/sql_parse.cc: WL#925 - Privileges for stored routines Remove function: check_sp_definer_access() Add handling for SP grants/revokes Add privilege checks for stored procedure invocation sql/sql_show.cc: WL#925 - Privileges for stored routines update result for SHOW PRIVILEGES sql/sql_yacc.yy: WL#925 - Privileges for stored routines New token ROUTINE rename some rules handle CREATE ROUTINE / ALTER ROUTINE privileges --- mysql-test/include/system_db_struct.inc | 1 + mysql-test/r/connect.result | 3 + mysql-test/r/grant.result | 11 +- mysql-test/r/grant2.result | 6 +- mysql-test/r/information_schema.result | 6 + mysql-test/r/show_check.result | 8 +- mysql-test/r/sp-security.result | 82 ++- mysql-test/r/sp.result | 10 +- mysql-test/r/system_mysql_db.result | 19 + mysql-test/t/grant2.test | 6 +- mysql-test/t/show_check.test | 8 +- mysql-test/t/sp-security.test | 97 ++- mysql-test/t/system_mysql_db_fix.test | 2 +- scripts/mysql_create_system_tables.sh | 49 +- scripts/mysql_fix_privilege_tables.sql | 39 ++ scripts/mysql_install_db.sh | 2 +- sql/item_func.cc | 11 + sql/lex.h | 1 + sql/mysql_priv.h | 3 + sql/mysqld.cc | 7 + sql/set_var.cc | 5 + sql/share/errmsg.txt | 10 +- sql/sp.cc | 39 ++ sql/sp.h | 3 + sql/sql_acl.cc | 894 +++++++++++++++++++++--- sql/sql_acl.h | 44 +- sql/sql_lex.h | 1 + sql/sql_parse.cc | 155 ++-- sql/sql_show.cc | 5 +- sql/sql_yacc.yy | 27 +- 30 files changed, 1344 insertions(+), 210 deletions(-) diff --git a/mysql-test/include/system_db_struct.inc b/mysql-test/include/system_db_struct.inc index 5a7aa26c65a..e24c8f3311d 100644 --- a/mysql-test/include/system_db_struct.inc +++ b/mysql-test/include/system_db_struct.inc @@ -10,3 +10,4 @@ show create table user; show create table func; show create table tables_priv; show create table columns_priv; +show create table procs_priv; diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result index 702b725764b..fef813371c8 100644 --- a/mysql-test/r/connect.result +++ b/mysql-test/r/connect.result @@ -9,6 +9,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second @@ -31,6 +32,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second @@ -57,6 +59,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second diff --git a/mysql-test/r/grant.result b/mysql-test/r/grant.result index b69ba9702a6..629a3221330 100644 --- a/mysql-test/r/grant.result +++ b/mysql-test/r/grant.result @@ -10,8 +10,8 @@ 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 ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_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 0 0 0 +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 ssl_type ssl_cipher x509_issuer x509_subject max_questions max_updates max_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 0 0 0 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' @@ -62,7 +62,7 @@ revoke LOCK TABLES, ALTER on mysqltest.* from mysqltest_1@localhost; show grants for mysqltest_1@localhost; Grants for mysqltest_1@localhost GRANT USAGE ON *.* TO 'mysqltest_1'@'localhost' -GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, CREATE VIEW, SHOW VIEW ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION +GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, CREATE TEMPORARY TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE ON `mysqltest`.* TO 'mysqltest_1'@'localhost' WITH GRANT OPTION revoke all privileges on mysqltest.* from mysqltest_1@localhost; delete from mysql.user where user='mysqltest_1'; flush privileges; @@ -347,13 +347,16 @@ drop table t1; SHOW PRIVILEGES; Privilege Context Comment Alter Tables To alter the table +Alter routine Functions,Procedures To alter or drop stored functions/procedures Create Databases,Tables,Indexes To create new databases and tables +Create routine Functions,Procedures To use CREATE FUNCTION/PROCEDURE Create temporary tables Databases To use CREATE TEMPORARY TABLE Create view Tables To create new views Delete Tables To delete existing rows Drop Databases,Tables To drop databases, tables, and views +Execute Functions,Procedures To execute stored routines File File access on server To read and write files on the server -Grant option Databases,Tables To give to other users those privileges you possess +Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess Index Tables To create or drop indexes Insert Tables To insert data into tables Lock tables Databases To use LOCK TABLES (together with SELECT privilege) diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index edfd1a07680..48cbac10d46 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -48,9 +48,9 @@ GRANT SELECT, INSERT ON `mysqltest`.* TO 'mysqltest_1'@'localhost' use mysqltest; insert into t1 values (1, 'I can''t change it!'); update t1 set data='I can change it!' where id = 1; -ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest' +ERROR 42000: update command denied to user 'mysqltest_1'@'localhost' for table 't1' insert into t1 values (1, 'XXX') on duplicate key update data= 'I can change it!'; -ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest' +ERROR 42000: update command denied to user 'mysqltest_1'@'localhost' for table 't1' select * from t1; id data 1 I can't change it! @@ -202,7 +202,7 @@ drop user '%@a'@'a'; create user mysqltest_2@localhost; grant usage on *.* to mysqltest_2@localhost with grant option; select host,user,password from mysql.user where user like 'mysqltest_%' order by host,user,password; -ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysql' +ERROR 42000: select command denied to user 'mysqltest_2'@'localhost' for table 'user' create user mysqltest_A@'%'; rename user mysqltest_A@'%' to mysqltest_B@'%'; drop user mysqltest_B@'%'; diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 0bd3ba14636..6cd58674a3c 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -58,6 +58,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second @@ -346,8 +347,11 @@ GRANTEE TABLE_CATALOG TABLE_SCHEMA PRIVILEGE_TYPE IS_GRANTABLE 'mysqltest_1'@'localhost' NULL test ALTER YES 'mysqltest_1'@'localhost' NULL test CREATE TEMPORARY TABLES YES 'mysqltest_1'@'localhost' NULL test LOCK TABLES YES +'mysqltest_1'@'localhost' NULL test EXECUTE YES 'mysqltest_1'@'localhost' NULL test CREATE VIEW YES 'mysqltest_1'@'localhost' NULL test SHOW VIEW YES +'mysqltest_1'@'localhost' NULL test CREATE ROUTINE YES +'mysqltest_1'@'localhost' NULL test ALTER ROUTINE YES select * from information_schema.TABLE_PRIVILEGES where grantee like '%mysqltest_1%'; GRANTEE TABLE_CATALOG TABLE_SCHEMA TABLE_NAME PRIVILEGE_TYPE IS_GRANTABLE 'mysqltest_1'@'localhost' NULL test t1 SELECT NO @@ -600,6 +604,8 @@ Process_priv select,insert,update,references Show_db_priv select,insert,update,references Lock_tables_priv select,insert,update,references Show_view_priv select,insert,update,references +Create_routine_priv select,insert,update,references +Alter_routine_priv select,insert,update,references max_questions select,insert,update,references max_connections select,insert,update,references use test; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index ec9bd33d301..c1cabce7e92 100644 --- a/mysql-test/r/show_check.result +++ b/mysql-test/r/show_check.result @@ -382,19 +382,19 @@ show create database mysqltest; Database Create Database mysqltest CREATE DATABASE `mysqltest` /*!40100 DEFAULT CHARACTER SET latin1 */ drop table t1; -ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest' +ERROR 42000: drop command denied to user 'mysqltest_1'@'localhost' for table 't1' drop database mysqltest; ERROR 42000: Access denied for user 'mysqltest_1'@'localhost' to database 'mysqltest' select * from mysqltest.t1; -ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest' +ERROR 42000: select command denied to user 'mysqltest_2'@'localhost' for table 't1' show create database mysqltest; ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest' drop table mysqltest.t1; -ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest' +ERROR 42000: drop command denied to user 'mysqltest_2'@'localhost' for table 't1' drop database mysqltest; ERROR 42000: Access denied for user 'mysqltest_2'@'localhost' to database 'mysqltest' select * from mysqltest.t1; -ERROR 42000: Access denied for user 'mysqltest_3'@'localhost' to database 'mysqltest' +ERROR 42000: select command denied to user 'mysqltest_3'@'localhost' for table 't1' show create database mysqltest; ERROR 42000: Access denied for user 'mysqltest_3'@'localhost' to database 'mysqltest' drop table mysqltest.t1; diff --git a/mysql-test/r/sp-security.result b/mysql-test/r/sp-security.result index 25582796812..365368873fa 100644 --- a/mysql-test/r/sp-security.result +++ b/mysql-test/r/sp-security.result @@ -23,12 +23,16 @@ root@localhost 1 select db(); db() db1_secret +grant execute on db1_secret.stamp to user1@'%'; +grant execute on db1_secret.db to user1@'%'; +grant execute on db1_secret.stamp to ''@'%'; +grant execute on db1_secret.db to ''@'%'; call db1_secret.stamp(2); select db1_secret.db(); db1_secret.db() db1_secret select * from db1_secret.t1; -ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret' +ERROR 42000: select command denied to user 'user1'@'localhost' for table 't1' create procedure db1_secret.dummy() begin end; ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db1_secret' drop procedure db1_secret.dummy; @@ -38,7 +42,7 @@ select db1_secret.db(); db1_secret.db() db1_secret select * from db1_secret.t1; -ERROR 42000: Access denied for user ''@'localhost' to database 'db1_secret' +ERROR 42000: select command denied to user ''@'localhost' for table 't1' create procedure db1_secret.dummy() begin end; ERROR 42000: Access denied for user ''@'localhost' to database 'db1_secret' drop procedure db1_secret.dummy; @@ -82,15 +86,16 @@ insert into t2 values (0); grant usage on db2.* to user1@localhost; grant select on db2.* to user1@localhost; grant usage on db2.* to user2@localhost; -grant select,insert,update,delete on db2.* to user2@localhost; +grant select,insert,update,delete,create routine on db2.* to user2@localhost; +grant create routine on db2.* to user1@localhost; flush privileges; use db2; create procedure p () insert into t2 values (1); call p(); -ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db2' +ERROR 42000: insert command denied to user 'user1'@'localhost' for table 't2' use db2; call p(); -ERROR 42000: Access denied for user 'user1'@'localhost' to database 'db2' +ERROR 42000: execute command denied to user 'user2'@'localhost' for routine 'db2.p' select * from t2; s1 0 @@ -100,6 +105,8 @@ select * from t2; s1 0 2 +grant usage on db2.q to user2@localhost with grant option; +grant execute on db2.q to user1@localhost; use db2; call q(); select * from t2; @@ -110,9 +117,9 @@ s1 alter procedure p modifies sql data; drop procedure p; alter procedure q modifies sql data; -ERROR 42000: Access denied; you are not the procedure/function definer of 'db2.q' +ERROR 42000: alter procedure command denied to user 'user1'@'localhost' for routine 'db2.q' drop procedure q; -ERROR 42000: Access denied; you are not the procedure/function definer of 'db2.q' +ERROR 42000: alter procedure command denied to user 'user1'@'localhost' for routine 'db2.q' use db2; alter procedure q modifies sql data; drop procedure q; @@ -126,3 +133,64 @@ drop database db2; select type,db,name from mysql.proc; type db name delete from mysql.user where user='user1' or user='user2'; +delete from mysql.procs_priv where user='user1' or user='user2'; +grant usage on *.* to usera@localhost; +grant usage on *.* to userb@localhost; +grant usage on *.* to userc@localhost; +create database sptest; +create table t1 ( u varchar(64), i int ); +create procedure sptest.p1(i int) insert into test.t1 values (user(), i); +grant insert on t1 to usera@localhost; +grant execute on sptest.p1 to usera@localhost; +show grants for usera@localhost; +Grants for usera@localhost +GRANT USAGE ON *.* TO 'usera'@'localhost' +GRANT INSERT ON `test`.`t1` TO 'usera'@'localhost' +GRANT EXECUTE ON `sptest`.`p1` TO 'usera'@'localhost' +grant execute on sptest.p1 to userc@localhost with grant option; +show grants for userc@localhost; +Grants for userc@localhost +GRANT USAGE ON *.* TO 'userc'@'localhost' +GRANT EXECUTE ON `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION +call sptest.p1(1); +grant execute on sptest.p1 to userb@localhost; +ERROR 42000: grant command denied to user 'usera'@'localhost' for routine 'sptest.p1' +drop procedure sptest.p1; +ERROR 42000: alter procedure command denied to user 'usera'@'localhost' for routine 'sptest.p1' +call sptest.p1(2); +ERROR 42000: execute command denied to user 'userb'@'localhost' for routine 'sptest.p1' +grant execute on sptest.p1 to userb@localhost; +ERROR 42000: execute command denied to user 'userb'@'localhost' for routine 'sptest.p1' +drop procedure sptest.p1; +ERROR 42000: alter procedure command denied to user 'userb'@'localhost' for routine 'sptest.p1' +call sptest.p1(3); +grant execute on sptest.p1 to userb@localhost; +drop procedure sptest.p1; +ERROR 42000: alter procedure command denied to user 'userc'@'localhost' for routine 'sptest.p1' +call sptest.p1(4); +grant execute on sptest.p1 to userb@localhost; +ERROR 42000: grant command denied to user 'userb'@'localhost' for routine 'sptest.p1' +drop procedure sptest.p1; +ERROR 42000: alter procedure command denied to user 'userb'@'localhost' for routine 'sptest.p1' +select * from t1; +u i +usera@localhost 1 +userc@localhost 3 +userb@localhost 4 +grant all privileges on sptest.p1 to userc@localhost; +show grants for userc@localhost; +Grants for userc@localhost +GRANT USAGE ON *.* TO 'userc'@'localhost' +GRANT EXECUTE, ALTER ROUTINE ON `sptest`.`p1` TO 'userc'@'localhost' WITH GRANT OPTION +show grants for userb@localhost; +Grants for userb@localhost +GRANT USAGE ON *.* TO 'userb'@'localhost' +GRANT EXECUTE ON `sptest`.`p1` TO 'userb'@'localhost' +revoke all privileges on sptest.p1 from userb@localhost; +show grants for userb@localhost; +Grants for userb@localhost +GRANT USAGE ON *.* TO 'userb'@'localhost' +use test; +drop database sptest; +delete from mysql.user where user='usera' or user='userb' or user='userc'; +delete from mysql.procs_priv where user='usera' or user='userb' or user='userc'; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 0d7f9f7d50c..269c4104a05 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -1654,13 +1654,16 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par Database Table In_use Name_locked Privilege Context Comment Alter Tables To alter the table +Alter routine Functions,Procedures To alter or drop stored functions/procedures Create Databases,Tables,Indexes To create new databases and tables +Create routine Functions,Procedures To use CREATE FUNCTION/PROCEDURE Create temporary tables Databases To use CREATE TEMPORARY TABLE Create view Tables To create new views Delete Tables To delete existing rows Drop Databases,Tables To drop databases, tables, and views +Execute Functions,Procedures To execute stored routines File File access on server To read and write files on the server -Grant option Databases,Tables To give to other users those privileges you possess +Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess Index Tables To create or drop indexes Insert Tables To insert data into tables Lock tables Databases To use LOCK TABLES (together with SELECT privilege) @@ -1704,13 +1707,16 @@ Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_par Database Table In_use Name_locked Privilege Context Comment Alter Tables To alter the table +Alter routine Functions,Procedures To alter or drop stored functions/procedures Create Databases,Tables,Indexes To create new databases and tables +Create routine Functions,Procedures To use CREATE FUNCTION/PROCEDURE Create temporary tables Databases To use CREATE TEMPORARY TABLE Create view Tables To create new views Delete Tables To delete existing rows Drop Databases,Tables To drop databases, tables, and views +Execute Functions,Procedures To execute stored routines File File access on server To read and write files on the server -Grant option Databases,Tables To give to other users those privileges you possess +Grant option Databases,Tables,Functions,Procedures To give to other users those privileges you possess Index Tables To create or drop indexes Insert Tables To insert data into tables Lock tables Databases To use LOCK TABLES (together with SELECT privilege) diff --git a/mysql-test/r/system_mysql_db.result b/mysql-test/r/system_mysql_db.result index 400b508ff50..e9606ec5f88 100644 --- a/mysql-test/r/system_mysql_db.result +++ b/mysql-test/r/system_mysql_db.result @@ -9,6 +9,7 @@ help_relation help_topic host proc +procs_priv tables_priv time_zone time_zone_leap_second @@ -36,6 +37,9 @@ db CREATE TABLE `db` ( `Lock_tables_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `Create_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `Show_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Create_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Alter_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Execute_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', PRIMARY KEY (`Host`,`Db`,`User`), KEY `User` (`User`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Database privileges' @@ -89,6 +93,8 @@ user CREATE TABLE `user` ( `Repl_client_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `Create_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `Show_view_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Create_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', + `Alter_routine_priv` enum('N','Y') collate utf8_bin NOT NULL default 'N', `ssl_type` enum('','ANY','X509','SPECIFIED') collate utf8_bin NOT NULL default '', `ssl_cipher` blob NOT NULL, `x509_issuer` blob NOT NULL, @@ -133,5 +139,18 @@ columns_priv CREATE TABLE `columns_priv` ( `Column_priv` set('Select','Insert','Update','References') collate utf8_bin NOT NULL default '', PRIMARY KEY (`Host`,`Db`,`User`,`Table_name`,`Column_name`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Column privileges' +show create table procs_priv; +Table Create Table +procs_priv CREATE TABLE `procs_priv` ( + `Host` char(60) collate utf8_bin NOT NULL default '', + `Db` char(64) collate utf8_bin NOT NULL default '', + `User` char(16) collate utf8_bin NOT NULL default '', + `Routine_name` char(64) collate utf8_bin NOT NULL default '', + `Grantor` char(77) collate utf8_bin NOT NULL default '', + `Timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP, + `Proc_priv` set('Execute','Alter Routine','Grant') collate utf8_bin NOT NULL default '', + PRIMARY KEY (`Host`,`Db`,`User`,`Routine_name`), + KEY `Grantor` (`Grantor`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Procedure privileges' show tables; Tables_in_test diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test index 69c42ce2252..ad3fc1c228e 100644 --- a/mysql-test/t/grant2.test +++ b/mysql-test/t/grant2.test @@ -64,10 +64,10 @@ connection mrbad; show grants for current_user(); use mysqltest; insert into t1 values (1, 'I can''t change it!'); ---error 1044 +--error 1142 update t1 set data='I can change it!' where id = 1; # This should not be allowed since it too require UPDATE privilege. ---error 1044 +--error 1142 insert into t1 values (1, 'XXX') on duplicate key update data= 'I can change it!'; select * from t1; @@ -199,7 +199,7 @@ create user mysqltest_2@localhost; grant usage on *.* to mysqltest_2@localhost with grant option; connect (user2,localhost,mysqltest_2,,); connection user2; ---error 1044 +--error 1142 select host,user,password from mysql.user where user like 'mysqltest_%' order by host,user,password; create user mysqltest_A@'%'; rename user mysqltest_A@'%' to mysqltest_B@'%'; diff --git a/mysql-test/t/show_check.test b/mysql-test/t/show_check.test index 8680da9b31a..566f9f625df 100644 --- a/mysql-test/t/show_check.test +++ b/mysql-test/t/show_check.test @@ -287,25 +287,25 @@ connect (con1,localhost,mysqltest_1,,mysqltest); connection con1; select * from t1; show create database mysqltest; ---error 1044 +--error 1142 drop table t1; --error 1044 drop database mysqltest; connect (con2,localhost,mysqltest_2,,test); connection con2; ---error 1044 +--error 1142 select * from mysqltest.t1; --error 1044 show create database mysqltest; ---error 1044 +--error 1142 drop table mysqltest.t1; --error 1044 drop database mysqltest; connect (con3,localhost,mysqltest_3,,test); connection con3; ---error 1044 +--error 1142 select * from mysqltest.t1; --error 1044 show create database mysqltest; diff --git a/mysql-test/t/sp-security.test b/mysql-test/t/sp-security.test index d1119499cf1..aad5f4eaf9e 100644 --- a/mysql-test/t/sp-security.test +++ b/mysql-test/t/sp-security.test @@ -40,6 +40,11 @@ call stamp(1); select * from t1; select db(); +grant execute on db1_secret.stamp to user1@'%'; +grant execute on db1_secret.db to user1@'%'; +grant execute on db1_secret.stamp to ''@'%'; +grant execute on db1_secret.db to ''@'%'; + connect (con2user1,localhost,user1,,); connect (con3anon,localhost,anon,,); @@ -54,7 +59,7 @@ call db1_secret.stamp(2); select db1_secret.db(); # ...but not this ---error 1044 +--error 1142 select * from db1_secret.t1; # ...and not this @@ -74,7 +79,7 @@ call db1_secret.stamp(3); select db1_secret.db(); # ...but not this ---error 1044 +--error 1142 select * from db1_secret.t1; # ...and not this @@ -146,7 +151,8 @@ insert into t2 values (0); grant usage on db2.* to user1@localhost; grant select on db2.* to user1@localhost; grant usage on db2.* to user2@localhost; -grant select,insert,update,delete on db2.* to user2@localhost; +grant select,insert,update,delete,create routine on db2.* to user2@localhost; +grant create routine on db2.* to user1@localhost; flush privileges; connection con2user1; @@ -155,7 +161,7 @@ use db2; create procedure p () insert into t2 values (1); # Check that this doesn't work. ---error 1044 +--error 1142 call p(); connect (con4user2,localhost,user2,,); @@ -164,7 +170,7 @@ connection con4user2; use db2; # This should not work, since p is executed with definer's (user1's) rights. ---error 1044 +--error 1370 call p(); select * from t2; @@ -173,6 +179,12 @@ create procedure q () insert into t2 values (2); call q(); select * from t2; +connection con1root; +grant usage on db2.q to user2@localhost with grant option; + +connection con4user2; +grant execute on db2.q to user1@localhost; + connection con2user1; use db2; @@ -206,6 +218,9 @@ drop procedure q; # Clean up #Still connection con1root; +disconnect con2user1; +disconnect con3anon; +disconnect con4user2; use test; select type,db,name from mysql.proc; drop database db1_secret; @@ -214,3 +229,75 @@ drop database db2; select type,db,name from mysql.proc; # Get rid of the users delete from mysql.user where user='user1' or user='user2'; +# And any routine privileges +delete from mysql.procs_priv where user='user1' or user='user2'; + +# +# Test the new security acls +# +grant usage on *.* to usera@localhost; +grant usage on *.* to userb@localhost; +grant usage on *.* to userc@localhost; +create database sptest; +create table t1 ( u varchar(64), i int ); +create procedure sptest.p1(i int) insert into test.t1 values (user(), i); +grant insert on t1 to usera@localhost; +grant execute on sptest.p1 to usera@localhost; +show grants for usera@localhost; +grant execute on sptest.p1 to userc@localhost with grant option; +show grants for userc@localhost; + +connect (con2usera,localhost,usera,,); +connect (con3userb,localhost,userb,,); +connect (con4userc,localhost,userc,,); + +connection con2usera; +call sptest.p1(1); +--error 1370 +grant execute on sptest.p1 to userb@localhost; +--error 1370 +drop procedure sptest.p1; + +connection con3userb; +--error 1370 +call sptest.p1(2); +--error 1370 +grant execute on sptest.p1 to userb@localhost; +--error 1370 +drop procedure sptest.p1; + +connection con4userc; +call sptest.p1(3); +grant execute on sptest.p1 to userb@localhost; +--error 1370 +drop procedure sptest.p1; + +connection con3userb; +call sptest.p1(4); +--error 1370 +grant execute on sptest.p1 to userb@localhost; +--error 1370 +drop procedure sptest.p1; + +connection con1root; +select * from t1; + +grant all privileges on sptest.p1 to userc@localhost; +show grants for userc@localhost; +show grants for userb@localhost; + +connection con4userc; +revoke all privileges on sptest.p1 from userb@localhost; + +connection con1root; +show grants for userb@localhost; + +#cleanup +disconnect con4userc; +disconnect con3userb; +disconnect con2usera; +use test; +drop database sptest; +delete from mysql.user where user='usera' or user='userb' or user='userc'; +delete from mysql.procs_priv where user='usera' or user='userb' or user='userc'; + diff --git a/mysql-test/t/system_mysql_db_fix.test b/mysql-test/t/system_mysql_db_fix.test index e34dbefbcba..56f291ae69d 100644 --- a/mysql-test/t/system_mysql_db_fix.test +++ b/mysql-test/t/system_mysql_db_fix.test @@ -74,7 +74,7 @@ INSERT INTO user VALUES ('localhost','', '','N','N','N','N','N','N','N','N',' -- disable_query_log -DROP TABLE db, host, user, func, tables_priv, columns_priv, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type; +DROP TABLE db, host, user, func, tables_priv, columns_priv, procs_priv, help_category, help_keyword, help_relation, help_topic, proc, time_zone, time_zone_leap_second, time_zone_name, time_zone_transition, time_zone_transition_type; -- enable_query_log diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh index 7a4da55f851..be99a081bcb 100644 --- a/scripts/mysql_create_system_tables.sh +++ b/scripts/mysql_create_system_tables.sh @@ -41,7 +41,7 @@ c_hk="" i_ht="" c_tzn="" c_tz="" c_tzt="" c_tztt="" c_tzls="" i_tzn="" i_tz="" i_tzt="" i_tztt="" i_tzls="" -c_p="" +c_p="" c_pp="" # Check for old tables if test ! -f $mdata/db.frm @@ -69,14 +69,17 @@ then c_d="$c_d Lock_tables_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_d="$c_d Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_d="$c_d Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_d="$c_d Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_d="$c_d Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_d="$c_d Execute_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_d="$c_d PRIMARY KEY Host (Host,Db,User)," c_d="$c_d KEY User (User)" c_d="$c_d ) engine=MyISAM" c_d="$c_d CHARACTER SET utf8 COLLATE utf8_bin" c_d="$c_d comment='Database privileges';" - i_d="INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y'); - INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y');" + i_d="INSERT INTO db VALUES ('%','test','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N'); + INSERT INTO db VALUES ('%','test\_%','','Y','Y','Y','Y','Y','Y','N','Y','Y','Y','Y','Y','Y','Y','Y','N','N');" fi if test ! -f $mdata/host.frm @@ -141,6 +144,8 @@ then c_u="$c_u Repl_client_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_u="$c_u Create_view_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_u="$c_u Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_u="$c_u Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL," + c_u="$c_u Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL," c_u="$c_u ssl_type enum('','ANY','X509', 'SPECIFIED') DEFAULT '' NOT NULL," c_u="$c_u ssl_cipher BLOB NOT NULL," c_u="$c_u x509_issuer BLOB NOT NULL," @@ -155,24 +160,24 @@ then if test "$1" = "test" then - i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); - INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); - REPLACE INTO user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + REPLACE INTO user VALUES ('127.0.0.1','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); INSERT INTO user (host,user) values ('localhost',''); INSERT INTO user (host,user) values ('$hostname','');" else - i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);" + i_u="INSERT INTO user VALUES ('localhost','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0);" if test "$windows" = "0" then i_u="$i_u - INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + INSERT INTO user VALUES ('$hostname','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); INSERT INTO user (host,user) values ('$hostname',''); INSERT INTO user (host,user) values ('localhost','');" else i_u="$i_u - INSERT INTO user VALUES ('%','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); - INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); - INSERT INTO user VALUES ('%','','','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);" + INSERT INTO user VALUES ('%','root','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + INSERT INTO user VALUES ('localhost','','','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','','','','',0,0,0); + INSERT INTO user VALUES ('%','','','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);" fi fi fi @@ -236,6 +241,27 @@ then c_c="$c_c comment='Column privileges';" fi +if test ! -f $mdata/procs_priv.frm +then + if test "$1" = "verbose" ; then + echo "Preparing procs_priv table" 1>&2; + fi + + c_pp="$c_pp CREATE TABLE procs_priv (" + c_pp="$c_pp Host char(60) binary DEFAULT '' NOT NULL," + c_pp="$c_pp Db char(64) binary DEFAULT '' NOT NULL," + c_pp="$c_pp User char(16) binary DEFAULT '' NOT NULL," + c_pp="$c_pp Routine_name char(64) binary DEFAULT '' NOT NULL," + c_pp="$c_pp Grantor char(77) DEFAULT '' NOT NULL," + c_pp="$c_pp Timestamp timestamp(14)," + c_pp="$c_pp Proc_priv set('Execute','Alter Routine','Grant') DEFAULT '' NOT NULL," + c_pp="$c_pp PRIMARY KEY (Host,Db,User,Routine_name)," + c_pp="$c_pp KEY Grantor (Grantor)" + c_pp="$c_pp ) engine=MyISAM" + c_pp="$c_pp CHARACTER SET utf8 COLLATE utf8_bin" + c_pp="$c_pp comment='Procedure privileges';" +fi + if test ! -f $mdata/help_topic.frm then if test "$1" = "verbose" ; then @@ -718,6 +744,7 @@ $c_tzls $i_tzls $c_p +$c_pp END_OF_DATA diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql index cae6a1d07b9..d4f095f5201 100644 --- a/scripts/mysql_fix_privilege_tables.sql +++ b/scripts/mysql_fix_privilege_tables.sql @@ -15,6 +15,7 @@ ALTER TABLE host type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; ALTER TABLE func type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; ALTER TABLE columns_priv type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; ALTER TABLE tables_priv type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; +ALTER TABLE procs_priv type=MyISAM, CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin; ALTER TABLE user change Password Password char(41) binary not null default ''; ALTER TABLE user add File_priv enum('N','Y') NOT NULL; CREATE TABLE IF NOT EXISTS func ( @@ -170,9 +171,47 @@ ALTER TABLE user ADD Show_view_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Cre # UPDATE user SET Create_view_priv=Create_priv, Show_view_priv=Create_priv where user<>"" AND @hadCreateViewPriv = 0; +# +# +# +SET @hadCreateRoutinePriv:=0; +SELECT @hadCreateRoutinePriv:=1 FROM user WHERE Create_routine_priv LIKE '%'; + +# +# Create PROCEDUREs privileges (v5.0) +# +ALTER TABLE db ADD Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Show_view_priv; +ALTER TABLE user ADD Create_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Show_view_priv; + +# +# Alter PROCEDUREs privileges (v5.0) +# +ALTER TABLE db ADD Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_routine_priv; +ALTER TABLE user ADD Alter_routine_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Create_routine_priv; + +ALTER TABLE db ADD Execute_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Alter_routine_priv; + +# +# Assign create/alter routine privileges to people who have create privileges +# +UPDATE user SET Create_routine_priv=Create_priv, Alter_routine_priv=Alter_priv where user<>"" AND @hadCreateRoutinePriv = 0; +UPDATE db SET Create_routine_priv=Create_priv, Alter_routine_priv=Alter_priv, Execute_priv=Select_priv where user<>"" AND @hadCreateRoutinePriv = 0; + # # Create some possible missing tables # +CREATE TABLE IF NOT EXISTS procs_priv ( +Host char(60) binary DEFAULT '' NOT NULL, +Db char(64) binary DEFAULT '' NOT NULL, +User char(16) binary DEFAULT '' NOT NULL, +Routine_name char(64) binary DEFAULT '' NOT NULL, +Grantor char(77) DEFAULT '' NOT NULL, +Timestamp timestamp(14), +Proc_priv set('Execute','Alter Routine','Grant') DEFAULT '' NOT NULL, +PRIMARY KEY (Host,Db,User,Routine_name), +KEY Grantor (Grantor) +) CHARACTER SET utf8 COLLATE utf8_bin comment='Procedure privileges'; + CREATE TABLE IF NOT EXISTS help_topic ( help_topic_id int unsigned not null, name varchar(64) not null, diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh index b4f59790e73..3be1320f0b0 100644 --- a/scripts/mysql_install_db.sh +++ b/scripts/mysql_install_db.sh @@ -3,7 +3,7 @@ # For a more info consult the file COPYRIGHT distributed with this file. # This scripts creates the privilege tables db, host, user, tables_priv, -# columns_priv in the mysql database, as well as the func table. +# columns_priv, procs_priv in the mysql database, as well as the func table. # # All unrecognized arguments to this script are passed to mysqld. diff --git a/sql/item_func.cc b/sql/item_func.cc index aba53b9b397..35cced090e6 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -22,6 +22,7 @@ #endif #include "mysql_priv.h" +#include "sql_acl.h" #include "slave.h" // for wait_for_master_pos #include #include @@ -3546,7 +3547,17 @@ Item_func_sp::execute(Item **itp) } #ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_procedure_access(thd, EXECUTE_ACL, + m_sp->m_db.str, m_sp->m_name.str, 0)) + DBUG_RETURN(-1); sp_change_security_context(thd, m_sp, &save_ctx); + if (save_ctx.changed && + check_procedure_access(thd, EXECUTE_ACL, + m_sp->m_db.str, m_sp->m_name.str, 0)) + { + sp_restore_security_context(thd, m_sp, &save_ctx); + DBUG_RETURN(-1); + } #endif /* diff --git a/sql/lex.h b/sql/lex.h index cf0059a1397..5b6c86bf0ed 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -399,6 +399,7 @@ static SYMBOL symbols[] = { { "RLIKE", SYM(REGEXP)}, /* Like in mSQL2 */ { "ROLLBACK", SYM(ROLLBACK_SYM)}, { "ROLLUP", SYM(ROLLUP_SYM)}, + { "ROUTINE", SYM(ROUTINE_SYM)}, { "ROW", SYM(ROW_SYM)}, { "ROWS", SYM(ROWS_SYM)}, { "ROW_FORMAT", SYM(ROW_FORMAT_SYM)}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 2fc82e05f31..fc06893c281 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -420,6 +420,8 @@ void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0, TABLE *stopper= 0); bool check_one_table_access(THD *thd, ulong privilege, TABLE_LIST *tables); +bool check_procedure_access(THD *thd,ulong want_access,char *db,char *name, + bool no_errors); bool check_some_access(THD *thd, ulong want_access, TABLE_LIST *table); bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *table_list); @@ -1024,6 +1026,7 @@ extern my_bool opt_slave_compressed_protocol, use_temp_pool; extern my_bool opt_readonly, lower_case_file_system; extern my_bool opt_enable_named_pipe, opt_sync_frm; extern my_bool opt_secure_auth; +extern my_bool sp_automatic_privileges; extern uint opt_crash_binlog_innodb; extern char *shared_memory_base_name, *mysqld_unix_port; extern bool opt_enable_shared_memory; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index c5698469341..8eec97efd81 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -299,6 +299,7 @@ my_bool opt_innodb_safe_binlog= 0; my_bool opt_large_pages= 0; uint opt_large_page_size= 0; volatile bool mqh_used = 0; +my_bool sp_automatic_privileges= 1; uint mysqld_port, test_flags, select_errors, dropping_tables, ha_open_options; uint delay_key_write_options, protocol_version; @@ -4199,6 +4200,7 @@ enum options_mysqld OPT_OPTIMIZER_SEARCH_DEPTH, OPT_OPTIMIZER_PRUNE_LEVEL, OPT_UPDATABLE_VIEWS_WITH_LIMIT, + OPT_SP_AUTOMATIC_PRIVILEGES, OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET, OPT_ENABLE_LARGE_PAGES }; @@ -4229,6 +4231,10 @@ struct my_option my_long_options[] = (gptr*) &global_system_variables.auto_increment_offset, (gptr*) &max_system_variables.auto_increment_offset, 0, GET_ULONG, OPT_ARG, 1, 1, 65535, 0, 1, 0 }, + {"automatic-sp-privileges", OPT_SP_AUTOMATIC_PRIVILEGES, + "Creating and dropping stored procedures alters ACLs. Disable with --skip-automatic-sp-privileges.", + (gptr*) &sp_automatic_privileges, (gptr*) &sp_automatic_privileges, + 0, GET_BOOL, NO_ARG, 1, 0, 0, 0, 0, 0}, {"basedir", 'b', "Path to installation directory. All paths are usually resolved relative to this.", (gptr*) &mysql_home_ptr, (gptr*) &mysql_home_ptr, 0, GET_STR, REQUIRED_ARG, @@ -6128,6 +6134,7 @@ get_one_option(int optid, const struct my_option *opt __attribute__((unused)), delay_key_write_options= (uint) DELAY_KEY_WRITE_NONE; myisam_concurrent_insert=0; myisam_recover_options= HA_RECOVER_NONE; + sp_automatic_privileges=0; my_use_symdir=0; ha_open_options&= ~(HA_OPEN_ABORT_IF_CRASHED | HA_OPEN_DELAY_KEY_WRITE); #ifdef HAVE_QUERY_CACHE diff --git a/sql/set_var.cc b/sql/set_var.cc index da6341597f1..b42ca91c7dd 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -133,6 +133,9 @@ sys_var_thd_ulong sys_auto_increment_increment("auto_increment_increment", sys_var_thd_ulong sys_auto_increment_offset("auto_increment_offset", &SV::auto_increment_offset); +sys_var_bool_ptr sys_automatic_sp_privileges("automatic_sp_privileges", + &sp_automatic_privileges); + sys_var_long_ptr sys_binlog_cache_size("binlog_cache_size", &binlog_cache_size); sys_var_thd_ulong sys_bulk_insert_buff_size("bulk_insert_buffer_size", @@ -509,6 +512,7 @@ sys_var *sys_variables[]= &sys_auto_increment_increment, &sys_auto_increment_offset, &sys_autocommit, + &sys_automatic_sp_privileges, &sys_big_tables, &sys_big_selects, &sys_binlog_cache_size, @@ -668,6 +672,7 @@ sys_var *sys_variables[]= struct show_var_st init_vars[]= { {"auto_increment_increment", (char*) &sys_auto_increment_increment, SHOW_SYS}, {"auto_increment_offset", (char*) &sys_auto_increment_offset, SHOW_SYS}, + {sys_automatic_sp_privileges.name,(char*) &sys_automatic_sp_privileges, SHOW_SYS}, {"back_log", (char*) &back_log, SHOW_LONG}, {"basedir", mysql_home, SHOW_CHAR}, #ifdef HAVE_BERKELEY_DB diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt index c93e203f0d5..5b48f27d2e3 100644 --- a/sql/share/errmsg.txt +++ b/sql/share/errmsg.txt @@ -5168,8 +5168,8 @@ ER_VIEW_CHECK_FAILED eng "CHECK OPTION failed '%-.64s.%-.64s'" rus "проверка CHECK OPTION для VIEW '%-.64s.%-.64s' провалилась" ukr "Перев╕рка CHECK OPTION для VIEW '%-.64s.%-.64s' не пройшла" -ER_SP_ACCESS_DENIED_ERROR 42000 - eng "Access denied; you are not the procedure/function definer of '%s'" +ER_PROCACCESS_DENIED_ERROR 42000 + eng "%-.16s command denied to user '%-.32s'@'%-.64s' for routine '%-.64s'" ER_RELAY_LOG_FAIL eng "Failed purging old relay logs: %s" ER_PASSWD_LENGTH @@ -5232,3 +5232,9 @@ ER_CANNOT_USER eng "Operation %s failed for %.256s" ger "Das Kommando %s scheiterte fЭr %.256s" norwegian-ny "Operation %s failed for '%.256s'" +ER_NONEXISTING_PROC_GRANT 42000 + eng "There is no such grant defined for user '%-.32s' on host '%-.64s' on routine '%-.64s'" +ER_PROC_AUTO_GRANT_FAIL + eng "Failed to grant EXECUTE and ALTER ROUTINE privileges" +ER_PROC_AUTO_REVOKE_FAIL + eng "Failed to revoke all privileges to dropped routine" diff --git a/sql/sp.cc b/sql/sp.cc index 4605d49f3ab..88693de0497 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -738,6 +738,45 @@ sp_find_procedure(THD *thd, sp_name *name) } +int +sp_exists_routine(THD *thd, TABLE_LIST *tables, bool any, bool no_error) +{ + TABLE_LIST *table; + bool result= 0; + DBUG_ENTER("sp_exists_routine"); + for (table= tables; table; table= table->next_global) + { + sp_name *name; + LEX_STRING lex_db; + LEX_STRING lex_name; + lex_db.length= strlen(table->db); + lex_name.length= strlen(table->real_name); + lex_db.str= thd->strmake(table->db, lex_db.length); + lex_name.str= thd->strmake(table->real_name, lex_name.length); + name= new sp_name(lex_db, lex_name); + name->init_qname(thd); + if (sp_find_procedure(thd, name) != NULL || + sp_find_function(thd, name) != NULL) + { + if (any) + DBUG_RETURN(1); + result= 1; + } + else if (!any) + { + if (!no_error) + { + my_error(ER_SP_DOES_NOT_EXIST, MYF(0), "FUNCTION or PROCEDURE", + table->real_name); + DBUG_RETURN(-1); + } + DBUG_RETURN(0); + } + } + DBUG_RETURN(result); +} + + int sp_create_procedure(THD *thd, sp_head *sp) { diff --git a/sql/sp.h b/sql/sp.h index acdfe824b97..152c59d0d02 100644 --- a/sql/sp.h +++ b/sql/sp.h @@ -36,6 +36,9 @@ sp_drop_db_routines(THD *thd, char *db); sp_head * sp_find_procedure(THD *thd, sp_name *name); +int +sp_exists_routine(THD *thd, TABLE_LIST *procs, bool any, bool no_error); + int sp_create_procedure(THD *thd, sp_head *sp); diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index d33faffb2c2..4dcd15741cb 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -33,6 +33,8 @@ #endif #include #include +#include "sp_head.h" +#include "sp.h" #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -59,7 +61,7 @@ static DYNAMIC_ARRAY acl_hosts,acl_users,acl_dbs; static MEM_ROOT mem, memex; static bool initialized=0; static bool allow_all_hosts=1; -static HASH acl_check_hosts, column_priv_hash; +static HASH acl_check_hosts, column_priv_hash, proc_priv_hash; static DYNAMIC_ARRAY acl_wild_hosts; static hash_filo *acl_cache; static uint grant_version=0; @@ -307,6 +309,16 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables) */ if (table->fields <= 31 && (user.access & CREATE_ACL)) user.access|= (CREATE_VIEW_ACL | SHOW_VIEW_ACL); + + /* + if it is pre 5.0.2 privilege table then map CREATE/ALTER privilege on + CREATE PROCEDURE & ALTER PROCEDURE privileges + */ + if (table->fields <= 33 && (user.access & CREATE_ACL)) + user.access|= CREATE_PROC_ACL; + if (table->fields <= 33 && (user.access & ALTER_ACL)) + user.access|= ALTER_PROC_ACL; + user.sort= get_sort(2,user.host.hostname,user.user); user.hostname_length= (user.host.hostname ? (uint) strlen(user.host.hostname) : 0); @@ -1859,13 +1871,25 @@ static byte* get_key_column(GRANT_COLUMN *buff,uint *length, } -class GRANT_TABLE :public Sql_alloc +class GRANT_NAME :public Sql_alloc { public: char *host,*db, *user, *tname, *hash_key, *orig_host; - ulong privs, cols; + ulong privs; ulong sort; uint key_length; + GRANT_NAME(const char *h, const char *d,const char *u, + const char *t, ulong p); + GRANT_NAME (TABLE *form); + virtual ~GRANT_NAME() {}; + virtual bool ok() { return privs != 0; } +}; + + +class GRANT_TABLE :public GRANT_NAME +{ +public: + ulong cols; HASH hash_columns; GRANT_TABLE(const char *h, const char *d,const char *u, @@ -1877,9 +1901,9 @@ public: -GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, - const char *t, ulong p, ulong c) - :privs(p), cols(c) +GRANT_NAME::GRANT_NAME(const char *h, const char *d,const char *u, + const char *t, ulong p) + :privs(p) { /* Host given by user */ orig_host= strdup_root(&memex,h); @@ -1897,15 +1921,20 @@ GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, key_length =(uint) strlen(d)+(uint) strlen(u)+(uint) strlen(t)+3; hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); +} + + +GRANT_TABLE::GRANT_TABLE(const char *h, const char *d,const char *u, + const char *t, ulong p, ulong c) + :GRANT_NAME(h,d,u,t,p), cols(c) +{ (void) hash_init(&hash_columns,system_charset_info, 0,0,0, (hash_get_key) get_key_column,0,0); } -GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) +GRANT_NAME::GRANT_NAME(TABLE *form) { - byte key[MAX_KEY_LENGTH]; - orig_host= host= get_field(&memex, form->field[0]); db= get_field(&memex,form->field[1]); user= get_field(&memex,form->field[2]); @@ -1921,8 +1950,7 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) if (!db || !tname) { /* Wrong table row; Ignore it */ - hash_clear(&hash_columns); /* allow for destruction */ - privs= cols= 0; /* purecov: inspected */ + privs= 0; return; /* purecov: inspected */ } if (lower_case_table_names) @@ -1935,8 +1963,23 @@ GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) hash_key = (char*) alloc_root(&memex,key_length); strmov(strmov(strmov(hash_key,user)+1,db)+1,tname); privs = (ulong) form->field[6]->val_int(); - cols = (ulong) form->field[7]->val_int(); privs = fix_rights_for_table(privs); +} + + +GRANT_TABLE::GRANT_TABLE(TABLE *form, TABLE *col_privs) + :GRANT_NAME(form) +{ + byte key[MAX_KEY_LENGTH]; + + if (!db || !tname) + { + /* Wrong table row; Ignore it */ + hash_clear(&hash_columns); /* allow for destruction */ + cols= 0; + return; + } + cols= (ulong) form->field[7]->val_int(); cols = fix_rights_for_column(cols); (void) hash_init(&hash_columns,system_charset_info, @@ -1995,7 +2038,7 @@ GRANT_TABLE::~GRANT_TABLE() } -static byte* get_grant_table(GRANT_TABLE *buff,uint *length, +static byte* get_grant_table(GRANT_NAME *buff,uint *length, my_bool not_used __attribute__((unused))) { *length=buff->key_length; @@ -2011,44 +2054,62 @@ void free_grant_table(GRANT_TABLE *grant_table) /* Search after a matching grant. Prefer exact grants before not exact ones */ -static GRANT_TABLE *table_hash_search(const char *host,const char* ip, +static GRANT_NAME *name_hash_search(HASH *name_hash, + const char *host,const char* ip, const char *db, const char *user, const char *tname, bool exact) { char helping [NAME_LEN*2+USERNAME_LENGTH+3]; uint len; - GRANT_TABLE *grant_table,*found=0; + GRANT_NAME *grant_name,*found=0; len = (uint) (strmov(strmov(strmov(helping,user)+1,db)+1,tname)-helping)+ 1; - for (grant_table=(GRANT_TABLE*) hash_search(&column_priv_hash, + for (grant_name=(GRANT_NAME*) hash_search(name_hash, (byte*) helping, len) ; - grant_table ; - grant_table= (GRANT_TABLE*) hash_next(&column_priv_hash,(byte*) helping, + grant_name ; + grant_name= (GRANT_NAME*) hash_next(name_hash,(byte*) helping, len)) { if (exact) { if ((host && - !my_strcasecmp(system_charset_info, host, grant_table->host)) || - (ip && !strcmp(ip,grant_table->host))) - return grant_table; + !my_strcasecmp(system_charset_info, host, grant_name->host)) || + (ip && !strcmp(ip,grant_name->host))) + return grant_name; } else { if (((host && !wild_case_compare(system_charset_info, - host,grant_table->host)) || + host,grant_name->host)) || (ip && !wild_case_compare(system_charset_info, - ip,grant_table->host))) && - (!found || found->sort < grant_table->sort)) - found=grant_table; // Host ok + ip,grant_name->host))) && + (!found || found->sort < grant_name->sort)) + found=grant_name; // Host ok } } return found; } +inline GRANT_NAME * +proc_hash_search(const char *host, const char *ip, const char *db, + const char *user, const char *tname, bool exact) +{ + return (GRANT_TABLE*) name_hash_search(&proc_priv_hash, host, ip, db, + user, tname, exact); +} + + +inline GRANT_TABLE * +table_hash_search(const char *host, const char *ip, const char *db, + const char *user, const char *tname, bool exact) +{ + return (GRANT_TABLE*) name_hash_search(&column_priv_hash, host, ip, db, + user, tname, exact); +} + inline GRANT_COLUMN * column_hash_search(GRANT_TABLE *t, const char *cname, uint length) @@ -2358,6 +2419,117 @@ table_error: } +static int replace_proc_table(THD *thd, GRANT_NAME *grant_name, + TABLE *table, const LEX_USER &combo, + const char *db, const char *proc_name, + ulong rights, bool revoke_grant) +{ + char grantor[HOSTNAME_LENGTH+USERNAME_LENGTH+2]; + int old_row_exists= 1; + int error=0; + ulong store_proc_rights; + DBUG_ENTER("replace_proc_table"); + + if (!initialized) + { + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), "--skip-grant-tables"); + DBUG_RETURN(-1); + } + + strxmov(grantor, thd->user, "@", thd->host_or_ip, NullS); + + /* + The following should always succeed as new users are created before + this function is called! + */ + if (!find_acl_user(combo.host.str,combo.user.str)) + { + my_error(ER_PASSWORD_NO_MATCH,MYF(0)); + DBUG_RETURN(-1); + } + + restore_record(table,default_values); // Get empty record + table->field[0]->store(combo.host.str,combo.host.length, &my_charset_latin1); + table->field[1]->store(db,(uint) strlen(db), &my_charset_latin1); + table->field[2]->store(combo.user.str,combo.user.length, &my_charset_latin1); + table->field[3]->store(proc_name,(uint) strlen(proc_name), &my_charset_latin1); + store_record(table,record[1]); // store at pos 1 + + if (table->file->index_read_idx(table->record[0],0, + (byte*) table->field[0]->ptr,0, + HA_READ_KEY_EXACT)) + { + /* + The following should never happen as we first check the in memory + grant tables for the user. There is however always a small change that + the user has modified the grant tables directly. + */ + if (revoke_grant) + { // no row, no revoke + my_error(ER_NONEXISTING_PROC_GRANT, MYF(0), + combo.user.str, combo.host.str, proc_name); + DBUG_RETURN(-1); + } + old_row_exists= 0; + restore_record(table,record[1]); // Get saved record + } + + store_proc_rights= get_rights_for_procedure(rights); + if (old_row_exists) + { + ulong j; + store_record(table,record[1]); + j= (ulong) table->field[6]->val_int(); + + if (revoke_grant) + { + /* column rights are already fixed in mysql_table_grant */ + store_proc_rights=j & ~store_proc_rights; + } + else + { + store_proc_rights|= j; + } + } + + table->field[4]->store(grantor,(uint) strlen(grantor), &my_charset_latin1); + table->field[6]->store((longlong) store_proc_rights); + rights=fix_rights_for_procedure(store_proc_rights); + + if (old_row_exists) + { + if (store_proc_rights) + { + if ((error=table->file->update_row(table->record[1],table->record[0]))) + goto table_error; + } + else if ((error= table->file->delete_row(table->record[1]))) + goto table_error; + } + else + { + error=table->file->write_row(table->record[0]); + if (error && error != HA_ERR_FOUND_DUPP_KEY) + goto table_error; + } + + if (rights) + { + grant_name->privs= rights; + } + else + { + hash_delete(&proc_priv_hash,(byte*) grant_name); + } + DBUG_RETURN(0); + + /* This should never happen */ +table_error: + table->file->print_error(error,MYF(0)); + DBUG_RETURN(-1); +} + + /* Store table level and column level grants in the privilege tables @@ -2601,6 +2773,161 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list, } +/* + Store procedure level grants in the privilege tables + + SYNOPSIS + mysql_procedure_grant() + thd Thread handle + table_list List of procedures to give grant + user_list List of users to give grant + rights Table level grant + revoke_grant Set to 1 if this is a REVOKE command + + RETURN + 0 ok + 1 error +*/ + +bool mysql_procedure_grant(THD *thd, TABLE_LIST *table_list, + List &user_list, ulong rights, + bool revoke_grant, bool no_error) +{ + List_iterator str_list (user_list); + LEX_USER *Str; + TABLE_LIST tables[2]; + bool create_new_users=0, result=0; + char *db_name, *real_name; + DBUG_ENTER("mysql_procedure_grant"); + + if (!initialized) + { + if (!no_error) + my_error(ER_OPTION_PREVENTS_STATEMENT, MYF(0), + "--skip-grant-tables"); + DBUG_RETURN(TRUE); + } + if (rights & ~PROC_ACLS) + { + if (!no_error) + my_message(ER_ILLEGAL_GRANT_FOR_TABLE, ER(ER_ILLEGAL_GRANT_FOR_TABLE), + MYF(0)); + DBUG_RETURN(TRUE); + } + + if (!revoke_grant) + { + if (sp_exists_routine(thd, table_list, 0, no_error)<0) + DBUG_RETURN(TRUE); + } + + /* open the mysql.user and mysql.procs_priv tables */ + + bzero((char*) &tables,sizeof(tables)); + tables[0].alias=tables[0].real_name= (char*) "user"; + tables[1].alias=tables[1].real_name= (char*) "procs_priv"; + tables[0].next_local= tables[0].next_global= tables+1; + tables[0].lock_type=tables[1].lock_type=TL_WRITE; + tables[0].db=tables[1].db=(char*) "mysql"; + +#ifdef HAVE_REPLICATION + /* + GRANT and REVOKE are applied the slave in/exclusion rules as they are + some kind of updates to the mysql.% tables. + */ + if (thd->slave_thread && table_rules_on) + { + /* + The tables must be marked "updating" so that tables_ok() takes them into + account in tests. + */ + tables[0].updating= tables[1].updating= 1; + if (!tables_ok(0, tables)) + DBUG_RETURN(FALSE); + } +#endif + + if (simple_open_n_lock_tables(thd,tables)) + { // Should never happen + close_thread_tables(thd); + DBUG_RETURN(TRUE); + } + + if (!revoke_grant) + create_new_users= test_if_create_new_users(thd); + rw_wrlock(&LOCK_grant); + MEM_ROOT *old_root= thd->mem_root; + thd->mem_root= &memex; + + DBUG_PRINT("info",("now time to iterate and add users")); + + while ((Str= str_list++)) + { + int error; + GRANT_NAME *grant_name; + if (Str->host.length > HOSTNAME_LENGTH || + Str->user.length > USERNAME_LENGTH) + { + if (!no_error) + my_message(ER_GRANT_WRONG_HOST_OR_USER, ER(ER_GRANT_WRONG_HOST_OR_USER), + MYF(0)); + result= TRUE; + continue; + } + /* Create user if needed */ + pthread_mutex_lock(&acl_cache->lock); + error=replace_user_table(thd, tables[0].table, *Str, + 0, revoke_grant, create_new_users); + pthread_mutex_unlock(&acl_cache->lock); + if (error) + { + result= TRUE; // Remember error + continue; // Add next user + } + + db_name= table_list->db; + real_name= table_list->real_name; + + grant_name= proc_hash_search(Str->host.str, NullS, db_name, + Str->user.str, real_name, 1); + if (!grant_name) + { + if (revoke_grant) + { + if (!no_error) + my_error(ER_NONEXISTING_PROC_GRANT, MYF(0), + Str->user.str, Str->host.str, real_name); + result= TRUE; + continue; + } + grant_name= new GRANT_NAME(Str->host.str, db_name, + Str->user.str, real_name, + rights); + if (!grant_name) + { + result= TRUE; + continue; + } + my_hash_insert(&proc_priv_hash,(byte*) grant_name); + } + + if (replace_proc_table(thd, grant_name, tables[1].table, *Str, + db_name, real_name, rights, revoke_grant)) + { + result= TRUE; + continue; + } + } + grant_option=TRUE; + thd->mem_root= old_root; + rw_unlock(&LOCK_grant); + if (!result && !no_error) + send_ok(thd); + /* Tables are automatically closed */ + DBUG_RETURN(result); +} + + bool mysql_grant(THD *thd, const char *db, List &list, ulong rights, bool revoke_grant) { @@ -2713,6 +3040,7 @@ void grant_free(void) DBUG_ENTER("grant_free"); grant_option = FALSE; hash_free(&column_priv_hash); + hash_free(&proc_priv_hash); free_root(&memex,MYF(0)); DBUG_VOID_RETURN; } @@ -2723,11 +3051,11 @@ void grant_free(void) my_bool grant_init(THD *org_thd) { THD *thd; - TABLE_LIST tables[2]; + TABLE_LIST tables[3]; MYSQL_LOCK *lock; MEM_ROOT *memex_ptr; my_bool return_val= 1; - TABLE *t_table, *c_table; + TABLE *t_table, *c_table, *p_table; bool check_no_resolve= specialflag & SPECIAL_NO_RESOLVE; DBUG_ENTER("grant_init"); @@ -2735,6 +3063,9 @@ my_bool grant_init(THD *org_thd) (void) hash_init(&column_priv_hash,system_charset_info, 0,0,0, (hash_get_key) get_grant_table, (hash_free_key) free_grant_table,0); + (void) hash_init(&proc_priv_hash,system_charset_info, + 0,0,0, (hash_get_key) get_grant_table, + 0,0); init_sql_alloc(&memex, ACL_ALLOC_BLOCK_SIZE, 0); /* Don't do anything if running with --skip-grant */ @@ -2749,69 +3080,110 @@ my_bool grant_init(THD *org_thd) bzero((char*) &tables, sizeof(tables)); tables[0].alias=tables[0].real_name= (char*) "tables_priv"; tables[1].alias=tables[1].real_name= (char*) "columns_priv"; + tables[2].alias=tables[2].real_name= (char*) "procs_priv"; tables[0].next_local= tables[0].next_global= tables+1; - tables[0].lock_type=tables[1].lock_type=TL_READ; - tables[0].db=tables[1].db=thd->db; + tables[1].next_local= tables[1].next_global= tables+2; + tables[0].lock_type=tables[1].lock_type=tables[2].lock_type=TL_READ; + tables[0].db=tables[1].db=tables[2].db=thd->db; uint counter; if (open_tables(thd, tables, &counter)) goto end; - TABLE *ptr[2]; // Lock tables for quick update + TABLE *ptr[3]; // Lock tables for quick update ptr[0]= tables[0].table; ptr[1]= tables[1].table; - if (!(lock=mysql_lock_tables(thd,ptr,2))) + ptr[2]= tables[2].table; + if (!(lock=mysql_lock_tables(thd,ptr,3))) goto end; t_table = tables[0].table; c_table = tables[1].table; + p_table= tables[2].table; t_table->file->ha_index_init(0); - if (t_table->file->index_first(t_table->record[0])) + p_table->file->ha_index_init(0); + if (!t_table->file->index_first(t_table->record[0])) { - return_val= 0; - goto end_unlock; - } - grant_option= TRUE; - - /* Will be restored by org_thd->store_globals() */ - memex_ptr= &memex; - my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); - do - { - GRANT_TABLE *mem_check; - if (!(mem_check=new GRANT_TABLE(t_table,c_table))) + /* Will be restored by org_thd->store_globals() */ + memex_ptr= &memex; + my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); + do { - /* This could only happen if we are out memory */ - grant_option= FALSE; /* purecov: deadcode */ - goto end_unlock; - } - - if (check_no_resolve) - { - if (hostname_requires_resolving(mem_check->host)) + GRANT_TABLE *mem_check; + if (!(mem_check=new GRANT_TABLE(t_table,c_table))) { - sql_print_warning("'tables_priv' entry '%s %s@%s' " - "ignored in --skip-name-resolve mode.", - mem_check->tname, mem_check->user, - mem_check->host, mem_check->host); - continue; + /* This could only happen if we are out memory */ + grant_option= FALSE; + goto end_unlock; + } + + if (check_no_resolve) + { + if (hostname_requires_resolving(mem_check->host)) + { + sql_print_warning("'tables_priv' entry '%s %s@%s' " + "ignored in --skip-name-resolve mode.", + mem_check->tname, mem_check->user, + mem_check->host, mem_check->host); + continue; + } + } + + if (! mem_check->ok()) + delete mem_check; + else if (my_hash_insert(&column_priv_hash,(byte*) mem_check)) + { + delete mem_check; + grant_option= FALSE; + goto end_unlock; } } - - if (! mem_check->ok()) - delete mem_check; - else if (my_hash_insert(&column_priv_hash,(byte*) mem_check)) - { - delete mem_check; - grant_option= FALSE; - goto end_unlock; - } + while (!t_table->file->index_next(t_table->record[0])); } - while (!t_table->file->index_next(t_table->record[0])); + if (!p_table->file->index_first(p_table->record[0])) + { + /* Will be restored by org_thd->store_globals() */ + memex_ptr= &memex; + my_pthread_setspecific_ptr(THR_MALLOC, &memex_ptr); + do + { + GRANT_NAME *mem_check; + if (!(mem_check=new GRANT_NAME(p_table))) + { + /* This could only happen if we are out memory */ + grant_option= FALSE; + goto end_unlock; + } + if (check_no_resolve) + { + if (hostname_requires_resolving(mem_check->host)) + { + sql_print_warning("'procs_priv' entry '%s %s@%s' " + "ignored in --skip-name-resolve mode.", + mem_check->tname, mem_check->user, + mem_check->host, mem_check->host); + continue; + } + } + + mem_check->privs= fix_rights_for_procedure(mem_check->privs); + if (! mem_check->ok()) + delete mem_check; + else if (my_hash_insert(&proc_priv_hash,(byte*) mem_check)) + { + delete mem_check; + grant_option= FALSE; + goto end_unlock; + } + } + while (!p_table->file->index_next(p_table->record[0])); + } + grant_option= TRUE; return_val=0; // Return ok end_unlock: t_table->file->ha_index_end(); + p_table->file->ha_index_end(); mysql_unlock_tables(thd, lock); thd->version--; // Force close to free memory @@ -2842,7 +3214,7 @@ end: void grant_reload(THD *thd) { - HASH old_column_priv_hash; + HASH old_column_priv_hash, old_proc_priv_hash; bool old_grant_option; MEM_ROOT old_mem; DBUG_ENTER("grant_reload"); @@ -2850,6 +3222,7 @@ void grant_reload(THD *thd) rw_wrlock(&LOCK_grant); grant_version++; old_column_priv_hash= column_priv_hash; + old_proc_priv_hash= proc_priv_hash; old_grant_option= grant_option; old_mem= memex; @@ -2858,12 +3231,14 @@ void grant_reload(THD *thd) DBUG_PRINT("error",("Reverting to old privileges")); grant_free(); /* purecov: deadcode */ column_priv_hash= old_column_priv_hash; /* purecov: deadcode */ + proc_priv_hash= old_proc_priv_hash; grant_option= old_grant_option; /* purecov: deadcode */ memex= old_mem; /* purecov: deadcode */ } else { hash_free(&old_column_priv_hash); + hash_free(&old_proc_priv_hash); free_root(&old_mem,MYF(0)); } rw_unlock(&LOCK_grant); @@ -3099,7 +3474,7 @@ err2: /* Check if a user has the right to access a database - Access is accepted if he has a grant for any table in the database + Access is accepted if he has a grant for any table/routine in the database Return 1 if access is denied */ @@ -3131,6 +3506,72 @@ bool check_grant_db(THD *thd,const char *db) return error; } + +/**************************************************************************** + Check procedure level grants + + SYNPOSIS + bool check_grant_procedure() + thd Thread handler + want_access Bits of privileges user needs to have + procs List of procedures to check. The user should have 'want_access' + no_errors If 0 then we write an error. The error is sent directly to + the client + + RETURN + 0 ok + 1 Error: User did not have the requested privielges +****************************************************************************/ + +bool check_grant_procedure(THD *thd, ulong want_access, + TABLE_LIST *procs, bool no_errors) +{ + TABLE_LIST *table; + char *user= thd->priv_user; + char *host= thd->priv_host; + DBUG_ENTER("check_grant_procedure"); + + want_access&= ~thd->master_access; + if (!want_access) + DBUG_RETURN(0); // ok + + rw_rdlock(&LOCK_grant); + for (table= procs; table; table= table->next_global) + { + GRANT_NAME *grant_proc; + if ((grant_proc= proc_hash_search(host,thd->ip, + table->db, user, table->real_name, 0))) + table->grant.privilege|= grant_proc->privs; + + if (want_access & ~table->grant.privilege) + { + want_access &= ~table->grant.privilege; + goto err; + } + } + rw_unlock(&LOCK_grant); + DBUG_RETURN(0); +err: + rw_unlock(&LOCK_grant); + if (!no_errors) + { + char buff[1024]; + const char *command=""; + if (table) + strxmov(buff, table->db, ".", table->real_name, NullS); + if (want_access & EXECUTE_ACL) + command= "execute"; + else if (want_access & ALTER_PROC_ACL) + command= "alter procedure"; + else if (want_access & GRANT_ACL) + command= "grant"; + my_error(ER_PROCACCESS_DENIED_ERROR, MYF(0), + command, user, host, table ? buff : "unknown"); + } + DBUG_RETURN(1); +} + + /***************************************************************************** Functions to retrieve the grant for a table/column (for SHOW functions) *****************************************************************************/ @@ -3215,12 +3656,12 @@ static const char *command_array[]= "SHUTDOWN", "PROCESS","FILE", "GRANT", "REFERENCES", "INDEX", "ALTER", "SHOW DATABASES", "SUPER", "CREATE TEMPORARY TABLES", "LOCK TABLES", "EXECUTE", "REPLICATION SLAVE", "REPLICATION CLIENT", - "CREATE VIEW", "SHOW VIEW" + "CREATE VIEW", "SHOW VIEW", "CREATE ROUTINE", "ALTER ROUTINE", }; static uint command_lengths[]= { - 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9 + 6, 6, 6, 6, 6, 4, 6, 8, 7, 4, 5, 10, 5, 5, 14, 5, 23, 11, 7, 17, 18, 11, 9, 14, 13 }; @@ -3565,6 +4006,74 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user) } } } + + /* Add procedure access */ + for (index=0 ; index < proc_priv_hash.records ; index++) + { + const char *user; + GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash, + index); + + if (!(user=grant_proc->user)) + user= ""; + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, + grant_proc->orig_host)) + { + ulong proc_access= grant_proc->privs; + if (proc_access != 0) + { + String global(buff, sizeof(buff), system_charset_info); + ulong test_access= proc_access & ~GRANT_ACL; + + global.length(0); + global.append("GRANT ",6); + + if (!test_access) + global.append("USAGE",5); + else + { + /* Add specific procedure access */ + int found= 0; + ulong j; + + for (counter= 0, j= SELECT_ACL; j <= PROC_ACLS; counter++, j<<= 1) + { + if (test_access & j) + { + if (found) + global.append(", ",2); + found= 1; + global.append(command_array[counter],command_lengths[counter]); + } + } + } + global.append(" ON ",4); + append_identifier(thd, &global, grant_proc->db, + strlen(grant_proc->db)); + global.append('.'); + append_identifier(thd, &global, grant_proc->tname, + strlen(grant_proc->tname)); + global.append(" TO '",5); + global.append(lex_user->user.str, lex_user->user.length, + system_charset_info); + global.append("'@'",3); + global.append(lex_user->host.str,lex_user->host.length, + system_charset_info); + global.append('\''); + if (proc_access & GRANT_ACL) + global.append(" WITH GRANT OPTION",18); + protocol->prepare_for_resend(); + protocol->store(global.ptr(),global.length(),global.charset()); + if (protocol->write()) + { + error= -1; + break; + } + } + } + } end: VOID(pthread_mutex_unlock(&acl_cache->lock)); rw_unlock(&LOCK_grant); @@ -3632,6 +4141,7 @@ void get_mqh(const char *user, const char *host, USER_CONN *uc) < 0 Error. */ +#define GRANT_TABLES 5 int open_grant_tables(THD *thd, TABLE_LIST *tables) { DBUG_ENTER("open_grant_tables"); @@ -3642,17 +4152,21 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) DBUG_RETURN(-1); } - bzero((char*) tables, 4*sizeof(*tables)); + bzero((char*) tables, GRANT_TABLES*sizeof(*tables)); tables->alias= tables->real_name= (char*) "user"; (tables+1)->alias= (tables+1)->real_name= (char*) "db"; (tables+2)->alias= (tables+2)->real_name= (char*) "tables_priv"; (tables+3)->alias= (tables+3)->real_name= (char*) "columns_priv"; + (tables+4)->alias= (tables+4)->real_name= (char*) "procs_priv"; tables->next_local= tables->next_global= tables+1; (tables+1)->next_local= (tables+1)->next_global= tables+2; (tables+2)->next_local= (tables+2)->next_global= tables+3; + (tables+3)->next_local= (tables+3)->next_global= tables+4; tables->lock_type= (tables+1)->lock_type= - (tables+2)->lock_type= (tables+3)->lock_type= TL_WRITE; - tables->db= (tables+1)->db= (tables+2)->db= (tables+3)->db=(char*) "mysql"; + (tables+2)->lock_type= (tables+3)->lock_type= + (tables+4)->lock_type= TL_WRITE; + tables->db= (tables+1)->db= (tables+2)->db= + (tables+3)->db= (tables+4)->db= (char*) "mysql"; #ifdef HAVE_REPLICATION /* @@ -3665,10 +4179,12 @@ int open_grant_tables(THD *thd, TABLE_LIST *tables) The tables must be marked "updating" so that tables_ok() takes them into account in tests. */ - tables[0].updating=tables[1].updating=tables[2].updating=tables[3].updating=1; + tables[0].updating=tables[1].updating=tables[2].updating= + tables[3].updating=tables[4].updating=1; if (!tables_ok(0, tables)) DBUG_RETURN(1); - tables[0].updating=tables[1].updating=tables[2].updating=tables[3].updating=0; + tables[0].updating=tables[1].updating=tables[2].updating= + tables[3].updating=tables[4].updating=0;; } #endif @@ -3761,7 +4277,7 @@ static int modify_grant_table(TABLE *table, Field *host_field, SYNOPSIS handle_grant_table() tables The array with the four open tables. - table_no The number of the table to handle (0..3). + table_no The number of the table to handle (0..4). drop If user_from is to be dropped. user_from The the user to be searched/dropped/renamed. user_to The new name for the user if to be renamed, @@ -3779,6 +4295,7 @@ static int modify_grant_table(TABLE *table, Field *host_field, 1 db 2 tables_priv 3 columns_priv + 4 procs_priv RETURN > 0 At least one record matched. @@ -3922,6 +4439,7 @@ static int handle_grant_table(TABLE_LIST *tables, uint table_no, bool drop, 0 acl_users 1 acl_dbs 2 column_priv_hash + 3 procs_priv_hash RETURN > 0 At least one element matched. @@ -3938,7 +4456,7 @@ static int handle_grant_struct(uint struct_no, bool drop, const char *host; ACL_USER *acl_user; ACL_DB *acl_db; - GRANT_TABLE *grant_table; + GRANT_NAME *grant_name; DBUG_ENTER("handle_grant_struct"); LINT_INIT(acl_user); LINT_INIT(acl_db); @@ -3955,8 +4473,15 @@ static int handle_grant_struct(uint struct_no, bool drop, case 1: elements= acl_dbs.elements; break; - default: + case 2: elements= column_priv_hash.records; + break; + case 3: + elements= proc_priv_hash.records; + break; + default: + DBUG_ASSERT((struct_no < 0) || (struct_no > 3)); + return -1; } #ifdef EXTRA_DEBUG @@ -3985,10 +4510,17 @@ static int handle_grant_struct(uint struct_no, bool drop, host= acl_db->host.hostname; break; - default: - grant_table= (GRANT_TABLE*) hash_element(&column_priv_hash, idx); - user= grant_table->user; - host= grant_table->host; + case 2: + grant_name= (GRANT_NAME*) hash_element(&column_priv_hash, idx); + user= grant_name->user; + host= grant_name->host; + break; + + case 3: + grant_name= (GRANT_NAME*) hash_element(&proc_priv_hash, idx); + user= grant_name->user; + host= grant_name->host; + break; } if (! user) user= ""; @@ -4015,8 +4547,13 @@ static int handle_grant_struct(uint struct_no, bool drop, delete_dynamic_element(&acl_dbs, idx); break; - default: - hash_delete(&column_priv_hash, (byte*) grant_table); + case 2: + hash_delete(&column_priv_hash, (byte*) grant_name); + break; + + case 3: + hash_delete(&proc_priv_hash, (byte*) grant_name); + break; } elements--; idx--; @@ -4035,9 +4572,11 @@ static int handle_grant_struct(uint struct_no, bool drop, acl_db->host.hostname= strdup_root(&mem, user_to->host.str); break; - default: - grant_table->user= strdup_root(&mem, user_to->user.str); - grant_table->host= strdup_root(&mem, user_to->host.str); + case 2: + case 3: + grant_name->user= strdup_root(&mem, user_to->user.str); + grant_name->host= strdup_root(&mem, user_to->host.str); + break; } } else @@ -4123,6 +4662,25 @@ static int handle_grant_data(TABLE_LIST *tables, bool drop, } } + /* Handle procedures table. */ + if ((found= handle_grant_table(tables, 4, drop, user_from, user_to)) < 0) + { + /* Handle of table failed, don't touch in-memory array. */ + result= -1; + } + else + { + /* Handle procs array. */ + if (((handle_grant_struct(3, drop, user_from, user_to) && ! result) || + found) && ! result) + { + result= 1; /* At least one record/element found. */ + /* If search is requested, we do not need to search further. */ + if (! drop && ! user_to) + goto end; + } + } + /* Handle tables table. */ if ((found= handle_grant_table(tables, 2, drop, user_from, user_to)) < 0) { @@ -4191,7 +4749,7 @@ bool mysql_create_user(THD *thd, List &list) ulong sql_mode; LEX_USER *user_name; List_iterator user_list(list); - TABLE_LIST tables[4]; + TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_create_user"); /* CREATE USER may be skipped on replication client. */ @@ -4253,7 +4811,7 @@ bool mysql_drop_user(THD *thd, List &list) String wrong_users; LEX_USER *user_name; List_iterator user_list(list); - TABLE_LIST tables[4]; + TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_drop_user"); /* DROP USER may be skipped on replication client. */ @@ -4302,7 +4860,7 @@ bool mysql_rename_user(THD *thd, List &list) LEX_USER *user_from; LEX_USER *user_to; List_iterator user_list(list); - TABLE_LIST tables[4]; + TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_rename_user"); /* RENAME USER may be skipped on replication client. */ @@ -4357,7 +4915,7 @@ bool mysql_revoke_all(THD *thd, List &list) uint counter, revoked; int result; ACL_DB *acl_db; - TABLE_LIST tables[4]; + TABLE_LIST tables[GRANT_TABLES]; DBUG_ENTER("mysql_revoke_all"); if ((result= open_grant_tables(thd, tables))) @@ -4467,6 +5025,35 @@ bool mysql_revoke_all(THD *thd, List &list) counter++; } } while (revoked); + + /* Remove procedure access */ + do { + for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; ) + { + const char *user,*host; + GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash, + counter); + if (!(user=grant_proc->user)) + user= ""; + if (!(host=grant_proc->host)) + host= ""; + + if (!strcmp(lex_user->user.str,user) && + !my_strcasecmp(system_charset_info, lex_user->host.str, host)) + { + if (!replace_proc_table(thd,grant_proc,tables[4].table,*lex_user, + grant_proc->db, + grant_proc->tname, + ~0, 1)) + { + revoked= 1; + continue; + } + result= -1; // Something went wrong + } + counter++; + } + } while (revoked); } VOID(pthread_mutex_unlock(&acl_cache->lock)); @@ -4480,6 +5067,129 @@ bool mysql_revoke_all(THD *thd, List &list) } +/* + Revoke privileges for all users on a stored procedure + + SYNOPSIS + sp_revoke_privileges() + thd The current thread. + db DB of the stored procedure + name Name of the stored procedure + + RETURN + 0 OK. + < 0 Error. Error message not yet sent. +*/ + +bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name) +{ + uint counter, revoked; + int result; + ACL_DB *acl_db; + TABLE_LIST tables[GRANT_TABLES]; + DBUG_ENTER("sp_revoke_privileges"); + + if ((result= open_grant_tables(thd, tables))) + DBUG_RETURN(result != 1); + + rw_wrlock(&LOCK_grant); + VOID(pthread_mutex_lock(&acl_cache->lock)); + + /* Remove procedure access */ + do { + for (counter= 0, revoked= 0 ; counter < proc_priv_hash.records ; ) + { + const char *db,*name; + GRANT_NAME *grant_proc= (GRANT_NAME*) hash_element(&proc_priv_hash, + counter); + if (!my_strcasecmp(system_charset_info, grant_proc->db, sp_db) && + !my_strcasecmp(system_charset_info, grant_proc->tname, sp_name)) + { + LEX_USER lex_user; + lex_user.user.str= grant_proc->user; + lex_user.user.length= strlen(grant_proc->user); + lex_user.host.str= grant_proc->host; + lex_user.host.length= strlen(grant_proc->host); + if (!replace_proc_table(thd,grant_proc,tables[4].table,lex_user, + grant_proc->db, grant_proc->tname, ~0, 1)) + { + revoked= 1; + continue; + } + result= -1; // Something went wrong + } + counter++; + } + } while (revoked); + + VOID(pthread_mutex_unlock(&acl_cache->lock)); + rw_unlock(&LOCK_grant); + close_thread_tables(thd); + + if (result) + my_message(ER_REVOKE_GRANTS, ER(ER_REVOKE_GRANTS), MYF(0)); + + DBUG_RETURN(result); +} + + +/* + Grant EXECUTE,ALTER privilege for a stored procedure + + SYNOPSIS + sp_grant_privileges() + thd The current thread. + db DB of the stored procedure + name Name of the stored procedure + + RETURN + 0 OK. + < 0 Error. Error message not yet sent. +*/ + +bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name) +{ + LEX_USER *combo; + TABLE_LIST tables[1]; + List user_list; + bool result; + DBUG_ENTER("sp_grant_privileges"); + + if (!(combo=(LEX_USER*) thd->alloc(sizeof(st_lex_user)))) + DBUG_RETURN(TRUE); + + combo->user.str= thd->user; + + if (!find_acl_user(combo->host.str=(char*)thd->host_or_ip, combo->user.str) && + !find_acl_user(combo->host.str=(char*)thd->host, combo->user.str) && + !find_acl_user(combo->host.str=(char*)thd->ip, combo->user.str) && + !find_acl_user(combo->host.str=(char*)"%", combo->user.str)) + DBUG_RETURN(TRUE); + + bzero((char*)tables, sizeof(TABLE_LIST)); + user_list.empty(); + + tables->db= (char*)sp_db; + tables->real_name= tables->alias= (char*)sp_name; + + combo->host.length= strlen(combo->host.str); + combo->user.length= strlen(combo->user.str); + combo->host.str= thd->strmake(combo->host.str,combo->host.length); + combo->user.str= thd->strmake(combo->user.str,combo->user.length); + combo->password.str= (char*)""; + combo->password.length= 0; + + if (user_list.push_back(combo)) + DBUG_RETURN(TRUE); + + thd->lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; + + result= mysql_procedure_grant(thd, tables, user_list, + DEFAULT_CREATE_PROC_ACLS, 0, 1); + DBUG_RETURN(result); +} + + /***************************************************************************** Instantiate used templates *****************************************************************************/ diff --git a/sql/sql_acl.h b/sql/sql_acl.h index 8f3ee072f43..4ebc3ad7707 100644 --- a/sql/sql_acl.h +++ b/sql/sql_acl.h @@ -37,6 +37,8 @@ #define REPL_CLIENT_ACL (1L << 20) #define CREATE_VIEW_ACL (1L << 21) #define SHOW_VIEW_ACL (1L << 22) +#define CREATE_PROC_ACL (1L << 23) +#define ALTER_PROC_ACL (1L << 24) /* don't forget to update static struct show_privileges_st sys_privileges[] @@ -47,7 +49,8 @@ #define DB_ACLS \ (UPDATE_ACL | SELECT_ACL | INSERT_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL | CREATE_TMP_ACL | \ - LOCK_TABLES_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL) + LOCK_TABLES_ACL | EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | \ + CREATE_PROC_ACL | ALTER_PROC_ACL) #define TABLE_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ @@ -57,43 +60,61 @@ #define COL_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | REFERENCES_ACL) +#define PROC_ACLS \ +(ALTER_PROC_ACL | EXECUTE_ACL | GRANT_ACL) + #define GLOBAL_ACLS \ (SELECT_ACL | INSERT_ACL | UPDATE_ACL | DELETE_ACL | CREATE_ACL | DROP_ACL | \ RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL | GRANT_ACL | \ REFERENCES_ACL | INDEX_ACL | ALTER_ACL | SHOW_DB_ACL | SUPER_ACL | \ CREATE_TMP_ACL | LOCK_TABLES_ACL | REPL_SLAVE_ACL | REPL_CLIENT_ACL | \ - EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL) + EXECUTE_ACL | CREATE_VIEW_ACL | SHOW_VIEW_ACL | CREATE_PROC_ACL | \ + ALTER_PROC_ACL ) #define EXTRA_ACL (1L << 29) #define NO_ACCESS (1L << 30) +#define DEFAULT_CREATE_PROC_ACLS \ +(ALTER_PROC_ACL | EXECUTE_ACL) + /* Defines to change the above bits to how things are stored in tables This is needed as the 'host' and 'db' table is missing a few privileges */ /* Continius bit-segments that needs to be shifted */ -#define DB_REL1 (RELOAD_ACL | SHUTDOWN_ACL | PROCESS_ACL | FILE_ACL) -#define DB_REL2 (GRANT_ACL | REFERENCES_ACL) -#define DB_REL3 (INDEX_ACL | ALTER_ACL) +#define DB_REL1 ((1L << 6) | (1L << 7) | (1L << 8) | (1L << 9)) +#define DB_REL2 ((1L << 10) | (1L << 11)) +#define DB_REL3 ((1L << 12) | (1L << 13) | (1L << 14) | (1L << 15)) +#define DB_REL4 ((1L << 16)) /* Privileges that needs to be reallocated (in continous chunks) */ #define DB_CHUNK1 (GRANT_ACL | REFERENCES_ACL | INDEX_ACL | ALTER_ACL) #define DB_CHUNK2 (CREATE_TMP_ACL | LOCK_TABLES_ACL) -#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL) +#define DB_CHUNK3 (CREATE_VIEW_ACL | SHOW_VIEW_ACL | \ + CREATE_PROC_ACL | ALTER_PROC_ACL ) +#define DB_CHUNK4 (EXECUTE_ACL) #define fix_rights_for_db(A) (((A) & 63) | \ (((A) & DB_REL1) << 4) | \ (((A) & DB_REL2) << 6) | \ - (((A) & DB_REL3) << 9)) + (((A) & DB_REL3) << 9) | \ + (((A) & DB_REL4) << 2)) #define get_rights_for_db(A) (((A) & 63) | \ (((A) & DB_CHUNK1) >> 4) | \ (((A) & DB_CHUNK2) >> 6) | \ - (((A) & DB_CHUNK3) >> 9)) + (((A) & DB_CHUNK3) >> 9) | \ + (((A) & DB_CHUNK4) >> 2)) #define fix_rights_for_table(A) (((A) & 63) | (((A) & ~63) << 4)) #define get_rights_for_table(A) (((A) & 63) | (((A) & ~63) >> 4)) #define fix_rights_for_column(A) (((A) & 7) | (((A) & ~7) << 8)) #define get_rights_for_column(A) (((A) & 7) | ((A) >> 8)) +#define fix_rights_for_procedure(A) ((((A) << 18) & EXECUTE_ACL) | \ + (((A) << 23) & ALTER_PROC_ACL) | \ + (((A) << 8) & GRANT_ACL)) +#define get_rights_for_procedure(A) ((((A) & EXECUTE_ACL) >> 18) | \ + (((A) & ALTER_PROC_ACL) >> 23) | \ + (((A) & GRANT_ACL) >> 8)) /* Classes */ @@ -163,6 +184,9 @@ bool mysql_grant(THD *thd, const char *db, List &user_list, bool mysql_table_grant(THD *thd, TABLE_LIST *table, List &user_list, List &column_list, ulong rights, bool revoke); +bool mysql_procedure_grant(THD *thd, TABLE_LIST *table, + List &user_list, ulong rights, + bool revoke, bool no_error); my_bool grant_init(THD *thd); void grant_free(void); void grant_reload(THD *thd); @@ -174,6 +198,8 @@ bool check_grant_column (THD *thd, GRANT_INFO *grant, bool check_grant_all_columns(THD *thd, ulong want_access, GRANT_INFO *grant, char* db_name, char *table_name, Field_iterator *fields); +bool check_grant_procedure(THD *thd, ulong want_access, + TABLE_LIST *procs, bool no_error); bool check_grant_db(THD *thd,const char *db); ulong get_table_grant(THD *thd, TABLE_LIST *table); ulong get_column_grant(THD *thd, GRANT_INFO *grant, @@ -188,6 +214,8 @@ bool mysql_rename_user(THD *thd, List &list); bool mysql_revoke_all(THD *thd, List &list); void fill_effective_table_privileges(THD *thd, GRANT_INFO *grant, const char *db, const char *table); +bool sp_revoke_privileges(THD *thd, const char *sp_db, const char *sp_name); +bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name); #ifdef NO_EMBEDDED_ACCESS_CHECKS #define check_grant(A,B,C,D,E,F) 0 diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 721febab548..19af2d9f243 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -743,6 +743,7 @@ typedef struct st_lex sp_head *sphead; sp_name *spname; bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */ + bool all_privileges; sp_pcontext *spcont; HASH spfuns; /* Called functions */ st_sp_chistics sp_chistics; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 0dec6e820be..80639e06f5f 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -69,7 +69,6 @@ static void remove_escape(char *name); static void refresh_status(void); static bool append_file_to_dir(THD *thd, const char **filename_ptr, const char *table_name); -static bool check_sp_definer_access(THD *thd, sp_head *sp); const char *any_db="*any*"; // Special symbol for check_access @@ -3495,15 +3494,30 @@ create_error: } if (first_table) { - if (grant_option && check_grant(thd, - (lex->grant | lex->grant_tot_col | - GRANT_ACL), - all_tables, 0, UINT_MAX, 0)) - goto error; - if (!(res = mysql_table_grant(thd, all_tables, lex->users_list, - lex->columns, lex->grant, - lex->sql_command == SQLCOM_REVOKE)) && - mysql_bin_log.is_open()) + if (!lex->columns.elements && + sp_exists_routine(thd, all_tables, 1, 1)) + { + uint grants= lex->all_privileges + ? (PROC_ACLS & ~GRANT_ACL) | (lex->grant & GRANT_ACL) + : lex->grant; + if (grant_option && + check_grant_procedure(thd, grants | GRANT_ACL, all_tables, 0)) + goto error; + res= mysql_procedure_grant(thd, all_tables, lex->users_list, + grants, lex->sql_command == SQLCOM_REVOKE,0); + } + else + { + if (grant_option && check_grant(thd, + (lex->grant | lex->grant_tot_col | + GRANT_ACL), + all_tables, 0, UINT_MAX, 0)) + goto error; + res= mysql_table_grant(thd, all_tables, lex->users_list, + lex->columns, lex->grant, + lex->sql_command == SQLCOM_REVOKE); + } + if (!res && mysql_bin_log.is_open()) { thd->clear_error(); Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE); @@ -3705,18 +3719,24 @@ create_error: case SQLCOM_CREATE_SPFUNCTION: { uint namelen; - char *name; + char *name, *db; int result; DBUG_ASSERT(lex->sphead); - if (! lex->sphead->m_db.str) + if (check_access(thd, CREATE_PROC_ACL, lex->sphead->m_db.str, 0, 0, 0)) { - my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR), MYF(0)); delete lex->sphead; lex->sphead= 0; goto error; } + + if (!lex->sphead->m_db.str || !lex->sphead->m_db.str[0]) + { + lex->sphead->m_db.length= strlen(thd->db); + lex->sphead->m_db.str= strmake_root(thd->mem_root, thd->db, + lex->sphead->m_db.length); + } name= lex->sphead->name(&namelen); #ifdef HAVE_DLOPEN @@ -3742,13 +3762,26 @@ create_error: goto error; } + name= thd->strdup(name); + db= thd->strmake(lex->sphead->m_db.str, lex->sphead->m_db.length); res= (result= lex->sphead->create(thd)); switch (result) { case SP_OK: - send_ok(thd); lex->unit.cleanup(); delete lex->sphead; lex->sphead= 0; + /* only add privileges if really neccessary */ + if (sp_automatic_privileges && + check_procedure_access(thd, DEFAULT_CREATE_PROC_ACLS, + db, name, 1)) + { + close_thread_tables(thd); + if (sp_grant_privileges(thd, db, name)) + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_PROC_AUTO_GRANT_FAIL, + ER(ER_PROC_AUTO_GRANT_FAIL)); + } + send_ok(thd); break; case SP_WRITE_ROW_FAILED: my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name); @@ -3815,7 +3848,26 @@ create_error: } #ifndef NO_EMBEDDED_ACCESS_CHECKS + if (check_procedure_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, 0)) + { +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif + goto error; + } sp_change_security_context(thd, sp, &save_ctx); + if (save_ctx.changed && + check_procedure_access(thd, EXECUTE_ACL, + sp->m_db.str, sp->m_name.str, 0)) + { +#ifndef EMBEDDED_LIBRARY + thd->net.no_send_ok= nsok; +#endif + sp_restore_security_context(thd, sp, &save_ctx); + goto error; + } + #endif select_limit= thd->variables.select_limit; thd->variables.select_limit= HA_POS_ERROR; @@ -3861,8 +3913,9 @@ create_error: result= SP_KEY_NOT_FOUND; else { - if (check_sp_definer_access(thd, sp)) - goto error; + if (check_procedure_access(thd, ALTER_PROC_ACL, sp->m_db.str, + sp->m_name.str, 0)) + goto error; memcpy(&lex->sp_chistics, &chistics, sizeof(lex->sp_chistics)); if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) result= sp_update_procedure(thd, lex->spname, &lex->sp_chistics); @@ -3890,6 +3943,7 @@ create_error: { sp_head *sp; int result; + char *db, *name; if (lex->sql_command == SQLCOM_DROP_PROCEDURE) sp= sp_find_procedure(thd, lex->spname); @@ -3898,8 +3952,17 @@ create_error: mysql_reset_errors(thd); if (sp) { - if (check_sp_definer_access(thd, sp)) + db= thd->strdup(sp->m_db.str); + name= thd->strdup(sp->m_name.str); + if (check_procedure_access(thd, ALTER_PROC_ACL, db, name, 0)) goto error; + if (sp_automatic_privileges && + sp_revoke_privileges(thd, db, name)) + { + push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, + ER_PROC_AUTO_REVOKE_FAIL, + ER(ER_PROC_AUTO_REVOKE_FAIL)); + } if (lex->sql_command == SQLCOM_DROP_PROCEDURE) result= sp_drop_procedure(thd, lex->spname); else @@ -4208,7 +4271,7 @@ check_access(THD *thd, ulong want_access, const char *db, ulong *save_priv, /* grant_option is set if there exists a single table or column grant */ if (db_access == want_access || (grant_option && !dont_check_global_grants && - !(want_access & ~(db_access | TABLE_ACLS)))) + !(want_access & ~(db_access | TABLE_ACLS | PROC_ACLS)))) DBUG_RETURN(FALSE); /* Ok */ DBUG_PRINT("error",("Access denied")); @@ -4304,6 +4367,28 @@ check_table_access(THD *thd, ulong want_access,TABLE_LIST *tables, } +bool +check_procedure_access(THD *thd, ulong want_access,char *db, char *name, + bool no_errors) +{ + TABLE_LIST tables[1]; + + bzero((char *)tables, sizeof(TABLE_LIST)); + tables->db= db; + tables->real_name= tables->alias= name; + + if ((thd->master_access & want_access) == want_access && !thd->db) + tables->grant.privilege= want_access; + else if (check_access(thd,want_access,db,&tables->grant.privilege, + 0, no_errors)) + return TRUE; + + if (grant_option) + return check_grant_procedure(thd, want_access, tables, no_errors); + + return FALSE; +} + /* Check if the given table has any of the asked privileges @@ -4377,40 +4462,6 @@ static bool check_db_used(THD *thd,TABLE_LIST *tables) } -/* - Check if the given SP is owned by thd->priv_user/host, or priv_user is root. - QQ This is not quite complete, but it will do as a basic security check - for now. The question is exactly which rights should 'root' have? - Should root have access regardless of host for instance? - - SYNOPSIS - check_sp_definer_access() - thd Thread handler - sp The SP pointer - - RETURN - 0 ok - 1 error Error message has been sent -*/ - -static bool -check_sp_definer_access(THD *thd, sp_head *sp) -{ - LEX_STRING *usr, *hst; - - if (strcmp("root", thd->priv_user) == 0) - return FALSE; /* QQ Any root is ok now */ - usr= &sp->m_definer_user; - hst= &sp->m_definer_host; - if (strncmp(thd->priv_user, usr->str, usr->length) == 0 && - strncmp(thd->priv_host, hst->str, hst->length) == 0) - return FALSE; /* Both user and host must match */ - - my_error(ER_SP_ACCESS_DENIED_ERROR, MYF(0), sp->m_qname.str); - return TRUE; /* Not definer or root */ -} - - /**************************************************************************** Check stack size; Send error if there isn't enough stack to continue ****************************************************************************/ diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 826bd2038f9..f8e7056b705 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -138,13 +138,16 @@ struct show_privileges_st { static struct show_privileges_st sys_privileges[]= { {"Alter", "Tables", "To alter the table"}, + {"Alter routine", "Functions,Procedures", "To alter or drop stored functions/procedures"}, {"Create", "Databases,Tables,Indexes", "To create new databases and tables"}, + {"Create routine","Functions,Procedures","To use CREATE FUNCTION/PROCEDURE"}, {"Create temporary tables","Databases","To use CREATE TEMPORARY TABLE"}, {"Create view", "Tables", "To create new views"}, {"Delete", "Tables", "To delete existing rows"}, {"Drop", "Databases,Tables", "To drop databases, tables, and views"}, + {"Execute", "Functions,Procedures", "To execute stored routines"}, {"File", "File access on server", "To read and write files on the server"}, - {"Grant option", "Databases,Tables", "To give to other users those privileges you possess"}, + {"Grant option", "Databases,Tables,Functions,Procedures", "To give to other users those privileges you possess"}, {"Index", "Tables", "To create or drop indexes"}, {"Insert", "Tables", "To insert data into tables"}, {"Lock tables","Databases","To use LOCK TABLES (together with SELECT privilege)"}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 88cd3daf924..75db6e332f9 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -390,6 +390,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token RESTORE_SYM %token RESTRICT %token REVOKE +%token ROUTINE_SYM %token ROWS_SYM %token ROW_FORMAT_SYM %token ROW_SYM @@ -790,7 +791,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); opt_outer table_list table_name opt_option opt_place opt_attribute opt_attribute_list attribute column_list column_list_id opt_column_list grant_privileges opt_table grant_list grant_option - grant_privilege grant_privilege_list user_list rename_list + object_privilege object_privilege_list user_list rename_list clear_privileges flush_options flush_option equal optional_braces opt_key_definition key_usage_list2 opt_mi_check_type opt_to mi_check_types normal_join @@ -1301,6 +1302,7 @@ clear_privileges: lex->users_list.empty(); lex->columns.empty(); lex->grant= lex->grant_tot_col= 0; + lex->all_privileges= 0; lex->select_lex.db= 0; lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; @@ -7031,6 +7033,7 @@ keyword: | RETURNS_SYM {} | ROLLBACK_SYM {} | ROLLUP_SYM {} + | ROUTINE_SYM {} | ROWS_SYM {} | ROW_FORMAT_SYM {} | ROW_SYM {} @@ -7543,14 +7546,16 @@ revoke_command: grant: GRANT clear_privileges grant_privileges ON opt_table TO_SYM grant_list require_clause grant_options - { - Lex->sql_command = SQLCOM_GRANT; - } + { Lex->sql_command= SQLCOM_GRANT; } ; grant_privileges: - grant_privilege_list {} - | ALL opt_privileges { Lex->grant = GLOBAL_ACLS;} + object_privilege_list { } + | ALL opt_privileges + { + Lex->all_privileges= 1; + Lex->grant= GLOBAL_ACLS; + } ; opt_privileges: @@ -7558,11 +7563,11 @@ opt_privileges: | PRIVILEGES ; -grant_privilege_list: - grant_privilege - | grant_privilege_list ',' grant_privilege; +object_privilege_list: + object_privilege + | object_privilege_list ',' object_privilege; -grant_privilege: +object_privilege: SELECT_SYM { Lex->which_columns = SELECT_ACL;} opt_column_list {} | INSERT { Lex->which_columns = INSERT_ACL;} opt_column_list {} | UPDATE_SYM { Lex->which_columns = UPDATE_ACL; } opt_column_list {} @@ -7587,6 +7592,8 @@ grant_privilege: | REPLICATION CLIENT_SYM { Lex->grant |= REPL_CLIENT_ACL; } | CREATE VIEW_SYM { Lex->grant |= CREATE_VIEW_ACL; } | SHOW VIEW_SYM { Lex->grant |= SHOW_VIEW_ACL; } + | CREATE ROUTINE_SYM { Lex->grant |= CREATE_PROC_ACL; } + | ALTER ROUTINE_SYM { Lex->grant |= ALTER_PROC_ACL; } ; From 0cc6e44707e7e76afcc96979e613368ee9075abb Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Dec 2004 11:23:36 +0000 Subject: [PATCH 03/13] Fix compile error, caused by wl#925 variable rename sql/sql_acl.cc: Fix compile error --- sql/sql_acl.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 4dcd15741cb..2de3fc4a711 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -4460,7 +4460,7 @@ static int handle_grant_struct(uint struct_no, bool drop, DBUG_ENTER("handle_grant_struct"); LINT_INIT(acl_user); LINT_INIT(acl_db); - LINT_INIT(grant_table); + LINT_INIT(grant_name); DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'", struct_no, user_from->user.str, user_from->host.str)); From dd6981f4d01072cbc5fa898bd6c42b54ffbb0fcf Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Dec 2004 11:42:57 +0000 Subject: [PATCH 04/13] Fix for embedded --- sql/sql_parse.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 80639e06f5f..1e232f064ba 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3770,6 +3770,7 @@ create_error: lex->unit.cleanup(); delete lex->sphead; lex->sphead= 0; +#ifndef NO_EMBEDDED_ACCESS_CHECKS /* only add privileges if really neccessary */ if (sp_automatic_privileges && check_procedure_access(thd, DEFAULT_CREATE_PROC_ACLS, @@ -3781,6 +3782,7 @@ create_error: ER_PROC_AUTO_GRANT_FAIL, ER(ER_PROC_AUTO_GRANT_FAIL)); } +#endif send_ok(thd); break; case SP_WRITE_ROW_FAILED: @@ -3956,6 +3958,7 @@ create_error: name= thd->strdup(sp->m_name.str); if (check_procedure_access(thd, ALTER_PROC_ACL, db, name, 0)) goto error; +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (sp_automatic_privileges && sp_revoke_privileges(thd, db, name)) { @@ -3963,6 +3966,7 @@ create_error: ER_PROC_AUTO_REVOKE_FAIL, ER(ER_PROC_AUTO_REVOKE_FAIL)); } +#endif if (lex->sql_command == SQLCOM_DROP_PROCEDURE) result= sp_drop_procedure(thd, lex->spname); else @@ -4383,8 +4387,10 @@ check_procedure_access(THD *thd, ulong want_access,char *db, char *name, 0, no_errors)) return TRUE; +#ifndef NO_EMBEDDED_ACCESS_CHECKS if (grant_option) return check_grant_procedure(thd, want_access, tables, no_errors); +#endif return FALSE; } From 1a1974544d0be772edc20806ec881b2c5df61a6d Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Dec 2004 20:47:03 +0300 Subject: [PATCH 05/13] Set default port to the one assigned by IANA --- server-tools/instance-manager/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server-tools/instance-manager/Makefile.am b/server-tools/instance-manager/Makefile.am index a15ff9321cb..c2bf501eca7 100644 --- a/server-tools/instance-manager/Makefile.am +++ b/server-tools/instance-manager/Makefile.am @@ -34,7 +34,7 @@ liboptions_a_CPPFLAGS= $(CPPFLAGS) \ -DDEFAULT_USER="root" \ -DDEFAULT_PASSWORD="" \ -DDEFAULT_MONITORING_INTERVAL="5" \ - -DDEFAULT_PORT="33006" \ + -DDEFAULT_PORT="2273" \ -DPROTOCOL_VERSION=@PROTOCOL_VERSION@ liboptions_a_SOURCES= options.h options.cc priv.h priv.cc From bec3feaa0bdf4d89bc3c3f1255ebf70a624f4850 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Dec 2004 20:11:38 +0100 Subject: [PATCH 06/13] WL#1895 - Print message to error log in case of detected MyISAM corruption Changed my_error() to print error messages, which come from arbitrary registered ranges of error messages. Messages can be unregistered (and should be at end of the program). Added registration of handler error messages. Added a new mi_print_error() macro and a new mi_report_error() function, which supply error messages with a table name. Added calls to mi_print_error() or mi_report_error() at all places in MyISAM, where table corruption is detected. extra/comp_err.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added prints for ER_ERROR_FIRST and ER_ERROR_LAST. Removed print for ER_ERROR_MESSAGES. include/errmsg.h: WL#1895 - Print message to error log in case of detected MyISAM corruption Added declaration for a new function. Added first and last error number defines. include/my_base.h: WL#1895 - Print message to error log in case of detected MyISAM corruption Added first and last error number defines. include/my_sys.h: WL#1895 - Print message to error log in case of detected MyISAM corruption Removed obsolete defines. Removed a global variable, which held pointers to the error message arrays. Added declarations for new functions. include/mysys_err.h: WL#1895 - Print message to error log in case of detected MyISAM corruption Removed an obsolete define. Changed two defines to use the new defines. Added first and last error number defines. libmysql/errmsg.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Replaced global array initialization by proper registration and unregistration of client error messages. libmysql/libmysql.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added a call for unregistration of client error messages. myisam/mi_delete.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. Added a debugging call to pretend MyISAM corruption in case a special debug string is set. Added a debugging call to test undefined error numbers in case a special debug string is set. myisam/mi_extra.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. myisam/mi_info.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added the error logging function. myisam/mi_key.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. myisam/mi_keycache.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. myisam/mi_locking.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. myisam/mi_open.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. Added a debugging call to pretend MyISAM corruption in case a special debug string is set. myisam/mi_page.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. myisam/mi_range.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. myisam/mi_rkey.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. myisam/mi_search.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. myisam/mi_update.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. Added a debugging call to pretend MyISAM corruption in case a special debug string is set. myisam/mi_write.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Added calls to the new error logging function at all places, where corruption is detected. Added a debugging call to pretend MyISAM corruption in case a special debug string is set. myisam/myisamdef.h: WL#1895 - Print message to error log in case of detected MyISAM corruption Added the declaration of the new error logging function and a new macro. mysql-test/r/merge.result: WL#1895 - Print message to error log in case of detected MyISAM corruption Changed test results. These come from the changed error reporting in openfrm(). mysql-test/r/repair.result: WL#1895 - Print message to error log in case of detected MyISAM corruption Changed test results. These come from the changed error reporting in openfrm(). mysql-test/t/merge.test: WL#1895 - Print message to error log in case of detected MyISAM corruption Changederror numbers. These come from the changed error reporting in openfrm(). mysys/errors.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Dropped the assignment of the global errors to the dropped global pointer array. mysys/my_error.c: WL#1895 - Print message to error log in case of detected MyISAM corruption Changed my_error() from using a static array of pointers to error message arrays to using a linked list of structures with pointers to array message arrays. Added functions for registering and unregistering error message arrays to the chain. sql/derror.cc: WL#1895 - Print message to error log in case of detected MyISAM corruption Changed reading of mysqld error messages to using the new registering and unregistering functions. sql/handler.cc: WL#1895 - Print message to error log in case of detected MyISAM corruption Added initialization and deinitialization of handler error messages. Included more handler error messages in the mapping to mysqld error messages. sql/mysqld.cc: WL#1895 - Print message to error log in case of detected MyISAM corruption Changed deinitialization of error messages to proper unregistration. sql/table.cc: WL#1895 - Print message to error log in case of detected MyISAM corruption Changed error reporting in openfrm() so that error messages from handler::ha_open() are reported by handler::print_error(). This changed messages from "Can't open 't1.MYI' (errno: 130)" to "Incorrect file format 't1'" for example. sql/unireg.h: WL#1895 - Print message to error log in case of detected MyISAM corruption Changed two defines to use the new defines. --- extra/comp_err.c | 9 +- include/errmsg.h | 7 ++ include/my_base.h | 6 ++ include/my_sys.h | 5 +- include/mysys_err.h | 10 ++- libmysql/errmsg.c | 28 ++++++- libmysql/libmysql.c | 1 + myisam/mi_delete.c | 32 +++++++- myisam/mi_extra.c | 5 ++ myisam/mi_info.c | 33 ++++++++ myisam/mi_key.c | 1 + myisam/mi_keycache.c | 1 + myisam/mi_locking.c | 6 ++ myisam/mi_open.c | 13 +++ myisam/mi_page.c | 2 + myisam/mi_range.c | 4 + myisam/mi_rkey.c | 1 + myisam/mi_search.c | 17 +++- myisam/mi_update.c | 9 ++ myisam/mi_write.c | 20 +++++ myisam/myisamdef.h | 3 + mysql-test/r/merge.result | 4 +- mysql-test/r/repair.result | 2 +- mysql-test/t/merge.test | 4 +- mysys/errors.c | 6 +- mysys/my_error.c | 163 ++++++++++++++++++++++++++++++++++--- sql/derror.cc | 64 +++++++++++---- sql/handler.cc | 101 +++++++++++++++++++++++ sql/mysqld.cc | 4 +- sql/table.cc | 9 +- sql/unireg.h | 4 +- 31 files changed, 518 insertions(+), 56 deletions(-) diff --git a/extra/comp_err.c b/extra/comp_err.c index 9ddd1d7d971..8bc8a989a6a 100644 --- a/extra/comp_err.c +++ b/extra/comp_err.c @@ -191,10 +191,11 @@ int main(int argc, char *argv[]) static int create_header_files(struct errors *error_head) { - uint er_count= 0; + uint er_last; FILE *er_definef, *sql_statef; struct errors *tmp_error; DBUG_ENTER("create_header_files"); + LINT_INIT(er_last); if (!(er_definef= my_fopen(HEADERFILE, O_WRONLY, MYF(MY_WME)))) { @@ -209,6 +210,8 @@ static int create_header_files(struct errors *error_head) fprintf(er_definef, "/* Autogenerated file, please don't edit */\n\n"); fprintf(sql_statef, "/* Autogenerated file, please don't edit */\n\n"); + fprintf(er_definef, "#define ER_ERROR_FIRST %d\n", error_head->d_code); + for (tmp_error= error_head; tmp_error; tmp_error= tmp_error->next_error) { /* @@ -217,16 +220,16 @@ static int create_header_files(struct errors *error_head) */ fprintf(er_definef, "#define %s %d\n", tmp_error->er_name, tmp_error->d_code); + er_last= tmp_error->d_code; /* generating sql_state.h file */ if (tmp_error->sql_code1[0] || tmp_error->sql_code2[0]) fprintf(sql_statef, "%-40s,\"%s\", \"%s\",\n", tmp_error->er_name, tmp_error->sql_code1, tmp_error->sql_code2); - er_count++; } /* finishing off with mysqld_error.h */ - fprintf(er_definef, "#define ER_ERROR_MESSAGES %d\n", er_count); + fprintf(er_definef, "#define ER_ERROR_LAST %d\n", er_last); my_fclose(er_definef, MYF(0)); my_fclose(sql_statef, MYF(0)); DBUG_RETURN(0); diff --git a/include/errmsg.h b/include/errmsg.h index 6115b24a3d8..55bbdf6d767 100644 --- a/include/errmsg.h +++ b/include/errmsg.h @@ -21,6 +21,7 @@ extern "C" { #endif void init_client_errs(void); +void finish_client_errs(void); extern const char *client_errors[]; /* Error messages */ #ifdef __cplusplus } @@ -35,6 +36,9 @@ extern const char *client_errors[]; /* Error messages */ #endif #define CLIENT_ERRMAP 2 /* Errormap used by my_error() */ +/* Do not add error numbers before CR_ERROR_FIRST. */ +/* If necessary to add lower numbers, change CR_ERROR_FIRST accordingly. */ +#define CR_ERROR_FIRST 2000 /*Copy first error nr.*/ #define CR_UNKNOWN_ERROR 2000 #define CR_SOCKET_CREATE_ERROR 2001 #define CR_CONNECTION_ERROR 2002 @@ -90,3 +94,6 @@ extern const char *client_errors[]; /* Error messages */ #define CR_SECURE_AUTH 2049 #define CR_FETCH_CANCELED 2050 #define CR_NO_DATA 2051 +#define CR_ERROR_LAST /*Copy last error nr:*/ 2051 +/* Add error numbers before CR_ERROR_LAST and change it accordingly. */ + diff --git a/include/my_base.h b/include/my_base.h index 4d043cf6b5b..7c4c6d521ab 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -273,6 +273,9 @@ enum ha_base_keytype { /* Errorcodes given by functions */ /* opt_sum_query() assumes these codes are > 1 */ +/* Do not add error numbers before HA_ERR_FIRST. */ +/* If necessary to add lower numbers, change HA_ERR_FIRST accordingly. */ +#define HA_ERR_FIRST 120 /*Copy first error nr.*/ #define HA_ERR_KEY_NOT_FOUND 120 /* Didn't find key on read or update */ #define HA_ERR_FOUND_DUPP_KEY 121 /* Dupplicate key on write */ #define HA_ERR_RECORD_CHANGED 123 /* Uppdate with is recoverable */ @@ -308,6 +311,9 @@ enum ha_base_keytype { #define HA_ERR_NO_SUCH_TABLE 155 /* The table does not exist in engine */ #define HA_ERR_TABLE_EXIST 156 /* The table existed in storage engine */ #define HA_ERR_NO_CONNECTION 157 /* Could not connect to storage engine */ +#define HA_ERR_LAST 157 /*Copy last error nr.*/ +/* Add error numbers before HA_ERR_LAST and change it accordingly. */ +#define HA_ERR_ERRORS (HA_ERR_LAST - HA_ERR_FIRST + 1) /* Other constants */ diff --git a/include/my_sys.h b/include/my_sys.h index e630c9bdbba..cbcd6f0f833 100644 --- a/include/my_sys.h +++ b/include/my_sys.h @@ -43,8 +43,6 @@ extern int NEAR my_errno; /* Last error in mysys */ #define MYSYS_PROGRAM_DONT_USE_CURSES() { error_handler_hook = my_message_no_curses; mysys_uses_curses=0;} #define MY_INIT(name); { my_progname= name; my_init(); } -#define MAXMAPS (4) /* Number of error message maps */ -#define ERRMOD (1000) /* Max number of errors in a map */ #define ERRMSGSIZE (SC_MAXWIDTH) /* Max length of a error message */ #define NRERRBUFFS (2) /* Buffers for parameters */ #define MY_FILE_ERROR ((uint) ~0) @@ -213,7 +211,6 @@ void __CDECL hfree(void *ptr); #else extern int errno; /* declare errno */ #endif -extern const char ** NEAR my_errmsg[]; extern char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; extern char *home_dir; /* Home directory for user */ extern char *my_progname; /* program-name (printed in errors) */ @@ -610,6 +607,8 @@ extern int my_error _VARARGS((int nr,myf MyFlags, ...)); extern int my_printf_error _VARARGS((uint my_err, const char *format, myf MyFlags, ...) __attribute__ ((format (printf, 2, 4)))); +extern int my_error_register(const char **errmsgs, int first, int last); +extern const char **my_error_unregister(int first, int last); extern int my_message(uint my_err, const char *str,myf MyFlags); extern int my_message_no_curses(uint my_err, const char *str,myf MyFlags); extern int my_message_curses(uint my_err, const char *str,myf MyFlags); diff --git a/include/mysys_err.h b/include/mysys_err.h index 230be5f4720..1fd7c2eddc6 100644 --- a/include/mysys_err.h +++ b/include/mysys_err.h @@ -20,13 +20,15 @@ extern "C" { #endif -#define GLOB 0 /* Error maps */ -#define GLOBERRS 28 /* Max number of error messages in map's */ -#define EE(X) globerrs[ X ] /* Defines to add error to right map */ +#define GLOBERRS (EE_ERROR_LAST - EE_ERROR_FIRST + 1) /* Nr of global errors */ +#define EE(X) (globerrs[(X) - EE_ERROR_FIRST]) extern const char * NEAR globerrs[]; /* my_error_messages is here */ /* Error message numbers in global map */ +/* Do not add error numbers before EE_ERROR_FIRST. */ +/* If necessary to add lower numbers, change EE_ERROR_FIRST accordingly. */ +#define EE_ERROR_FIRST 0 /*Copy first error nr.*/ #define EE_FILENOTFOUND 0 #define EE_CANTCREATEFILE 1 #define EE_READ 2 @@ -54,6 +56,8 @@ extern const char * NEAR globerrs[]; /* my_error_messages is here */ #define EE_CANT_SYMLINK 25 #define EE_REALPATH 26 #define EE_SYNC 27 +#define EE_ERROR_LAST 27 /*Copy last error nr.*/ +/* Add error numbers before EE_ERROR_LAST and change it accordingly. */ /* exit codes for all MySQL programs */ diff --git a/libmysql/errmsg.c b/libmysql/errmsg.c index 710bf4ccd8d..73726e772e5 100644 --- a/libmysql/errmsg.c +++ b/libmysql/errmsg.c @@ -199,7 +199,33 @@ const char *client_errors[]= #endif +/* + Register client error messages for use with my_error(). + + SYNOPSIS + init_client_errs() + + RETURN + void +*/ + void init_client_errs(void) { - my_errmsg[CLIENT_ERRMAP] = &client_errors[0]; + (void) my_error_register(client_errors, CR_ERROR_FIRST, CR_ERROR_LAST); +} + + +/* + Unregister client error messages. + + SYNOPSIS + finish_client_errs() + + RETURN + void +*/ + +void finish_client_errs(void) +{ + (void) my_error_unregister(CR_ERROR_FIRST, CR_ERROR_LAST); } diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 4476a42f8ac..8c20b566957 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -185,6 +185,7 @@ void STDCALL mysql_server_end() } else mysql_thread_end(); + finish_client_errs(); mysql_client_init= org_my_init_done= 0; } diff --git a/myisam/mi_delete.c b/myisam/mi_delete.c index b964cb35dd8..d79d9040ee7 100644 --- a/myisam/mi_delete.c +++ b/myisam/mi_delete.c @@ -45,6 +45,12 @@ int mi_delete(MI_INFO *info,const byte *record) /* Test if record is in datafile */ + DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage", + mi_print_error(info, HA_ERR_CRASHED); + DBUG_RETURN(my_errno= HA_ERR_CRASHED);); + DBUG_EXECUTE_IF("my_error_test_undefined_error", + mi_print_error(info, INT_MAX); + DBUG_RETURN(my_errno= INT_MAX);); if (!(info->update & HA_STATE_AKTIV)) { DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND); /* No database read */ @@ -109,13 +115,19 @@ err: mi_sizestore(lastpos,info->lastpos); myisam_log_command(MI_LOG_DELETE,info,(byte*) lastpos, sizeof(lastpos),0); if (save_errno != HA_ERR_RECORD_CHANGED) + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* mark table crashed */ + } VOID(_mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE)); info->update|=HA_STATE_WRITTEN; /* Buffer changed */ allow_break(); /* Allow SIGHUP & SIGINT */ my_errno=save_errno; if (save_errno == HA_ERR_KEY_NOT_FOUND) + { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; + } DBUG_RETURN(my_errno); } /* mi_delete */ @@ -142,6 +154,7 @@ static int _mi_ck_real_delete(register MI_INFO *info, MI_KEYDEF *keyinfo, if ((old_root=*root) == HA_OFFSET_ERROR) { + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(my_errno=HA_ERR_CRASHED); } if (!(root_buff= (uchar*) my_alloca((uint) keyinfo->block_length+ @@ -253,7 +266,9 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, my_off_t root; uchar *kpos=keypos; - tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&kpos,lastkey); + if (!(tmp_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&kpos,lastkey)) + && (my_errno == HA_ERR_CRASHED)) + mi_print_error(info, HA_ERR_CRASHED); root=_mi_dpos(info,nod_flag,kpos); if (subkeys == -1) { @@ -302,6 +317,7 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, if (!nod_flag) { DBUG_PRINT("error",("Didn't find key")); + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; /* This should newer happend */ goto err; } @@ -317,6 +333,8 @@ static int d_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, &next_block); if (tmp == 0) { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_PRINT("exit",("Return: %d",0)); DBUG_RETURN(0); } @@ -473,6 +491,8 @@ static int del(register MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *key, (info->quick_mode ? MI_MIN_KEYBLOCK_LENGTH : (uint) keyinfo->underflow_block_length)); err: + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); } /* del */ @@ -562,7 +582,11 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo, s_length=remove_key(keyinfo,key_reflength,keypos,anc_key, anc_buff+anc_length,(my_off_t *) 0); if (!s_length) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); goto err; + } anc_length-=s_length; mi_putint(anc_buff,anc_length,key_reflength); @@ -671,7 +695,11 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo, s_length=remove_key(keyinfo,key_reflength,keypos,anc_key, anc_buff+anc_length,(my_off_t *) 0); if (!s_length) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); goto err; + } anc_length-=s_length; mi_putint(anc_buff,anc_length,key_reflength); @@ -732,6 +760,8 @@ static int underflow(register MI_INFO *info, register MI_KEYDEF *keyinfo, goto err; DBUG_RETURN(anc_length <= (uint) keyinfo->block_length/2); err: + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); } /* underflow */ diff --git a/myisam/mi_extra.c b/myisam/mi_extra.c index 4b011ca424f..999c4ba8f3d 100644 --- a/myisam/mi_extra.c +++ b/myisam/mi_extra.c @@ -186,7 +186,10 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) if (info->opt_flag & WRITE_CACHE_USED) { if ((error=flush_io_cache(&info->rec_cache))) + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Fatal error found */ + } } break; case HA_EXTRA_NO_READCHECK: @@ -285,6 +288,7 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) { error=my_errno; share->changed=1; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Fatal error found */ } if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) @@ -339,6 +343,7 @@ int mi_extra(MI_INFO *info, enum ha_extra_function function, void *extra_arg) if (error) { share->changed=1; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Fatal error found */ } } diff --git a/myisam/mi_info.c b/myisam/mi_info.c index cf63ef63618..bdece9c2ee3 100644 --- a/myisam/mi_info.c +++ b/myisam/mi_info.c @@ -105,3 +105,36 @@ int mi_status(MI_INFO *info, register MI_ISAMINFO *x, uint flag) } DBUG_RETURN(0); } + + +/* + Write a message to the error log. + + SYNOPSIS + mi_report_error() + file_name Name of table file (e.g. index_file_name). + errcode Error number. + + DESCRIPTION + This function supplies my_error() with a table name. Most error + messages need one. Since string arguments in error messages are limited + to 64 characters by convention, we ensure that in case of truncation, + that the end of the index file path is in the message. This contains + the most valuable information (the table name and the database name). + + RETURN + void +*/ + +void mi_report_error(int errcode, const char *file_name) +{ + size_t lgt; + DBUG_ENTER("mi_report_error"); + DBUG_PRINT("enter",("errcode %d, table '%s'", errcode, file_name)); + + if ((lgt= strlen(file_name)) > 64) + file_name+= lgt - 64; + my_error(errcode, MYF(ME_NOREFRESH), file_name); + DBUG_VOID_RETURN; +} + diff --git a/myisam/mi_key.c b/myisam/mi_key.c index caca63452b0..eaaee617f32 100644 --- a/myisam/mi_key.c +++ b/myisam/mi_key.c @@ -477,6 +477,7 @@ int _mi_read_key_record(MI_INFO *info, my_off_t filepos, byte *buf) { /* Read only key */ if (_mi_put_key_in_record(info,(uint) info->lastinx,buf)) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; return -1; } diff --git a/myisam/mi_keycache.c b/myisam/mi_keycache.c index 99a2fd6db15..33d0ac4f6bc 100644 --- a/myisam/mi_keycache.c +++ b/myisam/mi_keycache.c @@ -79,6 +79,7 @@ int mi_assign_to_key_cache(MI_INFO *info, if (flush_key_blocks(share->key_cache, share->kfile, FLUSH_RELEASE)) { error= my_errno; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Mark that table must be checked */ } diff --git a/myisam/mi_locking.c b/myisam/mi_locking.c index 66950f62321..91e9f09b9fb 100644 --- a/myisam/mi_locking.c +++ b/myisam/mi_locking.c @@ -66,6 +66,7 @@ int mi_lock_database(MI_INFO *info, int lock_type) share->kfile,FLUSH_KEEP)) { error=my_errno; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); /* Mark that table must be checked */ } if (info->opt_flag & (READ_CACHE_USED | WRITE_CACHE_USED)) @@ -73,6 +74,7 @@ int mi_lock_database(MI_INFO *info, int lock_type) if (end_io_cache(&info->rec_cache)) { error=my_errno; + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); } } @@ -98,7 +100,10 @@ int mi_lock_database(MI_INFO *info, int lock_type) else share->not_flushed=1; if (error) + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); + } } if (info->lock_type != F_EXTRA_LCK) { @@ -285,6 +290,7 @@ void mi_update_status(void* param) { if (end_io_cache(&info->rec_cache)) { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); } info->opt_flag&= ~WRITE_CACHE_USED; diff --git a/myisam/mi_open.c b/myisam/mi_open.c index 58db2e47c1f..040bc1503aa 100644 --- a/myisam/mi_open.c +++ b/myisam/mi_open.c @@ -106,6 +106,12 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) share_buff.state.key_del=key_del; share_buff.key_cache= multi_key_cache_search(name_buff, strlen(name_buff)); + DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_open", + if (strstr(name, "/t1")) + { + my_errno= HA_ERR_CRASHED; + goto err; + }); if ((kfile=my_open(name_buff,(open_mode=O_RDWR) | O_SHARE,MYF(0))) < 0) { if ((errno != EROFS && errno != EACCES) || @@ -601,6 +607,10 @@ MI_INFO *mi_open(const char *name, int mode, uint open_flags) err: save_errno=my_errno ? my_errno : HA_ERR_END_OF_FILE; + if ((save_errno == HA_ERR_CRASHED) || + (save_errno == HA_ERR_CRASHED_ON_USAGE) || + (save_errno == HA_ERR_CRASHED_ON_REPAIR)) + mi_report_error(save_errno, name); switch (errpos) { case 6: my_free((gptr) m_info,MYF(0)); @@ -1223,7 +1233,10 @@ int mi_enable_indexes(MI_INFO *info) if (share->state.state.data_file_length || (share->state.state.key_file_length != share->base.keystart)) + { + mi_print_error(info, HA_ERR_CRASHED); error= HA_ERR_CRASHED; + } else share->state.key_map= ((ulonglong) 1L << share->base.keys) - 1; return error; diff --git a/myisam/mi_page.c b/myisam/mi_page.c index 16713c87e10..dc2bc75f1a0 100644 --- a/myisam/mi_page.c +++ b/myisam/mi_page.c @@ -40,6 +40,7 @@ uchar *_mi_fetch_keypage(register MI_INFO *info, MI_KEYDEF *keyinfo, { DBUG_PRINT("error",("Got errno: %d from key_cache_read",my_errno)); info->last_keypage=HA_OFFSET_ERROR; + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(0); } @@ -51,6 +52,7 @@ uchar *_mi_fetch_keypage(register MI_INFO *info, MI_KEYDEF *keyinfo, (ulong) page, page_size)); DBUG_DUMP("page", (char*) tmp, keyinfo->block_length); info->last_keypage = HA_OFFSET_ERROR; + mi_print_error(info, HA_ERR_CRASHED); my_errno = HA_ERR_CRASHED; tmp = 0; } diff --git a/myisam/mi_range.c b/myisam/mi_range.c index 789607c9889..0d8f8763b92 100644 --- a/myisam/mi_range.c +++ b/myisam/mi_range.c @@ -233,7 +233,11 @@ static uint _mi_keynr(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, u while (page < end) { if (!(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff)) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); return 0; /* Error */ + } max_key++; if (page == keypos) keynr=max_key; diff --git a/myisam/mi_rkey.c b/myisam/mi_rkey.c index 12db00337ee..d564c672f19 100644 --- a/myisam/mi_rkey.c +++ b/myisam/mi_rkey.c @@ -78,6 +78,7 @@ int mi_rkey(MI_INFO *info, byte *buf, int inx, const byte *key, uint key_len, case HA_KEY_ALG_RTREE: if (rtree_find_first(info,inx,key_buff,use_key_length,nextflag) < 0) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; goto err; } diff --git a/myisam/mi_search.c b/myisam/mi_search.c index 2259dd17fcd..f252719d29c 100644 --- a/myisam/mi_search.c +++ b/myisam/mi_search.c @@ -161,6 +161,8 @@ int _mi_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, DBUG_RETURN(0); err: DBUG_PRINT("exit",("Error: %d",my_errno)); + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); info->lastpos= HA_OFFSET_ERROR; info->page_changed=1; DBUG_RETURN (-1); @@ -234,6 +236,7 @@ int _mi_seq_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,t_buff); if (length == 0 || page > end) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_PRINT("error",("Found wrong key: length: %u page: %p end: %p", length, page, end)); @@ -380,6 +383,7 @@ int _mi_prefix_search(MI_INFO *info, register MI_KEYDEF *keyinfo, uchar *page, if (page > end) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_PRINT("error",("Found wrong key: length: %u page: %p end: %p", length, page, end)); @@ -969,6 +973,7 @@ uchar *_mi_get_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, *return_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,key); if (*return_key_length == 0) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(0); } @@ -1006,6 +1011,7 @@ static my_bool _mi_get_prev_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, *return_key_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page,key); if (*return_key_length == 0) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(1); } @@ -1046,6 +1052,7 @@ uchar *_mi_get_last_key(MI_INFO *info, MI_KEYDEF *keyinfo, uchar *page, if (*return_key_length == 0) { DBUG_PRINT("error",("Couldn't find last key: page: %p", page)); + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(0); } @@ -1178,7 +1185,11 @@ int _mi_search_next(register MI_INFO *info, register MI_KEYDEF *keyinfo, memcpy(lastkey,key,key_length); if (!(info->lastkey_length=(*keyinfo->get_key)(keyinfo,nod_flag, &info->int_keypos,lastkey))) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); + } } else /* Previous key */ { @@ -1236,8 +1247,10 @@ int _mi_search_first(register MI_INFO *info, register MI_KEYDEF *keyinfo, page=info->buff+2+nod_flag; } while ((pos=_mi_kpos(nod_flag,page)) != HA_OFFSET_ERROR); - info->lastkey_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page, - info->lastkey); + if (!(info->lastkey_length=(*keyinfo->get_key)(keyinfo,nod_flag,&page, + info->lastkey)) && + (my_errno == HA_ERR_CRASHED)) + mi_print_error(info, HA_ERR_CRASHED); info->int_keypos=page; info->int_maxpos=info->buff+mi_getint(info->buff)-1; info->int_nod_flag=nod_flag; info->int_keytree_version=keyinfo->version; diff --git a/myisam/mi_update.c b/myisam/mi_update.c index f62be133ed9..2936e29a01c 100644 --- a/myisam/mi_update.c +++ b/myisam/mi_update.c @@ -34,6 +34,9 @@ int mi_update(register MI_INFO *info, const byte *oldrec, byte *newrec) LINT_INIT(changed); LINT_INIT(old_checksum); + DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage", + mi_print_error(info, HA_ERR_CRASHED); + DBUG_RETURN(my_errno= HA_ERR_CRASHED);); if (!(info->update & HA_STATE_AKTIV)) { DBUG_RETURN(my_errno=HA_ERR_KEY_NOT_FOUND); @@ -205,7 +208,10 @@ err: } while (i-- != 0); } else + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); + } info->update= (HA_STATE_CHANGED | HA_STATE_AKTIV | HA_STATE_ROW_CHANGED | key_changed); @@ -214,6 +220,9 @@ err: VOID(_mi_writeinfo(info,WRITEINFO_UPDATE_KEYFILE)); allow_break(); /* Allow SIGHUP & SIGINT */ if (save_errno == HA_ERR_KEY_NOT_FOUND) + { + mi_print_error(info, HA_ERR_CRASHED); save_errno=HA_ERR_CRASHED; + } DBUG_RETURN(my_errno=save_errno); } /* mi_update */ diff --git a/myisam/mi_write.c b/myisam/mi_write.c index c2f24ae1ae3..8ff653bdd2d 100644 --- a/myisam/mi_write.c +++ b/myisam/mi_write.c @@ -52,6 +52,9 @@ int mi_write(MI_INFO *info, byte *record) DBUG_ENTER("mi_write"); DBUG_PRINT("enter",("isam: %d data: %d",info->s->kfile,info->dfile)); + DBUG_EXECUTE_IF("myisam_pretend_crashed_table_on_usage", + mi_print_error(info, HA_ERR_CRASHED); + DBUG_RETURN(my_errno= HA_ERR_CRASHED);); if (share->options & HA_OPTION_READ_ONLY_DATA) { DBUG_RETURN(my_errno=EACCES); @@ -202,7 +205,10 @@ err: } } else + { + mi_print_error(info, HA_ERR_CRASHED); mi_mark_crashed(info); + } info->update= (HA_STATE_CHANGED | HA_STATE_WRITTEN | HA_STATE_ROW_CHANGED); my_errno=save_errno; err2: @@ -346,7 +352,11 @@ static int w_search(register MI_INFO *info, register MI_KEYDEF *keyinfo, if (tmp_key_length) dupp_key_pos=_mi_dpos(info,0,keybuff+tmp_key_length); else + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); dupp_key_pos= HA_OFFSET_ERROR; + } if (keyinfo->flag & HA_FULLTEXT) { uint off; @@ -455,6 +465,7 @@ int _mi_insert(register MI_INFO *info, register MI_KEYDEF *keyinfo, { if (t_length >= keyinfo->maxlength*2+MAX_POINTER_LENGTH) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(-1); } @@ -464,6 +475,7 @@ int _mi_insert(register MI_INFO *info, register MI_KEYDEF *keyinfo, { if (-t_length >= keyinfo->maxlength*2+MAX_POINTER_LENGTH) { + mi_print_error(info, HA_ERR_CRASHED); my_errno=HA_ERR_CRASHED; DBUG_RETURN(-1); } @@ -558,7 +570,11 @@ int _mi_split_page(register MI_INFO *info, register MI_KEYDEF *keyinfo, key_pos=_mi_find_half_pos(nod_flag,keyinfo,buff,key_buff, &key_length, &after_key); if (!key_pos) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); + } length=(uint) (key_pos-buff); a_length=mi_getint(buff); mi_putint(buff,length,nod_flag); @@ -578,7 +594,11 @@ int _mi_split_page(register MI_INFO *info, register MI_KEYDEF *keyinfo, /* Store new page */ if (!(*keyinfo->get_key)(keyinfo,nod_flag,&key_pos,key_buff)) + { + if (my_errno == HA_ERR_CRASHED) + mi_print_error(info, HA_ERR_CRASHED); DBUG_RETURN(-1); + } t_length=(*keyinfo->pack_key)(keyinfo,nod_flag,(uchar *) 0, (uchar*) 0, (uchar*) 0, key_buff, &s_temp); diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h index 12ce112dbe0..c0f56a7b720 100644 --- a/myisam/myisamdef.h +++ b/myisam/myisamdef.h @@ -356,6 +356,8 @@ typedef struct st_mi_sort_param #define mi_mark_crashed_on_repair(x) { (x)->s->state.changed|=STATE_CRASHED|STATE_CRASHED_ON_REPAIR ; (x)->update|= HA_STATE_CHANGED; } #define mi_is_crashed(x) ((x)->s->state.changed & STATE_CRASHED) #define mi_is_crashed_on_repair(x) ((x)->s->state.changed & STATE_CRASHED_ON_REPAIR) +#define mi_print_error(INFO, ERRNO) \ + mi_report_error((ERRNO), (INFO)->s->index_file_name) /* Functions to store length of space packed keys, VARCHAR or BLOB keys */ @@ -667,6 +669,7 @@ extern void _myisam_log_command(enum myisam_log_commands command, extern void _myisam_log_record(enum myisam_log_commands command,MI_INFO *info, const byte *record,my_off_t filepos, int result); +extern void mi_report_error(int errcode, const char *file_name); extern my_bool _mi_memmap_file(MI_INFO *info); extern void _mi_unmap_file(MI_INFO *info); extern uint save_pack_length(byte *block_buff,ulong length); diff --git a/mysql-test/r/merge.result b/mysql-test/r/merge.result index a9d90813660..618816cd623 100644 --- a/mysql-test/r/merge.result +++ b/mysql-test/r/merge.result @@ -178,9 +178,9 @@ t3 CREATE TABLE `t3` ( ) ENGINE=MRG_MyISAM DEFAULT CHARSET=latin1 UNION=(`t1`,`t2`) create table t4 (a int not null, b char(10), key(a)) engine=MERGE UNION=(t1,t2); select * from t4; -ERROR HY000: Can't open file: 't4.MRG' (errno: 143) +ERROR HY000: All tables in the MERGE table are not identically defined alter table t4 add column c int; -ERROR HY000: Can't open file: 't4.MRG' (errno: 143) +ERROR HY000: All tables in the MERGE table are not identically defined create database mysqltest; create table mysqltest.t6 (a int not null primary key auto_increment, message char(20)); create table t5 (a int not null, b char(20), key(a)) engine=MERGE UNION=(test.t1,mysqltest.t6); diff --git a/mysql-test/r/repair.result b/mysql-test/r/repair.result index dbca5c39a6c..0347d3a52f5 100644 --- a/mysql-test/r/repair.result +++ b/mysql-test/r/repair.result @@ -31,7 +31,7 @@ create table t1 engine=myisam SELECT 1,"table 1"; flush tables; repair table t1; Table Op Msg_type Msg_text -test.t1 repair error Can't open file: 't1.MYI' (errno: 130) +test.t1 repair error Incorrect file format 't1' repair table t1 use_frm; Table Op Msg_type Msg_text test.t1 repair warning Number of rows changed from 0 to 1 diff --git a/mysql-test/t/merge.test b/mysql-test/t/merge.test index 9d367260049..41d44376525 100644 --- a/mysql-test/t/merge.test +++ b/mysql-test/t/merge.test @@ -47,9 +47,9 @@ show create table t3; # The following should give errors create table t4 (a int not null, b char(10), key(a)) engine=MERGE UNION=(t1,t2); ---error 1016 +--error 1168 select * from t4; ---error 1016 +--error 1168 alter table t4 add column c int; # diff --git a/mysys/errors.c b/mysys/errors.c index 5401c2b3cc6..5f548cad480 100644 --- a/mysys/errors.c +++ b/mysys/errors.c @@ -53,15 +53,13 @@ const char * NEAR globerrs[GLOBERRS]= void init_glob_errs(void) { - my_errmsg[GLOB] = & globerrs[0]; -} /* init_glob_errs */ + /* This is now done statically. */ +} #else void init_glob_errs() { - my_errmsg[GLOB] = & globerrs[0]; - EE(EE_FILENOTFOUND) = "File '%s' not found (Errcode: %d)"; EE(EE_CANTCREATEFILE) = "Can't create/write to file '%s' (Errcode: %d)"; EE(EE_READ) = "Error reading file '%s' (Errcode: %d)"; diff --git a/mysys/my_error.c b/mysys/my_error.c index 175f8cf516b..0c18bbf6e8b 100644 --- a/mysys/my_error.c +++ b/mysys/my_error.c @@ -31,9 +31,30 @@ my_printf_error(ER_CODE, format, MYF(N), ...) */ -const char ** NEAR my_errmsg[MAXMAPS]={0,0,0,0}; char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; +/* + Message texts are registered into a linked list of 'my_err_head' structs. + Each struct contains (1.) an array of pointers to C character strings with + '\0' termination, (2.) the error number for the first message in the array + (array index 0) and (3.) the error number for the last message in the array + (array index (last - first)). + The array may contain gaps with NULL pointers and pointers to empty strings. + Both kinds of gaps will be translated to "Unknown error %d.", if my_error() + is called with a respective error number. + The list of header structs is sorted in increasing order of error numbers. + Negative error numbers are allowed. Overlap of error numbers is not allowed. + Not registered error numbers will be translated to "Unknown error %d.". +*/ +static struct my_err_head +{ + struct my_err_head *meh_next; /* chain link */ + const char **meh_errmsgs; /* error messages array */ + int meh_first; /* error number matching array slot 0 */ + int meh_last; /* error number matching last slot */ +} my_errmsgs_globerrs = {NULL, globerrs, EE_ERROR_FIRST, EE_ERROR_LAST}; +static struct my_err_head *my_errmsgs_list= &my_errmsgs_globerrs; + /* Error message to user @@ -42,30 +63,42 @@ char NEAR errbuff[NRERRBUFFS][ERRMSGSIZE]; nr Errno MyFlags Flags ... variable list - NOTE - The following subset of printf format is supported: - "%[0-9.-]*l?[sdu]", where all length flags are parsed but ignored. - Additionally "%.*s" is supported and "%.*[ud]" is correctly parsed but - the length value is ignored. + RETURN + What (*error_handler_hook)() returns: + 0 OK */ int my_error(int nr, myf MyFlags, ...) { const char *format; + struct my_err_head *meh_p; va_list args; char ebuff[ERRMSGSIZE + 20]; DBUG_ENTER("my_error"); DBUG_PRINT("my", ("nr: %d MyFlags: %d errno: %d", nr, MyFlags, errno)); - if (nr / ERRMOD == GLOB && my_errmsg[GLOB] == 0) - init_glob_errs(); - format= my_errmsg[nr / ERRMOD][nr % ERRMOD]; + /* Search for the error messages array, which could contain the message. */ + for (meh_p= my_errmsgs_list; meh_p; meh_p= meh_p->meh_next) + if (nr <= meh_p->meh_last) + break; - va_start(args,MyFlags); - (void) my_vsnprintf (ebuff, sizeof(ebuff), format, args); - va_end(args); +#ifdef SHARED_LIBRARY + if ((meh_p == &my_errmsgs_globerrs) && ! globerrs[0]) + init_glob_errs(); +#endif + + /* get the error message string. Default, if NULL or empty string (""). */ + if (! (format= (meh_p && (nr >= meh_p->meh_first)) ? + meh_p->meh_errmsgs[nr - meh_p->meh_first] : NULL) || ! *format) + (void) my_snprintf (ebuff, sizeof(ebuff), "Unknown error %d", nr); + else + { + va_start(args,MyFlags); + (void) my_vsnprintf (ebuff, sizeof(ebuff), format, args); + va_end(args); + } DBUG_RETURN((*error_handler_hook)(nr, ebuff, MyFlags)); } @@ -108,3 +141,109 @@ int my_message(uint error, const char *str, register myf MyFlags) { return (*error_handler_hook)(error, str, MyFlags); } + + +/* + Register error messages for use with my_error(). + + SYNOPSIS + my_error_register() + errmsgs array of pointers to error messages + first error number of first message in the array + last error number of last message in the array + + DESCRIPTION + The pointer array is expected to contain addresses to NUL-terminated + C character strings. The array contains (last - first + 1) pointers. + NULL pointers and empty strings ("") are allowed. These will be mapped to + "Unknown error" when my_error() is called with a matching error number. + This function registers the error numbers 'first' to 'last'. + No overlapping with previously registered error numbers is allowed. + + RETURN + 0 OK + != 0 Error +*/ + +int my_error_register(const char **errmsgs, int first, int last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + + /* Allocate a new header structure. */ + if (! (meh_p= (struct my_err_head*) my_malloc(sizeof(struct my_err_head), + MYF(MY_WME)))) + return 1; + meh_p->meh_errmsgs= errmsgs; + meh_p->meh_first= first; + meh_p->meh_last= last; + + /* Search for the right position in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if ((*search_meh_pp)->meh_last > first) + break; + } + + /* Error numbers must be unique. No overlapping is allowed. */ + if (*search_meh_pp && ((*search_meh_pp)->meh_first <= last)) + return 1; + + /* Insert header into the chain. */ + meh_p->meh_next= *search_meh_pp; + *search_meh_pp= meh_p; + return 0; +} + + +/* + Unregister formerly registered error messages. + + SYNOPSIS + my_error_unregister() + first error number of first message + last error number of last message + + DESCRIPTION + This function unregisters the error numbers 'first' to 'last'. + These must have been previously registered by my_error_register(). + 'first' and 'last' must exactly match the registration. + If a matching registration is present, the header is removed from the + list and the pointer to the error messages pointers array is returned. + Otherwise, NULL is returned. + + RETURN + non-NULL OK, returns address of error messages pointers array. + NULL Error, no such number range registered. +*/ + +const char **my_error_unregister(int first, int last) +{ + struct my_err_head *meh_p; + struct my_err_head **search_meh_pp; + const char **errmsgs; + + /* Search for the registration in the list. */ + for (search_meh_pp= &my_errmsgs_list; + *search_meh_pp; + search_meh_pp= &(*search_meh_pp)->meh_next) + { + if (((*search_meh_pp)->meh_first == first) && + ((*search_meh_pp)->meh_last == last)) + break; + } + if (! *search_meh_pp) + return NULL; + + /* Remove header from the chain. */ + meh_p= *search_meh_pp; + *search_meh_pp= meh_p->meh_next; + + /* Save the return value and free the header. */ + errmsgs= meh_p->meh_errmsgs; + my_free((gptr) meh_p, MYF(0)); + + return errmsgs; +} diff --git a/sql/derror.cc b/sql/derror.cc index 09f43d20044..4690e76d0e3 100644 --- a/sql/derror.cc +++ b/sql/derror.cc @@ -24,15 +24,43 @@ static bool read_texts(const char *file_name,const char ***point, uint error_messages); static void init_myfunc_errs(void); - /* Read messages from errorfile */ +/* + Read messages from errorfile. + + SYNOPSIS + init_errmessage() + + DESCRIPTION + This function can be called multiple times to reload the messages. + + RETURN + FALSE OK + TRUE Error +*/ bool init_errmessage(void) { + const char **errmsgs; DBUG_ENTER("init_errmessage"); - if (read_texts(ERRMSG_FILE,&my_errmsg[ERRMAPP],ER_ERROR_MESSAGES)) + /* + Get a pointer to the old error messages pointer array. + read_texts() tries to free it. + */ + errmsgs= my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST); + + /* Read messages from file. */ + if (read_texts(ERRMSG_FILE, &errmsgs, ER_ERROR_LAST - ER_ERROR_FIRST + 1)) DBUG_RETURN(TRUE); - errmesg=my_errmsg[ERRMAPP]; /* Init global variabel */ + + /* Register messages for use with my_error(). */ + if (my_error_register(errmsgs, ER_ERROR_FIRST, ER_ERROR_LAST)) + { + x_free((gptr) errmsgs); + DBUG_RETURN(TRUE); + } + + errmesg= errmsgs; /* Init global variabel */ init_myfunc_errs(); /* Init myfunc messages */ DBUG_RETURN(FALSE); } @@ -148,20 +176,20 @@ static void init_myfunc_errs() init_glob_errs(); /* Initiate english errors */ if (!(specialflag & SPECIAL_ENGLISH)) { - globerrs[EE_FILENOTFOUND % ERRMOD] = ER(ER_FILE_NOT_FOUND); - globerrs[EE_CANTCREATEFILE % ERRMOD]= ER(ER_CANT_CREATE_FILE); - globerrs[EE_READ % ERRMOD] = ER(ER_ERROR_ON_READ); - globerrs[EE_WRITE % ERRMOD] = ER(ER_ERROR_ON_WRITE); - globerrs[EE_BADCLOSE % ERRMOD] = ER(ER_ERROR_ON_CLOSE); - globerrs[EE_OUTOFMEMORY % ERRMOD] = ER(ER_OUTOFMEMORY); - globerrs[EE_DELETE % ERRMOD] = ER(ER_CANT_DELETE_FILE); - globerrs[EE_LINK % ERRMOD] = ER(ER_ERROR_ON_RENAME); - globerrs[EE_EOFERR % ERRMOD] = ER(ER_UNEXPECTED_EOF); - globerrs[EE_CANTLOCK % ERRMOD] = ER(ER_CANT_LOCK); - globerrs[EE_DIR % ERRMOD] = ER(ER_CANT_READ_DIR); - globerrs[EE_STAT % ERRMOD] = ER(ER_CANT_GET_STAT); - globerrs[EE_GETWD % ERRMOD] = ER(ER_CANT_GET_WD); - globerrs[EE_SETWD % ERRMOD] = ER(ER_CANT_SET_WD); - globerrs[EE_DISK_FULL % ERRMOD] = ER(ER_DISK_FULL); + EE(EE_FILENOTFOUND) = ER(ER_FILE_NOT_FOUND); + EE(EE_CANTCREATEFILE) = ER(ER_CANT_CREATE_FILE); + EE(EE_READ) = ER(ER_ERROR_ON_READ); + EE(EE_WRITE) = ER(ER_ERROR_ON_WRITE); + EE(EE_BADCLOSE) = ER(ER_ERROR_ON_CLOSE); + EE(EE_OUTOFMEMORY) = ER(ER_OUTOFMEMORY); + EE(EE_DELETE) = ER(ER_CANT_DELETE_FILE); + EE(EE_LINK) = ER(ER_ERROR_ON_RENAME); + EE(EE_EOFERR) = ER(ER_UNEXPECTED_EOF); + EE(EE_CANTLOCK) = ER(ER_CANT_LOCK); + EE(EE_DIR) = ER(ER_CANT_READ_DIR); + EE(EE_STAT) = ER(ER_CANT_GET_STAT); + EE(EE_GETWD) = ER(ER_CANT_GET_WD); + EE(EE_SETWD) = ER(ER_CANT_SET_WD); + EE(EE_DISK_FULL) = ER(ER_DISK_FULL); } } diff --git a/sql/handler.cc b/sql/handler.cc index e43f2c2e888..ed17034907c 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -244,9 +244,99 @@ bool ha_caching_allowed(THD* thd, char* table_key, return 1; } + +/* + Register handler error messages for use with my_error(). + + SYNOPSIS + ha_init_errors() + + RETURN + 0 OK + != 0 Error +*/ + +static int ha_init_errors(void) +{ +#define SETMSG(nr, msg) errmsgs[(nr) - HA_ERR_FIRST]= (msg) + const char **errmsgs; + + /* Allocate a pointer array for the error message strings. */ + /* Zerofill it to avoid uninitialized gaps. */ + if (! (errmsgs= (const char**) my_malloc(HA_ERR_ERRORS * sizeof(char*), + MYF(MY_WME | MY_ZEROFILL)))) + return 1; + + /* Set the dedicated error messages. */ + SETMSG(HA_ERR_KEY_NOT_FOUND, ER(ER_KEY_NOT_FOUND)); + SETMSG(HA_ERR_FOUND_DUPP_KEY, ER(ER_DUP_KEY)); + SETMSG(HA_ERR_RECORD_CHANGED, "Update wich is recoverable"); + SETMSG(HA_ERR_WRONG_INDEX, "Wrong index given to function"); + SETMSG(HA_ERR_CRASHED, ER(ER_NOT_KEYFILE)); + SETMSG(HA_ERR_WRONG_IN_RECORD, ER(ER_CRASHED_ON_USAGE)); + SETMSG(HA_ERR_OUT_OF_MEM, "Table handler out of memory"); + SETMSG(HA_ERR_NOT_A_TABLE, "Incorrect file format '%.64s'"); + SETMSG(HA_ERR_WRONG_COMMAND, "Command not supported"); + SETMSG(HA_ERR_OLD_FILE, ER(ER_OLD_KEYFILE)); + SETMSG(HA_ERR_NO_ACTIVE_RECORD, "No record read in update"); + SETMSG(HA_ERR_RECORD_DELETED, "Intern record deleted"); + SETMSG(HA_ERR_RECORD_FILE_FULL, ER(ER_RECORD_FILE_FULL)); + SETMSG(HA_ERR_INDEX_FILE_FULL, "No more room in index file '%.64s'"); + SETMSG(HA_ERR_END_OF_FILE, "End in next/prev/first/last"); + SETMSG(HA_ERR_UNSUPPORTED, ER(ER_ILLEGAL_HA)); + SETMSG(HA_ERR_TO_BIG_ROW, "Too big row"); + SETMSG(HA_WRONG_CREATE_OPTION, "Wrong create option"); + SETMSG(HA_ERR_FOUND_DUPP_UNIQUE, ER(ER_DUP_UNIQUE)); + SETMSG(HA_ERR_UNKNOWN_CHARSET, "Can't open charset"); + SETMSG(HA_ERR_WRONG_MRG_TABLE_DEF, ER(ER_WRONG_MRG_TABLE)); + SETMSG(HA_ERR_CRASHED_ON_REPAIR, ER(ER_CRASHED_ON_REPAIR)); + SETMSG(HA_ERR_CRASHED_ON_USAGE, ER(ER_CRASHED_ON_USAGE)); + SETMSG(HA_ERR_LOCK_WAIT_TIMEOUT, ER(ER_LOCK_WAIT_TIMEOUT)); + SETMSG(HA_ERR_LOCK_TABLE_FULL, ER(ER_LOCK_TABLE_FULL)); + SETMSG(HA_ERR_READ_ONLY_TRANSACTION, ER(ER_READ_ONLY_TRANSACTION)); + SETMSG(HA_ERR_LOCK_DEADLOCK, ER(ER_LOCK_DEADLOCK)); + SETMSG(HA_ERR_CANNOT_ADD_FOREIGN, ER(ER_CANNOT_ADD_FOREIGN)); + SETMSG(HA_ERR_NO_REFERENCED_ROW, ER(ER_NO_REFERENCED_ROW)); + SETMSG(HA_ERR_ROW_IS_REFERENCED, ER(ER_ROW_IS_REFERENCED)); + SETMSG(HA_ERR_NO_SAVEPOINT, "No savepoint with that name"); + SETMSG(HA_ERR_NON_UNIQUE_BLOCK_SIZE, "Non unique key block size"); + SETMSG(HA_ERR_NO_SUCH_TABLE, "No such table: '%.64s'"); + SETMSG(HA_ERR_TABLE_EXIST, ER(ER_TABLE_EXISTS_ERROR)); + SETMSG(HA_ERR_NO_CONNECTION, "Could not connect to storage engine"); + + /* Register the error messages for use with my_error(). */ + return my_error_register(errmsgs, HA_ERR_FIRST, HA_ERR_LAST); +} + + +/* + Unregister handler error messages. + + SYNOPSIS + ha_finish_errors() + + RETURN + 0 OK + != 0 Error +*/ + +static int ha_finish_errors(void) +{ + const char **errmsgs; + + /* Allocate a pointer array for the error message strings. */ + if (! (errmsgs= my_error_unregister(HA_ERR_FIRST, HA_ERR_LAST))) + return 1; + my_free((gptr) errmsgs, MYF(0)); + return 0; +} + + int ha_init() { int error= 0; + if (ha_init_errors()) + return 1; #ifdef HAVE_BERKELEY_DB if (have_berkeley_db == SHOW_OPTION_YES) { @@ -314,6 +404,8 @@ int ha_panic(enum ha_panic_function flag) if (have_ndbcluster == SHOW_OPTION_YES) error|=ndbcluster_end(); #endif + if (ha_finish_errors()) + error= 1; return error; } /* ha_panic */ @@ -1241,9 +1333,15 @@ void handler::print_error(int error, myf errflag) case HA_ERR_CRASHED: textno=ER_NOT_KEYFILE; break; + case HA_ERR_WRONG_IN_RECORD: + textno= ER_CRASHED_ON_USAGE; + break; case HA_ERR_CRASHED_ON_USAGE: textno=ER_CRASHED_ON_USAGE; break; + case HA_ERR_NOT_A_TABLE: + textno= error; + break; case HA_ERR_CRASHED_ON_REPAIR: textno=ER_CRASHED_ON_REPAIR; break; @@ -1262,6 +1360,9 @@ void handler::print_error(int error, myf errflag) case HA_ERR_RECORD_FILE_FULL: textno=ER_RECORD_FILE_FULL; break; + case HA_ERR_INDEX_FILE_FULL: + textno= errno; + break; case HA_ERR_LOCK_WAIT_TIMEOUT: textno=ER_LOCK_WAIT_TIMEOUT; break; diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8eec97efd81..8d3c99bc146 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -1005,7 +1005,9 @@ void clean_up(bool print_message) if (!opt_bootstrap) (void) my_delete(pidfile_name,MYF(0)); // This may not always exist #endif - x_free((gptr) my_errmsg[ERRMAPP]); /* Free messages */ + finish_client_errs(); + const char **errmsgs= my_error_unregister(ER_ERROR_FIRST, ER_ERROR_LAST); + x_free((gptr) errmsgs); /* Free messages */ DBUG_PRINT("quit", ("Error messages freed")); /* Tell main we are ready */ (void) pthread_mutex_lock(&LOCK_thread_count); diff --git a/sql/table.cc b/sql/table.cc index c18a2557337..3cf6bbded58 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -76,6 +76,7 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, my_string record; const char **int_array; bool use_hash, null_field_first; + bool error_reported= FALSE; File file; Field **field_ptr,*reg_field; KEY *keyinfo; @@ -788,6 +789,11 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, error= 1; my_errno= ENOENT; } + else + { + outparam->file->print_error(err, MYF(0)); + error_reported= TRUE; + } goto err_not_open; /* purecov: inspected */ } } @@ -814,7 +820,8 @@ int openfrm(THD *thd, const char *name, const char *alias, uint db_stat, err_end: /* Here when no file */ delete crypted; *root_ptr= old_root; - frm_error(error,outparam,name,ME_ERROR+ME_WAITTANG); + if (! error_reported) + frm_error(error,outparam,name,ME_ERROR+ME_WAITTANG); delete outparam->file; outparam->file=0; // For easier errorchecking outparam->db_stat=0; diff --git a/sql/unireg.h b/sql/unireg.h index 932bdf4dfc5..053ca393ad0 100644 --- a/sql/unireg.h +++ b/sql/unireg.h @@ -37,8 +37,8 @@ #define SHAREDIR "share/" #endif -#define ER(X) errmesg[(X)-1000] -#define ER_SAFE(X) (((X) >= 1000 && (X) < ER_ERROR_MESSAGES + 1000) ? ER(X) : "Invalid error code") +#define ER(X) errmesg[(X) - ER_ERROR_FIRST] +#define ER_SAFE(X) (((X) >= ER_ERROR_FIRST && (X) <= ER_ERROR_LAST) ? ER(X) : "Invalid error code") #define ERRMAPP 1 /* Errormap f|r my_error */ From 8d11c01c595261c9c742356c0152c2152b847db9 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 23 Dec 2004 21:45:10 +0100 Subject: [PATCH 07/13] WL#2126 - Multi_read_range. Added the required structures and functions for handing over multiple key ranges to the table handler. include/my_base.h: WL#2126 - Multi_read_range. Moved key range flags from sql/opt_range.h to here. Added the multi-range structure. sql/handler.cc: WL#2126 - Multi_read_range. Added the new table handler methods. sql/handler.h: WL#2126 - Multi_read_range. Added a new table flag. Added a declaration for the handler buffer. Added new elements to class handler. Added new function declarations. sql/mysqld.cc: WL#2126 - Multi_read_range. Added an option to set new system variable 'multi_range_count'. sql/opt_range.cc: WL#2126 - Multi_read_range. Added initialization for the new class members. Added initialization for the extended get_next(). Added de-initialization for the allocated buffers. Added a buffer allocation method. Added an inner loop to collect multiple ranges. Adapted range collection loops to the new initialization. sql/opt_range.h: WL#2126 - Multi_read_range. Moved key range flags from here to include/my_base.h. Added new elements to class QUICK_RANGE_SELECT. Added a copy constructor. sql/records.cc: WL#2126 - Multi_read_range. Added a call of the allocation method. sql/set_var.cc: WL#2126 - Multi_read_range. Added the new system variable 'multi_range_count'. sql/sql_class.h: WL#2126 - Multi_read_range. Added the new system variable 'multi_range_count'. --- include/my_base.h | 17 ++++ sql/handler.cc | 125 ++++++++++++++++++++++++ sql/handler.h | 26 +++++ sql/mysqld.cc | 7 +- sql/opt_range.cc | 241 +++++++++++++++++++++++++++++++++++----------- sql/opt_range.h | 47 ++++++--- sql/records.cc | 8 ++ sql/set_var.cc | 2 + sql/sql_class.h | 1 + 9 files changed, 406 insertions(+), 68 deletions(-) diff --git a/include/my_base.h b/include/my_base.h index 4d043cf6b5b..d219cdd4289 100644 --- a/include/my_base.h +++ b/include/my_base.h @@ -370,6 +370,15 @@ enum data_file_type { /* For key ranges */ +#define NO_MIN_RANGE 1 +#define NO_MAX_RANGE 2 +#define NEAR_MIN 4 +#define NEAR_MAX 8 +#define UNIQUE_RANGE 16 +#define EQ_RANGE 32 +#define NULL_RANGE 64 +#define GEOM_FLAG 128 + typedef struct st_key_range { const byte *key; @@ -377,6 +386,14 @@ typedef struct st_key_range enum ha_rkey_function flag; } key_range; +typedef struct st_key_multi_range +{ + key_range start_key; + key_range end_key; + char *ptr; /* Free to use by caller (ptr to row etc) */ + uint range_flag; /* key range flags see above */ +} KEY_MULTI_RANGE; + /* For number of records */ #ifdef BIG_TABLES diff --git a/sql/handler.cc b/sql/handler.cc index e43f2c2e888..87a66dc54b5 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -1685,6 +1685,131 @@ int ha_table_exists(THD* thd, const char* db, const char* name) #endif +/* + Read the first row of a multi-range set. + + SYNOPSIS + read_multi_range_first() + found_range_p Returns a pointer to the element in 'ranges' that + corresponds to the returned row. + ranges An array of KEY_MULTI_RANGE range descriptions. + range_count Number of ranges in 'ranges'. + sorted If result should be sorted per key. + buffer A HANDLER_BUFFER for internal handler usage. + + NOTES + Record is read into table->record[0]. + *found_range_p returns a valid value only if read_multi_range_first() + returns 0. + Sorting is done within each range. If you want an overall sort, enter + 'ranges' with sorted ranges. + + RETURN + 0 OK, found a row + HA_ERR_END_OF_FILE No rows in range + # Error code +*/ + +int handler::read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE *ranges, uint range_count, + bool sorted, HANDLER_BUFFER *buffer) +{ + int result= HA_ERR_END_OF_FILE; + DBUG_ENTER("handler::read_multi_range_first"); + multi_range_sorted= sorted; + multi_range_buffer= buffer; + + for (multi_range_curr= ranges, multi_range_end= ranges + range_count; + multi_range_curr < multi_range_end; + multi_range_curr++) + { + result= read_range_first(multi_range_curr->start_key.length ? + &multi_range_curr->start_key : 0, + multi_range_curr->end_key.length ? + &multi_range_curr->end_key : 0, + test(multi_range_curr->range_flag & EQ_RANGE), + multi_range_sorted); + if (result != HA_ERR_END_OF_FILE) + break; + } + + *found_range_p= multi_range_curr; + DBUG_PRINT("exit",("result %d", result)); + DBUG_RETURN(result); +} + + +/* + Read the next row of a multi-range set. + + SYNOPSIS + read_multi_range_next() + found_range_p Returns a pointer to the element in 'ranges' that + corresponds to the returned row. + + NOTES + Record is read into table->record[0]. + *found_range_p returns a valid value only if read_multi_range_next() + returns 0. + + RETURN + 0 OK, found a row + HA_ERR_END_OF_FILE No (more) rows in range + # Error code +*/ + +int handler::read_multi_range_next(KEY_MULTI_RANGE **found_range_p) +{ + int result; + DBUG_ENTER("handler::read_multi_range_next"); + + /* We should not be called after the last call returned EOF. */ + DBUG_ASSERT(multi_range_curr < multi_range_end); + + do + { + /* Save a call if there can be only one row in range. */ + if (multi_range_curr->range_flag != (UNIQUE_RANGE | EQ_RANGE)) + { + result= read_range_next(); + + /* On success or non-EOF errors jump to the end. */ + if (result != HA_ERR_END_OF_FILE) + break; + } + else + { + /* + We need to set this for the last range only, but checking this + condition is more expensive than just setting the result code. + */ + result= HA_ERR_END_OF_FILE; + } + + /* Try the next range(s) until one matches a record. */ + for (multi_range_curr++; + multi_range_curr < multi_range_end; + multi_range_curr++) + { + result= read_range_first(multi_range_curr->start_key.length ? + &multi_range_curr->start_key : 0, + multi_range_curr->end_key.length ? + &multi_range_curr->end_key : 0, + test(multi_range_curr->range_flag & EQ_RANGE), + multi_range_sorted); + if (result != HA_ERR_END_OF_FILE) + break; + } + } + while ((result == HA_ERR_END_OF_FILE) && + (multi_range_curr < multi_range_end)); + + *found_range_p= multi_range_curr; + DBUG_PRINT("exit",("handler::read_multi_range_next: result %d", result)); + DBUG_RETURN(result); +} + + /* Read first row between two ranges. Store ranges for future calls to read_range_next diff --git a/sql/handler.h b/sql/handler.h index f0faeff9234..62638ee456b 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -76,6 +76,7 @@ #define HA_FILE_BASED (1 << 26) #define HA_NO_VARCHAR (1 << 27) #define HA_CAN_BIT_FIELD (1 << 28) /* supports bit fields */ +#define HA_NEED_READ_RANGE_BUFFER (1 << 29) /* for read_multi_range */ /* bits in index_flags(index_number) for what you can do with index */ @@ -276,6 +277,21 @@ typedef struct st_ha_check_opt } HA_CHECK_OPT; +/* + This is a buffer area that the handler can use to store rows. + 'end_of_used_area' should be kept updated after calls to + read-functions so that other parts of the code can use the + remaining area (until next read calls is issued). +*/ + +typedef struct st_handler_buffer +{ + const byte *buffer; /* Buffer one can start using */ + const byte *buffer_end; /* End of buffer */ + byte *end_of_used_area; /* End of area that was used by handler */ +} HANDLER_BUFFER; + + class handler :public Sql_alloc { protected: @@ -310,6 +326,12 @@ public: time_t check_time; time_t update_time; + /* The following are for read_multi_range */ + bool multi_range_sorted; + KEY_MULTI_RANGE *multi_range_curr; + KEY_MULTI_RANGE *multi_range_end; + HANDLER_BUFFER *multi_range_buffer; + /* The following are for read_range() */ key_range save_end_range, *end_range; KEY_PART_INFO *range_key_part; @@ -421,6 +443,10 @@ public: virtual int index_next_same(byte *buf, const byte *key, uint keylen); virtual int index_read_last(byte * buf, const byte * key, uint key_len) { return (my_errno=HA_ERR_WRONG_COMMAND); } + virtual int read_multi_range_first(KEY_MULTI_RANGE **found_range_p, + KEY_MULTI_RANGE *ranges, uint range_count, + bool sorted, HANDLER_BUFFER *buffer); + virtual int read_multi_range_next(KEY_MULTI_RANGE **found_range_p); virtual int read_range_first(const key_range *start_key, const key_range *end_key, bool eq_range, bool sorted); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 8eec97efd81..34b1fbcd364 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4141,7 +4141,7 @@ enum options_mysqld OPT_MAX_SEEKS_FOR_KEY, OPT_MAX_TMP_TABLES, OPT_MAX_USER_CONNECTIONS, OPT_MAX_LENGTH_FOR_SORT_DATA, OPT_MAX_WRITE_LOCK_COUNT, OPT_BULK_INSERT_BUFFER_SIZE, - OPT_MAX_ERROR_COUNT, OPT_MYISAM_DATA_POINTER_SIZE, + OPT_MAX_ERROR_COUNT, OPT_MULTI_RANGE_COUNT, OPT_MYISAM_DATA_POINTER_SIZE, OPT_MYISAM_BLOCK_SIZE, OPT_MYISAM_MAX_EXTRA_SORT_FILE_SIZE, OPT_MYISAM_MAX_SORT_FILE_SIZE, OPT_MYISAM_SORT_BUFFER_SIZE, OPT_NET_BUFFER_LENGTH, OPT_NET_RETRY_COUNT, @@ -5137,6 +5137,11 @@ The minimum value for this variable is 4096.", "After this many write locks, allow some read locks to run in between.", (gptr*) &max_write_lock_count, (gptr*) &max_write_lock_count, 0, GET_ULONG, REQUIRED_ARG, ~0L, 1, ~0L, 0, 1, 0}, + {"multi_range_count", OPT_MULTI_RANGE_COUNT, + "Number of key ranges to request at once.", + (gptr*) &global_system_variables.multi_range_count, + (gptr*) &max_system_variables.multi_range_count, 0, + GET_ULONG, REQUIRED_ARG, 256, 1, ~0L, 0, 1, 0}, {"myisam_block_size", OPT_MYISAM_BLOCK_SIZE, "Block size to be used for MyISAM index pages.", (gptr*) &opt_myisam_block_size, diff --git a/sql/opt_range.cc b/sql/opt_range.cc index e47c7e147a7..e7f03b51fc0 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -712,7 +712,7 @@ QUICK_SELECT_I::QUICK_SELECT_I() QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, bool no_alloc, MEM_ROOT *parent_alloc) - :dont_free(0),error(0),free_file(0),cur_range(NULL),range(0) + :dont_free(0),error(0),free_file(0),cur_range(NULL),range(0),in_range(0) { sorted= 0; index= key_nr; @@ -720,6 +720,13 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, key_part_info= head->key_info[index].key_part; my_init_dynamic_array(&ranges, sizeof(QUICK_RANGE*), 16, 16); + /* 'thd' is not accessible in QUICK_RANGE_SELECT::get_next_init(). */ + multi_range_bufsiz= thd->variables.read_rnd_buff_size; + multi_range_count= thd->variables.multi_range_count; + multi_range_length= 0; + multi_range= NULL; + multi_range_buff= NULL; + if (!no_alloc && !parent_alloc) { // Allocates everything through the internal memroot @@ -736,6 +743,10 @@ QUICK_RANGE_SELECT::QUICK_RANGE_SELECT(THD *thd, TABLE *table, uint key_nr, int QUICK_RANGE_SELECT::init() { DBUG_ENTER("QUICK_RANGE_SELECT::init"); + + if ((error= get_next_init())) + DBUG_RETURN(error); + if (file->inited == handler::NONE) DBUG_RETURN(error= file->ha_index_init(index)); error= 0; @@ -771,6 +782,10 @@ QUICK_RANGE_SELECT::~QUICK_RANGE_SELECT() delete_dynamic(&ranges); /* ranges are allocated in alloc */ free_root(&alloc,MYF(0)); } + if (multi_range) + my_free((char*) multi_range, MYF(0)); + if (multi_range_buff) + my_free((char*) multi_range_buff, MYF(0)); DBUG_VOID_RETURN; } @@ -5872,58 +5887,178 @@ int QUICK_ROR_UNION_SELECT::get_next() DBUG_RETURN(error); } - /* get next possible record using quick-struct */ + +/* + Initialize data structures needed by get_next(). + + SYNOPSIS + QUICK_RANGE_SELECT::get_next_init() + + DESCRIPTION + This is called from get_next() at its first call for an object. + It allocates memory buffers and sets size variables. + + RETURN + 0 OK. + != 0 Error. +*/ + +int QUICK_RANGE_SELECT::get_next_init(void) +{ + uint mrange_bufsiz; + byte *mrange_buff; + DBUG_ENTER("QUICK_RANGE_SELECT::get_next_init"); + + /* Do not allocate the buffers twice. */ + if (multi_range_length) + { + DBUG_ASSERT(multi_range_length == min(multi_range_count, ranges.elements)); + DBUG_RETURN(0); + } + + /* If the ranges are not yet initialized, wait for the next call. */ + if (! ranges.elements) + { + DBUG_RETURN(0); + } + + /* + Allocate the ranges array. + */ + multi_range_length= min(multi_range_count, ranges.elements); + DBUG_ASSERT(multi_range_length > 0); + while (multi_range_length && ! (multi_range= (KEY_MULTI_RANGE*) + my_malloc(multi_range_length * + sizeof(KEY_MULTI_RANGE), + MYF(MY_WME)))) + { + /* Try to shrink the buffers until it is 0. */ + multi_range_length/= 2; + } + if (! multi_range) + { + multi_range_length= 0; + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + + /* + Allocate the handler buffer if necessary. + */ + if (file->table_flags() & HA_NEED_READ_RANGE_BUFFER) + { + mrange_bufsiz= min(multi_range_bufsiz, + QUICK_SELECT_I::records * head->reclength); + + while (mrange_bufsiz && + ! my_multi_malloc(MYF(MY_WME), + &multi_range_buff, sizeof(*multi_range_buff), + &mrange_buff, mrange_bufsiz, + NullS)) + { + /* Try to shrink the buffers until both are 0. */ + mrange_bufsiz/= 2; + } + if (! multi_range_buff) + { + my_free((char*) multi_range, MYF(0)); + multi_range= NULL; + multi_range_length= 0; + DBUG_RETURN(HA_ERR_OUT_OF_MEM); + } + + /* Initialize the handler buffer. */ + multi_range_buff->buffer= mrange_buff; + multi_range_buff->buffer_end= mrange_buff + mrange_bufsiz; + multi_range_buff->end_of_used_area= mrange_buff; + } + + /* Initialize the current QUICK_RANGE pointer. */ + cur_range= (QUICK_RANGE**) ranges.buffer; + DBUG_RETURN(0); +} + + +/* + Get next possible record using quick-struct. + + SYNOPSIS + QUICK_RANGE_SELECT::get_next() + + NOTES + Record is read into table->record[0] + + RETURN + 0 Found row + HA_ERR_END_OF_FILE No (more) rows in range + # Error code +*/ int QUICK_RANGE_SELECT::get_next() { + int result; + KEY_MULTI_RANGE *mrange; + key_range *start_key; + key_range *end_key; DBUG_ENTER("QUICK_RANGE_SELECT::get_next"); + DBUG_ASSERT(multi_range_length && multi_range && + (cur_range >= (QUICK_RANGE**) ranges.buffer) && + (cur_range <= (QUICK_RANGE**) ranges.buffer + ranges.elements)); for (;;) { - int result; - key_range start_key, end_key; - if (range) + if (in_range) { - // Already read through key - result= file->read_range_next(); + /* We did already start to read this key. */ + result= file->read_multi_range_next(&mrange); if (result != HA_ERR_END_OF_FILE) + { + in_range= ! result; DBUG_RETURN(result); + } } - if (!cur_range) - range= *(cur_range= (QUICK_RANGE**) ranges.buffer); - else - range= - (cur_range == ((QUICK_RANGE**) ranges.buffer + ranges.elements - 1)) ? - (QUICK_RANGE*) 0 : *(++cur_range); + uint count= min(multi_range_length, ranges.elements - + (cur_range - (QUICK_RANGE**) ranges.buffer)); + if (count == 0) + { + /* Ranges have already been used up before. None is left for read. */ + in_range= FALSE; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + KEY_MULTI_RANGE *mrange_slot, *mrange_end; + for (mrange_slot= multi_range, mrange_end= mrange_slot+count; + mrange_slot < mrange_end; + mrange_slot++) + { + start_key= &mrange_slot->start_key; + end_key= &mrange_slot->end_key; + range= *(cur_range++); - if (!range) - DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used + start_key->key= (const byte*) range->min_key; + start_key->length= range->min_length; + start_key->flag= ((range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : + (range->flag & EQ_RANGE) ? + HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT); + end_key->key= (const byte*) range->max_key; + end_key->length= range->max_length; + /* + We use HA_READ_AFTER_KEY here because if we are reading on a key + prefix. We want to find all keys with this prefix. + */ + end_key->flag= (range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : + HA_READ_AFTER_KEY); - start_key.key= (const byte*) range->min_key; - start_key.length= range->min_length; - start_key.flag= ((range->flag & NEAR_MIN) ? HA_READ_AFTER_KEY : - (range->flag & EQ_RANGE) ? - HA_READ_KEY_EXACT : HA_READ_KEY_OR_NEXT); - end_key.key= (const byte*) range->max_key; - end_key.length= range->max_length; - /* - We use READ_AFTER_KEY here because if we are reading on a key - prefix we want to find all keys with this prefix - */ - end_key.flag= (range->flag & NEAR_MAX ? HA_READ_BEFORE_KEY : - HA_READ_AFTER_KEY); - - result= file->read_range_first(range->min_length ? &start_key : 0, - range->max_length ? &end_key : 0, - test(range->flag & EQ_RANGE), - sorted); - if (range->flag == (UNIQUE_RANGE | EQ_RANGE)) - range=0; // Stop searching + mrange_slot->range_flag= range->flag; + } + result= file->read_multi_range_first(&mrange, multi_range, count, + sorted, multi_range_buff); if (result != HA_ERR_END_OF_FILE) + { + in_range= ! result; DBUG_RETURN(result); - range=0; // No matching rows; go to next range + } + in_range= FALSE; /* No matching rows; go to next set of ranges. */ } } @@ -5974,15 +6109,14 @@ int QUICK_RANGE_SELECT::get_next_prefix(uint prefix_length, byte *cur_prefix) DBUG_RETURN(result); } - if (!cur_range) - range= *(cur_range= (QUICK_RANGE**) ranges.buffer); /* First range. */ - else - range= - (cur_range == ((QUICK_RANGE**) ranges.buffer + ranges.elements - 1)) ? - (QUICK_RANGE*) 0 : *(++cur_range); /* Next range. */ - - if (!range) - DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used + uint count= ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer); + if (count == 0) + { + /* Ranges have already been used up before. None is left for read. */ + range= 0; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + range= *(cur_range++); start_key.key= (const byte*) range->min_key; start_key.length= min(range->min_length, prefix_length); @@ -6030,15 +6164,14 @@ int QUICK_RANGE_SELECT_GEOM::get_next() DBUG_RETURN(result); } - if (!cur_range) - range= *(cur_range= (QUICK_RANGE**) ranges.buffer); - else - range= - (cur_range == ((QUICK_RANGE**) ranges.buffer + ranges.elements - 1)) ? - (QUICK_RANGE*) 0 : *(++cur_range); - - if (!range) - DBUG_RETURN(HA_ERR_END_OF_FILE); // All ranges used + uint count= ranges.elements - (cur_range - (QUICK_RANGE**) ranges.buffer); + if (count == 0) + { + /* Ranges have already been used up before. None is left for read. */ + range= 0; + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + range= *(cur_range++); result= file->index_read(record, (byte*) range->min_key, diff --git a/sql/opt_range.h b/sql/opt_range.h index 74d388128c8..71981dfb5c7 100644 --- a/sql/opt_range.h +++ b/sql/opt_range.h @@ -24,16 +24,6 @@ #pragma interface /* gcc class implementation */ #endif -#define NO_MIN_RANGE 1 -#define NO_MAX_RANGE 2 -#define NEAR_MIN 4 -#define NEAR_MAX 8 -#define UNIQUE_RANGE 16 -#define EQ_RANGE 32 -#define NULL_RANGE 64 -#define GEOM_FLAG 128 - - typedef struct st_key_part { uint16 key,part, store_length, length; uint8 null_bit; @@ -135,9 +125,24 @@ public: */ virtual int reset(void) = 0; + /* + Initialize get_next() for row retrieval. + SYNOPSIS + get_next_init() + + get_next_init() must be called before the first get_next(). + If get_next_init() call fails get_next() must not be called. + + RETURN + 0 OK + other Error code + */ + virtual int get_next_init() { return false; } + virtual int get_next() = 0; /* get next record to retrieve */ + /* Range end should be called when we have looped over the whole index */ virtual void range_end() {} - virtual int get_next() = 0; /* get next record to retrieve */ + virtual bool reverse_sorted() = 0; virtual bool unique_key_range() { return false; } @@ -236,6 +241,14 @@ protected: closed no later then this quick select is deleted. */ bool free_file; + bool in_range; + uint multi_range_count; /* copy from thd->variables.multi_range_count */ + uint multi_range_length; /* the allocated length for the array */ + uint multi_range_bufsiz; /* copy from thd->variables.read_rnd_buff_size */ + KEY_MULTI_RANGE *multi_range; /* the multi-range array (allocated and + freed by QUICK_RANGE_SELECT) */ + HANDLER_BUFFER *multi_range_buff; /* the handler buffer (allocated and + freed by QUICK_RANGE_SELECT) */ protected: friend class TRP_ROR_INTERSECT; @@ -270,18 +283,19 @@ public: MEM_ROOT *parent_alloc=NULL); ~QUICK_RANGE_SELECT(); + int init(); int reset(void) { next=0; range= NULL; - cur_range= NULL; + cur_range= (QUICK_RANGE**) ranges.buffer; /* Note: in opt_range.cc there are places where it is assumed that this function always succeeds */ return 0; } - int init(); + int get_next_init(void); int get_next(); void range_end(); int get_next_prefix(uint prefix_length, byte *cur_prefix); @@ -296,6 +310,13 @@ public: #ifndef DBUG_OFF void dbug_dump(int indent, bool verbose); #endif + QUICK_RANGE_SELECT(const QUICK_RANGE_SELECT& org) : QUICK_SELECT_I() + { + bcopy(&org, this, sizeof(*this)); + multi_range_length= 0; + multi_range= NULL; + multi_range_buff= NULL; + } }; diff --git a/sql/records.cc b/sql/records.cc index 5963c36afd9..3c0143d2307 100644 --- a/sql/records.cc +++ b/sql/records.cc @@ -100,11 +100,19 @@ void init_read_record(READ_RECORD *info,THD *thd, TABLE *table, } else if (select && select->quick) { + int error; DBUG_PRINT("info",("using rr_quick")); if (!table->file->inited) table->file->ha_index_init(select->quick->index); info->read_record=rr_quick; + + if ((error= select->quick->get_next_init())) + { + /* Cannot return error code here. Instead print to error log. */ + table->file->print_error(error,MYF(ME_NOREFRESH)); + thd->fatal_error(); + } } else if (table->sort.record_pointers) { diff --git a/sql/set_var.cc b/sql/set_var.cc index b42ca91c7dd..2d453925fa5 100644 --- a/sql/set_var.cc +++ b/sql/set_var.cc @@ -259,6 +259,8 @@ sys_var_thd_ulong sys_max_tmp_tables("max_tmp_tables", &SV::max_tmp_tables); sys_var_long_ptr sys_max_write_lock_count("max_write_lock_count", &max_write_lock_count); +sys_var_thd_ulong sys_multi_range_count("multi_range_count", + &SV::multi_range_count); sys_var_long_ptr sys_myisam_data_pointer_size("myisam_data_pointer_size", &myisam_data_pointer_size); sys_var_thd_ulonglong sys_myisam_max_extra_sort_file_size("myisam_max_extra_sort_file_size", &SV::myisam_max_extra_sort_file_size, fix_myisam_max_extra_sort_file_size, 1); diff --git a/sql/sql_class.h b/sql/sql_class.h index 6cec2c2c787..af1ed0b2209 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -388,6 +388,7 @@ struct system_variables ulong max_sort_length; ulong max_tmp_tables; ulong max_insert_delayed_threads; + ulong multi_range_count; ulong myisam_repair_threads; ulong myisam_sort_buff_size; ulong net_buffer_length; From 2c92b3f8806049d25e1599783a7b45399ba5b109 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Dec 2004 12:16:47 +0300 Subject: [PATCH 08/13] Fix for bug: #7218: information_schema: errors in "tables" --- mysql-test/r/information_schema.result | 8 ++++++++ mysql-test/t/information_schema.test | 7 +++++++ sql/sql_show.cc | 12 ++++++++---- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/mysql-test/r/information_schema.result b/mysql-test/r/information_schema.result index 9f379a0b3c6..037588afda8 100644 --- a/mysql-test/r/information_schema.result +++ b/mysql-test/r/information_schema.result @@ -653,3 +653,11 @@ where table_schema="information_schema" and table_name="COLUMNS" and column_type varchar(64) varchar(64) +select TABLE_ROWS from information_schema.tables where +table_schema="information_schema" and table_name="COLUMNS"; +TABLE_ROWS +NULL +select table_type from information_schema.tables +where table_schema="mysql" and table_name="user"; +table_type +BASE TABLE diff --git a/mysql-test/t/information_schema.test b/mysql-test/t/information_schema.test index b575e48e72f..66315fe7a29 100644 --- a/mysql-test/t/information_schema.test +++ b/mysql-test/t/information_schema.test @@ -339,3 +339,10 @@ select column_type from information_schema.columns where table_schema="information_schema" and table_name="COLUMNS" and (column_name="character_set_name" or column_name="collation_name"); +# +# Bug#2718 information_schema: errors in "tables" +# +select TABLE_ROWS from information_schema.tables where +table_schema="information_schema" and table_name="COLUMNS"; +select table_type from information_schema.tables +where table_schema="mysql" and table_name="user"; diff --git a/sql/sql_show.cc b/sql/sql_show.cc index f0d12333c58..d075e428aa0 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -2249,14 +2249,14 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, TABLE *show_table= tables->table; handler *file= show_table->file; file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK); - if (table->tmp_table == TMP_TABLE) + if (show_table->tmp_table == TMP_TABLE) table->field[3]->store("TEMPORARY", 9, cs); else table->field[3]->store("BASE TABLE", 10, cs); for (int i= 4; i < 20; i++) { - if ((i > 12 && i < 17) || i == 18) + if (i == 7 || (i > 12 && i < 17) || i == 18) continue; table->field[i]->set_notnull(); } @@ -2268,7 +2268,11 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables, (show_table->db_options_in_use & HA_OPTION_PACK_RECORD) ? "Dynamic" : "Fixed"); table->field[6]->store(tmp_buff, strlen(tmp_buff), cs); - table->field[7]->store((longlong) file->records); + if (!tables->schema_table) + { + table->field[7]->store((longlong) file->records); + table->field[7]->set_notnull(); + } table->field[8]->store((longlong) file->mean_rec_length); table->field[9]->store((longlong) file->data_file_length); if (file->max_data_file_length) @@ -3481,7 +3485,7 @@ ST_FIELD_INFO tables_fields_info[]= {"CREATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Create_time"}, {"UPDATE_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Update_time"}, {"CHECK_TIME", 0, MYSQL_TYPE_TIMESTAMP, 0, 1, "Check_time"}, - {"COLLATION", 60, MYSQL_TYPE_STRING, 0, 1, "Collation"}, + {"TABLE_COLLATION", 64, MYSQL_TYPE_STRING, 0, 1, "Collation"}, {"CHECKSUM", 21 , MYSQL_TYPE_LONG, 0, 1, "Checksum"}, {"CREATE_OPTIONS", 255, MYSQL_TYPE_STRING, 0, 1, "Create_options"}, {"TABLE_COMMENT", 80, MYSQL_TYPE_STRING, 0, 0, "Comment"}, From 6499169d811a041cdccd561cced7f1478a8e0719 Mon Sep 17 00:00:00 2001 From: unknown Date: Fri, 24 Dec 2004 12:42:01 +0300 Subject: [PATCH 09/13] Fix for bug #7467: sql_lex.c on HPUX fails to compile --- sql/field.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sql/field.h b/sql/field.h index 6ce5cf2a526..9375fbc8d5a 100644 --- a/sql/field.h +++ b/sql/field.h @@ -113,7 +113,7 @@ public: This trickery is used to decrease a number of malloc calls. */ virtual String *val_str(String*,String *)=0; - String *Field::val_int_as_str(String *val_buffer, my_bool unsigned_flag); + String *val_int_as_str(String *val_buffer, my_bool unsigned_flag); virtual Item_result result_type () const=0; virtual Item_result cmp_type () const { return result_type(); } bool eq(Field *field) From 520e7f2d50d107cae54f749a1655886e015483c7 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Dec 2004 12:08:22 +0100 Subject: [PATCH 10/13] revert "LOCK TABLES ... WHERE ENGINE=INNODB" --- sql/handler.h | 7 ------ sql/lock.cc | 58 ------------------------------------------------ sql/mysql_priv.h | 1 - sql/sql_lex.h | 4 ++-- sql/sql_parse.cc | 25 ++------------------- sql/sql_yacc.yy | 13 ++--------- 6 files changed, 6 insertions(+), 102 deletions(-) diff --git a/sql/handler.h b/sql/handler.h index 62638ee456b..b10e6bfe88c 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -480,13 +480,6 @@ public: { return extra(operation); } virtual int reset() { return extra(HA_EXTRA_RESET); } virtual int external_lock(THD *thd, int lock_type) { return 0; } - /* - This is called to set transactional table lock to a table. - If the handler don't support this, then this function will - return HA_ERR_WRONG_COMMAND and MySQL will give - ER_ILLEGAL_HA error message. - */ - virtual int transactional_table_lock(THD *thd, int lock_type) {return HA_ERR_WRONG_COMMAND;} virtual void unlock_row() {} virtual int start_stmt(THD *thd) {return 0;} /* diff --git a/sql/lock.cc b/sql/lock.cc index 393cf4cf142..973e82b7b10 100644 --- a/sql/lock.cc +++ b/sql/lock.cc @@ -939,61 +939,3 @@ bool make_global_read_lock_block_commit(THD *thd) DBUG_RETURN(error); } -/* - Take transactional table lock for all tables in the list - - SYNOPSIS - transactional_lock_tables - thd Thread THD - tables list of tables - counter number of tables in the list - - NOTES - - RETURN - 0 - OK - -1 - error - -*/ -int transactional_lock_tables(THD *thd, TABLE_LIST *tables, uint counter) -{ - uint i; - int lock_type,error=0; - TABLE_LIST *table; - TABLE **start,**ptr; - - DBUG_ENTER("transactional_lock_tables"); - - if (!(ptr=start=(TABLE**) sql_alloc(sizeof(TABLE*) * counter))) - return -1; - - for (table= tables; table; table= table->next_global) - { - if (!table->placeholder() && !table->schema_table) - *(ptr++)= table->table; - } - - for (i=1 ; i <= counter ; i++, start++) - { - DBUG_ASSERT((*start)->reginfo.lock_type >= TL_READ); - lock_type=F_WRLCK; /* Lock exclusive */ - - if ((*start)->db_stat & HA_READ_ONLY || - ((*start)->reginfo.lock_type >= TL_READ && - (*start)->reginfo.lock_type <= TL_READ_NO_INSERT)) - lock_type=F_RDLCK; - - if ((error=(*start)->file->transactional_table_lock(thd, lock_type))) - { - print_lock_error(error, (*start)->file->table_type()); - DBUG_RETURN(-1); - } - else - { - (*start)->db_stat &= ~ HA_BLOCK_LOCK; - (*start)->current_lock= lock_type; - } - } - - DBUG_RETURN(0); -} diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index fc06893c281..d69669d097a 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -830,7 +830,6 @@ int open_tables(THD *thd, TABLE_LIST *tables, uint *counter); int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables); bool open_and_lock_tables(THD *thd,TABLE_LIST *tables); int lock_tables(THD *thd, TABLE_LIST *tables, uint counter); -int transactional_lock_tables(THD *thd, TABLE_LIST *tables, uint counter); TABLE *open_temporary_table(THD *thd, const char *path, const char *db, const char *table_name, bool link_in_list); bool rm_temporary_table(enum db_type base, char *path); diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 19af2d9f243..c5b3abb93d2 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -63,7 +63,7 @@ enum enum_sql_command { SQLCOM_CHANGE_DB, SQLCOM_CREATE_DB, SQLCOM_DROP_DB, SQLCOM_ALTER_DB, SQLCOM_REPAIR, SQLCOM_REPLACE, SQLCOM_REPLACE_SELECT, SQLCOM_CREATE_FUNCTION, SQLCOM_DROP_FUNCTION, - SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, + SQLCOM_REVOKE,SQLCOM_OPTIMIZE, SQLCOM_CHECK, SQLCOM_ASSIGN_TO_KEYCACHE, SQLCOM_PRELOAD_KEYS, SQLCOM_FLUSH, SQLCOM_KILL, SQLCOM_ANALYZE, SQLCOM_ROLLBACK, SQLCOM_ROLLBACK_TO_SAVEPOINT, @@ -87,8 +87,8 @@ enum enum_sql_command { SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE, SQLCOM_CREATE_VIEW, SQLCOM_DROP_VIEW, SQLCOM_CREATE_TRIGGER, SQLCOM_DROP_TRIGGER, - SQLCOM_LOCK_TABLES_TRANSACTIONAL, /* This should be the last !!! */ + SQLCOM_END }; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 1e232f064ba..4a357c6eefe 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3252,27 +3252,6 @@ create_error: thd->options&= ~(ulong) (OPTION_TABLE_LOCK); thd->in_lock_tables=0; break; - case SQLCOM_LOCK_TABLES_TRANSACTIONAL: - { - uint counter = 0; - - if (check_db_used(thd, all_tables)) - goto error; - if (check_table_access(thd, LOCK_TABLES_ACL | SELECT_ACL, all_tables, 0)) - goto error; - - thd->in_lock_tables=1; - thd->options|= OPTION_TABLE_LOCK; - - if (open_tables(thd, all_tables, &counter) == 0 && - transactional_lock_tables(thd, all_tables, counter) == 0) - send_ok(thd); - else - thd->options&= ~(ulong) (OPTION_TABLE_LOCK); - - thd->in_lock_tables=0; - break; - } case SQLCOM_CREATE_DB: { char *alias; @@ -3284,12 +3263,12 @@ create_error: /* If in a slave thread : CREATE DATABASE DB was certainly not preceded by USE DB. - For that reason, db_ok() in sql/slave.cc did not check the + For that reason, db_ok() in sql/slave.cc did not check the do_db/ignore_db. And as this query involves no tables, tables_ok() above was not called. So we have to check rules again here. */ #ifdef HAVE_REPLICATION - if (thd->slave_thread && + if (thd->slave_thread && (!db_ok(lex->name, replicate_do_db, replicate_ignore_db) || !db_ok_with_wild_table(lex->name))) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 75db6e332f9..5c03a4c98ef 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -7418,8 +7418,8 @@ lock: { Lex->sql_command=SQLCOM_LOCK_TABLES; } - table_lock_list lock_engine_opt - {} + table_lock_list + {} ; table_or_tables: @@ -7445,15 +7445,6 @@ lock_option: | READ_SYM LOCAL_SYM { $$= TL_READ; } ; -lock_engine_opt: - /* empty */ - | WHERE - { - Lex->sql_command=SQLCOM_LOCK_TABLES_TRANSACTIONAL; - } - ENGINE_SYM opt_equal storage_engines - ; - unlock: UNLOCK_SYM table_or_tables { Lex->sql_command=SQLCOM_UNLOCK_TABLES; } ; From 75463050b90d829e044f014b9b317a20b9809bf2 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Dec 2004 14:02:30 +0100 Subject: [PATCH 11/13] Ensure "bottom-up" order of "inline functions" within 'innobase/include/rem0rec.ic'. This is a partial solution for bug#7464. innobase/include/rem0rec.ic: On several platforms, "inline functions" must be coded before being used, in "bottom-up" order. Ensure this for 'rec_get_status' and 'rec_offs_n_fields'. --- innobase/include/rem0rec.ic | 78 ++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/innobase/include/rem0rec.ic b/innobase/include/rem0rec.ic index 6c3dabf04a2..2a1f8c8a0db 100644 --- a/innobase/include/rem0rec.ic +++ b/innobase/include/rem0rec.ic @@ -373,6 +373,26 @@ rec_set_n_fields_old( REC_OLD_N_FIELDS_MASK, REC_OLD_N_FIELDS_SHIFT); } +/********************************************************** +The following function retrieves the status bits of a new-style record. */ +UNIV_INLINE +ulint +rec_get_status( +/*===========*/ + /* out: status bits */ + rec_t* rec) /* in: physical record */ +{ + ulint ret; + + ut_ad(rec); + + ret = rec_get_bit_field_1(rec, REC_NEW_STATUS, + REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT); + ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0); + + return(ret); +} + /********************************************************** The following function is used to get the number of fields in a record. */ @@ -484,26 +504,6 @@ rec_set_info_bits( REC_INFO_BITS_MASK, REC_INFO_BITS_SHIFT); } -/********************************************************** -The following function retrieves the status bits of a new-style record. */ -UNIV_INLINE -ulint -rec_get_status( -/*===========*/ - /* out: status bits */ - rec_t* rec) /* in: physical record */ -{ - ulint ret; - - ut_ad(rec); - - ret = rec_get_bit_field_1(rec, REC_NEW_STATUS, - REC_NEW_STATUS_MASK, REC_NEW_STATUS_SHIFT); - ut_ad((ret & ~REC_NEW_STATUS_MASK) == 0); - - return(ret); -} - /********************************************************** The following function is used to set the status bits of a new-style record. */ UNIV_INLINE @@ -756,6 +756,25 @@ rec_offs_set_n_alloc( offsets[0] = n_alloc; } +/************************************************************** +The following function returns the number of fields in a record. */ +UNIV_INLINE +ulint +rec_offs_n_fields( +/*===============*/ + /* out: number of fields */ + const ulint* offsets)/* in: array returned by rec_get_offsets() */ +{ + ulint n_fields; + ut_ad(offsets); + n_fields = offsets[1]; + ut_ad(n_fields > 0); + ut_ad(n_fields <= REC_MAX_N_FIELDS); + ut_ad(n_fields + REC_OFFS_HEADER_SIZE + <= rec_offs_get_n_alloc(offsets)); + return(n_fields); +} + /**************************************************************** Validates offsets returned by rec_get_offsets(). */ UNIV_INLINE @@ -1202,25 +1221,6 @@ rec_get_data_size_old( return(rec_get_field_start_offs(rec, rec_get_n_fields_old(rec))); } -/************************************************************** -The following function returns the number of fields in a record. */ -UNIV_INLINE -ulint -rec_offs_n_fields( -/*===============*/ - /* out: number of fields */ - const ulint* offsets)/* in: array returned by rec_get_offsets() */ -{ - ulint n_fields; - ut_ad(offsets); - n_fields = offsets[1]; - ut_ad(n_fields > 0); - ut_ad(n_fields <= REC_MAX_N_FIELDS); - ut_ad(n_fields + REC_OFFS_HEADER_SIZE - <= rec_offs_get_n_alloc(offsets)); - return(n_fields); -} - /************************************************************** The following function sets the number of fields in offsets. */ UNIV_INLINE From 83e58bc0eaa2856e54b08c84417192b07db68a86 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 27 Dec 2004 23:17:09 +0100 Subject: [PATCH 12/13] strings/decimal.c test_d2b2d("123.4", 10, 2, "123.40"); strings/decimal.c: test_d2b2d("123.4", 10, 2, "123.40"); --- strings/decimal.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/strings/decimal.c b/strings/decimal.c index c691bcb62fc..42e65c26ad1 100644 --- a/strings/decimal.c +++ b/strings/decimal.c @@ -14,6 +14,8 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#line __LINE__ "decimal.c" + /* ======================================================================= NOTE: this library implements SQL standard "exact numeric" type @@ -680,8 +682,12 @@ int decimal2bin(decimal *from, char *to, int precision, int frac) /* frac1x part */ if (frac1x) { - int i=dig2bytes[frac1x]; - dec1 x=(*buf1 / powers10[DIG_PER_DEC1 - frac1x]) ^ mask; + dec1 x; + int i=dig2bytes[frac1x], + lim=(frac1 < frac0 ? DIG_PER_DEC1 : frac0x); + while (frac1x < lim && dig2bytes[frac1x] == i) + frac1x++; + x=(*buf1 / powers10[DIG_PER_DEC1 - frac1x]) ^ mask; switch (i) { case 1: mi_int1store(to, x); break; @@ -2087,6 +2093,8 @@ main() test_d2b2d("-.000000012345000098765", 30, 20,"-.00000001234500009876"); test_d2b2d("1234500009876.5", 30, 5,"1234500009876.50000"); test_d2b2d("111111111.11", 10, 2,"11111111.11"); + full=1; + test_d2b2d("123.4", 10, 2, "123.40"); printf("==== decimal_cmp ====\n"); test_dc("12","13",-1); From 7ce58befd33d78e1c11872fcbc696a9cfad698c2 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 28 Dec 2004 01:34:52 +0200 Subject: [PATCH 13/13] InnoDB: Fix some bugs in the new record format. (Bug #7493) innobase/btr/btr0btr.c: Remove parameter n_fields from cmp_rec_rec() innobase/btr/btr0cur.c: Remove parameter n_fields from cmp_rec_rec_with_match() innobase/btr/btr0pcur.c: Remove parameter n_fields from cmp_rec_rec() innobase/include/rem0cmp.h: Remove parameter n from cmp_rec_rec_with_match() and cmp_rec_rec() innobase/include/rem0cmp.ic: Remove parameter n from cmp_rec_rec() innobase/include/rem0rec.ic: Correct the implementation of rec_offs_nth_size() (Bug #7493) innobase/page/page0page.c: Remove parameter n_fields from cmp_rec_rec() innobase/rem/rem0cmp.c: Remove parameter n from cmp_rec_rec_with_match() innobase/rem/rem0rec.c: rec_get_offsets(): Pass the number of allocated elements to rec_offs_set_n_alloc() instead of the number of allocated bytes, so that debugging assertions are more likely to detect out-of-bounds errors. --- innobase/btr/btr0btr.c | 5 ++--- innobase/btr/btr0cur.c | 2 +- innobase/btr/btr0pcur.c | 4 +--- innobase/include/rem0cmp.h | 8 -------- innobase/include/rem0cmp.ic | 3 +-- innobase/include/rem0rec.ic | 3 ++- innobase/page/page0page.c | 3 +-- innobase/rem/rem0cmp.c | 14 ++------------ innobase/rem/rem0rec.c | 6 +++--- 9 files changed, 13 insertions(+), 35 deletions(-) diff --git a/innobase/btr/btr0btr.c b/innobase/btr/btr0btr.c index 9da422e927f..4fb930da50f 100644 --- a/innobase/btr/btr0btr.c +++ b/innobase/btr/btr0btr.c @@ -2735,9 +2735,8 @@ loop: offsets, ULINT_UNDEFINED, &heap); offsets2 = rec_get_offsets(right_rec, index, offsets2, ULINT_UNDEFINED, &heap); - if (cmp_rec_rec(rec, right_rec, offsets, offsets2, - dict_index_get_n_fields(index), - index) >= 0) { + if (cmp_rec_rec(rec, right_rec, offsets, offsets2, index) + >= 0) { btr_validate_report2(index, level, page, right_page); diff --git a/innobase/btr/btr0cur.c b/innobase/btr/btr0cur.c index a3083e0e545..4c2a501a08a 100644 --- a/innobase/btr/btr0cur.c +++ b/innobase/btr/btr0cur.c @@ -2848,7 +2848,7 @@ btr_estimate_number_of_different_key_vals( cmp_rec_rec_with_match(rec, next_rec, offsets1, offsets2, - index, n_cols, &matched_fields, + index, &matched_fields, &matched_bytes); for (j = matched_fields + 1; j <= n_cols; j++) { diff --git a/innobase/btr/btr0pcur.c b/innobase/btr/btr0pcur.c index ad7613e0655..ceaa4f41a18 100644 --- a/innobase/btr/btr0pcur.c +++ b/innobase/btr/btr0pcur.c @@ -268,9 +268,7 @@ btr_pcur_restore_position( cursor->old_n_fields, &heap); ut_ad(cmp_rec_rec(cursor->old_rec, - rec, offsets1, offsets2, - cursor->old_n_fields, - index) == 0); + rec, offsets1, offsets2, index) == 0); mem_heap_free(heap); #endif /* UNIV_DEBUG */ return(TRUE); diff --git a/innobase/include/rem0cmp.h b/innobase/include/rem0cmp.h index 77a5a42c2d5..1b1ee26b809 100644 --- a/innobase/include/rem0cmp.h +++ b/innobase/include/rem0cmp.h @@ -137,10 +137,6 @@ cmp_rec_rec_with_match( const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ dict_index_t* index, /* in: data dictionary index */ - ulint n, /* in: number of fields to compare, - or ULINT_UNDEFINED if both records - contain all fields, and all fields - should be compared */ ulint* matched_fields, /* in/out: number of already completely matched fields; when the function returns, contains the value the for current @@ -163,10 +159,6 @@ cmp_rec_rec( rec_t* rec2, /* in: physical record */ const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ - ulint n, /* in: number of fields to compare, - or ULINT_UNDEFINED if both records - contain all fields, and all fields - should be compared */ dict_index_t* index); /* in: data dictionary index */ diff --git a/innobase/include/rem0cmp.ic b/innobase/include/rem0cmp.ic index d4c30f25f03..b86534e0a6a 100644 --- a/innobase/include/rem0cmp.ic +++ b/innobase/include/rem0cmp.ic @@ -59,12 +59,11 @@ cmp_rec_rec( rec_t* rec2, /* in: physical record */ const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ - ulint n, /* in: number of fields to compare */ dict_index_t* index) /* in: data dictionary index */ { ulint match_f = 0; ulint match_b = 0; - return(cmp_rec_rec_with_match(rec1, rec2, offsets1, offsets2, index, n, + return(cmp_rec_rec_with_match(rec1, rec2, offsets1, offsets2, index, &match_f, &match_b)); } diff --git a/innobase/include/rem0rec.ic b/innobase/include/rem0rec.ic index 2a1f8c8a0db..b9a79c259a4 100644 --- a/innobase/include/rem0rec.ic +++ b/innobase/include/rem0rec.ic @@ -946,7 +946,8 @@ rec_offs_nth_size( { ut_ad(rec_offs_validate(NULL, NULL, offsets)); ut_ad(n < rec_offs_n_fields(offsets)); - return(rec_offs_base(offsets)[1 + n] & REC_OFFS_MASK); + return((rec_offs_base(offsets)[1 + n] - rec_offs_base(offsets)[n]) + & REC_OFFS_MASK); } /********************************************************** diff --git a/innobase/page/page0page.c b/innobase/page/page0page.c index 67c7bd936d1..20bbfba7a50 100644 --- a/innobase/page/page0page.c +++ b/innobase/page/page0page.c @@ -1749,8 +1749,7 @@ page_validate( /* Check that the records are in the ascending order */ if ((count >= 2) && (!page_cur_is_after_last(&cur))) { if (!(1 == cmp_rec_rec(rec, old_rec, - offsets, old_offsets, - ULINT_UNDEFINED, index))) { + offsets, old_offsets, index))) { fprintf(stderr, "InnoDB: Records in wrong order on page %lu", (ulong) buf_frame_get_page_no(page)); diff --git a/innobase/rem/rem0cmp.c b/innobase/rem/rem0cmp.c index 974fc7a24d0..6473d356ba8 100644 --- a/innobase/rem/rem0cmp.c +++ b/innobase/rem/rem0cmp.c @@ -728,10 +728,6 @@ cmp_rec_rec_with_match( const ulint* offsets1,/* in: rec_get_offsets(rec1, index) */ const ulint* offsets2,/* in: rec_get_offsets(rec2, index) */ dict_index_t* index, /* in: data dictionary index */ - ulint n, /* in: number of fields to compare, - or ULINT_UNDEFINED if both records - contain all fields, and all fields - should be compared */ ulint* matched_fields, /* in/out: number of already completely matched fields; when the function returns, contains the value the for current @@ -765,14 +761,8 @@ cmp_rec_rec_with_match( ut_ad(rec_offs_comp(offsets1) == rec_offs_comp(offsets2)); comp = rec_offs_comp(offsets1); - if (n == ULINT_UNDEFINED) { - rec1_n_fields = rec_offs_n_fields(offsets1); - rec2_n_fields = rec_offs_n_fields(offsets2); - } else { - ut_ad(n <= rec_offs_n_fields(offsets1)); - ut_ad(n <= rec_offs_n_fields(offsets2)); - rec1_n_fields = rec2_n_fields = n; - } + rec1_n_fields = rec_offs_n_fields(offsets1); + rec2_n_fields = rec_offs_n_fields(offsets2); cur_field = *matched_fields; cur_bytes = *matched_bytes; diff --git a/innobase/rem/rem0rec.c b/innobase/rem/rem0rec.c index 74876ad9402..90cbffe7a9e 100644 --- a/innobase/rem/rem0rec.c +++ b/innobase/rem/rem0rec.c @@ -333,14 +333,14 @@ rec_get_offsets_func( n = n_fields; } - size = (n + (1 + REC_OFFS_HEADER_SIZE)) * sizeof(ulint); + size = n + (1 + REC_OFFS_HEADER_SIZE); if (!offsets || rec_offs_get_n_alloc(offsets) < size) { if (!*heap) { - *heap = mem_heap_create_func(size, + *heap = mem_heap_create_func(size * sizeof(ulint), NULL, MEM_HEAP_DYNAMIC, file, line); } - offsets = mem_heap_alloc(*heap, size); + offsets = mem_heap_alloc(*heap, size * sizeof(ulint)); rec_offs_set_n_alloc(offsets, size); }