From 00ac598a44fb38b888ccec14befc38348ddeae53 Mon Sep 17 00:00:00 2001 From: "Bernt M. Johnsen" Date: Wed, 25 Feb 2009 16:53:49 +0100 Subject: [PATCH 1/5] Prepared for push (BUG#43110) --- mysql-test/t/ps.test | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/mysql-test/t/ps.test b/mysql-test/t/ps.test index 6b20fd14394..db5994d434b 100644 --- a/mysql-test/t/ps.test +++ b/mysql-test/t/ps.test @@ -946,8 +946,13 @@ set global max_prepared_stmt_count=3; select @@max_prepared_stmt_count; show status like 'prepared_stmt_count'; prepare stmt from "select 1"; + connect (con1,localhost,root,,); + +# Switch to connection con1 connection con1; +let $con1_id=`SELECT CONNECTION_ID()`; + prepare stmt from "select 2"; prepare stmt1 from "select 3"; --error ER_MAX_PREPARED_STMT_COUNT_REACHED @@ -957,18 +962,17 @@ connection default; prepare stmt2 from "select 4"; select @@max_prepared_stmt_count; show status like 'prepared_stmt_count'; + +# Disconnect connection con1 and switch to default connection disconnect con1; connection default; -# Wait for the connection to die: deal with a possible race + +# Wait for the connection con1 to die +let $wait_condition=SELECT COUNT(*)=0 FROM information_schema.processlist WHERE id=$con1_id; +--source include/wait_condition.inc + deallocate prepare stmt; -let $query= select variable_value from information_schema.global_status - where variable_name = 'prepared_stmt_count'; -let $count= `$query`; -if ($count) -{ ---sleep 1 - let $count= `$query`; -} + select @@max_prepared_stmt_count; show status like 'prepared_stmt_count'; # From c9e1884cd6e70d89f0a0c2bce1b80f84b913f59a Mon Sep 17 00:00:00 2001 From: Ramil Kalimullin Date: Thu, 26 Feb 2009 12:34:15 +0400 Subject: [PATCH 2/5] Fix for bug#19829:make test Failed in mysql_client_test *with --with-charset=utf8* Problem: wrong LONG TEXT field length is sent to a client when multibyte server character set used. Fix: always limit field length sent to a client to 2^32, as we store it in 4 byte slot. Note: mysql_client_test changed accordingly. sql/protocol.cc: Fix for bug#19829:make test Failed in mysql_client_test *with --with-charset=utf8* - limit field length sent to client to UINT_MAX32 as it may exceeds 32 bit slot for LONG TEXT fields if thd_charset->mbmaxlen > 1. tests/mysql_client_test.c: Fix for bug#19829:make test Failed in mysql_client_test *with --with-charset=utf8* - checking field members have in mind that field length is limited to UINT_MAX32. --- sql/protocol.cc | 22 ++++++++++++++++------ tests/mysql_client_test.c | 15 +++++++++------ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/sql/protocol.cc b/sql/protocol.cc index ff58d96f59b..2309bac88a9 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -616,7 +616,8 @@ bool Protocol::send_fields(List *list, uint flags) else { /* With conversion */ - uint max_char_len; + ulonglong max_length; + uint32 field_length; int2store(pos, thd_charset->number); /* For TEXT/BLOB columns, field_length describes the maximum data @@ -627,12 +628,21 @@ bool Protocol::send_fields(List *list, uint flags) char_count * mbmaxlen, where character count is taken from the definition of the column. In other words, the maximum number of characters here is limited by the column definition. + + When one has a LONG TEXT column with a single-byte + character set, and the connection character set is multi-byte, the + client may get fields longer than UINT_MAX32, due to + -> conversion. + In that case column max length does not fit into the 4 bytes + reserved for it in the protocol. */ - max_char_len= (field.type >= (int) MYSQL_TYPE_TINY_BLOB && - field.type <= (int) MYSQL_TYPE_BLOB) ? - field.length / item->collation.collation->mbminlen : - field.length / item->collation.collation->mbmaxlen; - int4store(pos+2, max_char_len * thd_charset->mbmaxlen); + max_length= (field.type >= MYSQL_TYPE_TINY_BLOB && + field.type <= MYSQL_TYPE_BLOB) ? + field.length / item->collation.collation->mbminlen : + field.length / item->collation.collation->mbmaxlen; + max_length*= thd_charset->mbmaxlen; + field_length= (max_length > UINT_MAX32) ? UINT_MAX32 : max_length; + int4store(pos + 2, field_length); } pos[6]= field.type; int2store(pos+7,field.flags); diff --git a/tests/mysql_client_test.c b/tests/mysql_client_test.c index f848e93a5c6..7df84c600c9 100644 --- a/tests/mysql_client_test.c +++ b/tests/mysql_client_test.c @@ -714,6 +714,7 @@ static void do_verify_prepare_field(MYSQL_RES *result, { MYSQL_FIELD *field; CHARSET_INFO *cs; + ulonglong expected_field_length; if (!(field= mysql_fetch_field_direct(result, no))) { @@ -722,6 +723,8 @@ static void do_verify_prepare_field(MYSQL_RES *result, } cs= get_charset(field->charsetnr, 0); DIE_UNLESS(cs); + if ((expected_field_length= length * cs->mbmaxlen) > UINT_MAX32) + expected_field_length= UINT_MAX32; if (!opt_silent) { fprintf(stdout, "\n field[%d]:", no); @@ -736,8 +739,8 @@ static void do_verify_prepare_field(MYSQL_RES *result, fprintf(stdout, "\n org_table:`%s`\t(expected: `%s`)", field->org_table, org_table); fprintf(stdout, "\n database :`%s`\t(expected: `%s`)", field->db, db); - fprintf(stdout, "\n length :`%lu`\t(expected: `%lu`)", - field->length, length * cs->mbmaxlen); + fprintf(stdout, "\n length :`%lu`\t(expected: `%llu`)", + field->length, expected_field_length); fprintf(stdout, "\n maxlength:`%ld`", field->max_length); fprintf(stdout, "\n charsetnr:`%d`", field->charsetnr); fprintf(stdout, "\n default :`%s`\t(expected: `%s`)", @@ -773,11 +776,11 @@ static void do_verify_prepare_field(MYSQL_RES *result, as utf8. Field length is calculated as number of characters * maximum number of bytes a character can occupy. */ - if (length && field->length != length * cs->mbmaxlen) + if (length && (field->length != expected_field_length)) { - fprintf(stderr, "Expected field length: %d, got length: %d\n", - (int) (length * cs->mbmaxlen), (int) field->length); - DIE_UNLESS(field->length == length * cs->mbmaxlen); + fprintf(stderr, "Expected field length: %llu, got length: %lu\n", + expected_field_length, field->length); + DIE_UNLESS(field->length == expected_field_length); } if (def) DIE_UNLESS(strcmp(field->def, def) == 0); From a9d707037ab527564bb84885e0af69a2bb793219 Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Thu, 26 Feb 2009 19:00:44 +0200 Subject: [PATCH 3/5] Bug #41354: Access control is bypassed when all columns of a view are selected by * wildcard Backported a part of the fix for 36086 to 5.0 mysql-test/r/view_grant.result: Bug #41354: test case mysql-test/t/view_grant.test: Bug #41354: test case sql/sql_acl.cc: Bug #41354: return table error when no access and * sql/sql_base.cc: Bug #41354: backported the check in bug 36086 to 5.0 --- mysql-test/r/view_grant.result | 26 ++++++++++++++++++++++ mysql-test/t/view_grant.test | 40 ++++++++++++++++++++++++++++++++++ sql/sql_acl.cc | 28 +++++++++++++++++++----- sql/sql_base.cc | 2 +- 4 files changed, 89 insertions(+), 7 deletions(-) diff --git a/mysql-test/r/view_grant.result b/mysql-test/r/view_grant.result index 2f8462045ca..1df8ed335a7 100644 --- a/mysql-test/r/view_grant.result +++ b/mysql-test/r/view_grant.result @@ -919,4 +919,30 @@ c4 DROP DATABASE mysqltest1; DROP DATABASE mysqltest2; DROP USER mysqltest_u1@localhost; +CREATE DATABASE db1; +USE db1; +CREATE TABLE t1(f1 INT, f2 INT); +CREATE VIEW v1 AS SELECT f1, f2 FROM t1; +GRANT SELECT (f1) ON t1 TO foo; +GRANT SELECT (f1) ON v1 TO foo; +USE db1; +SELECT f1 FROM t1; +f1 +SELECT f2 FROM t1; +ERROR 42000: SELECT command denied to user 'foo'@'localhost' for column 'f2' in table 't1' +SELECT * FROM t1; +ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 't1' +SELECT f1 FROM v1; +f1 +SELECT f2 FROM v1; +ERROR 42000: SELECT command denied to user 'foo'@'localhost' for column 'f2' in table 'v1' +SELECT * FROM v1; +ERROR 42000: SELECT command denied to user 'foo'@'localhost' for table 'v1' +USE test; +REVOKE SELECT (f1) ON db1.t1 FROM foo; +REVOKE SELECT (f1) ON db1.v1 FROM foo; +DROP USER foo; +DROP VIEW db1.v1; +DROP TABLE db1.t1; +DROP DATABASE db1; End of 5.0 tests. diff --git a/mysql-test/t/view_grant.test b/mysql-test/t/view_grant.test index be9daacec4f..c8b31f711b5 100644 --- a/mysql-test/t/view_grant.test +++ b/mysql-test/t/view_grant.test @@ -1185,4 +1185,44 @@ DROP DATABASE mysqltest1; DROP DATABASE mysqltest2; DROP USER mysqltest_u1@localhost; + +# +# Bug #41354: Access control is bypassed when all columns of a view are +# selected by * wildcard + +CREATE DATABASE db1; +USE db1; +CREATE TABLE t1(f1 INT, f2 INT); +CREATE VIEW v1 AS SELECT f1, f2 FROM t1; + +GRANT SELECT (f1) ON t1 TO foo; +GRANT SELECT (f1) ON v1 TO foo; + +connect (addconfoo, localhost, foo,,); +connection addconfoo; +USE db1; + + +SELECT f1 FROM t1; +--error ER_COLUMNACCESS_DENIED_ERROR +SELECT f2 FROM t1; +--error ER_TABLEACCESS_DENIED_ERROR +SELECT * FROM t1; + +SELECT f1 FROM v1; +--error ER_COLUMNACCESS_DENIED_ERROR +SELECT f2 FROM v1; +--error ER_TABLEACCESS_DENIED_ERROR +SELECT * FROM v1; + +connection default; +USE test; +disconnect addconfoo; +REVOKE SELECT (f1) ON db1.t1 FROM foo; +REVOKE SELECT (f1) ON db1.v1 FROM foo; +DROP USER foo; +DROP VIEW db1.v1; +DROP TABLE db1.t1; +DROP DATABASE db1; + --echo End of 5.0 tests. diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc index 22135d376fe..c59c42d512a 100644 --- a/sql/sql_acl.cc +++ b/sql/sql_acl.cc @@ -3866,6 +3866,11 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, Security_context *sctx= thd->security_ctx; ulong want_access= want_access_arg; const char *table_name= NULL; + /* + Flag that gets set if privilege checking has to be performed on column + level. + */ + bool using_column_privileges= FALSE; if (grant_option) { @@ -3909,6 +3914,8 @@ bool check_grant_all_columns(THD *thd, ulong want_access_arg, GRANT_COLUMN *grant_column= column_hash_search(grant_table, field_name, (uint) strlen(field_name)); + if (grant_column) + using_column_privileges= TRUE; if (!grant_column || (~grant_column->rights & want_access)) goto err; } @@ -3924,12 +3931,21 @@ err: char command[128]; get_privilege_desc(command, sizeof(command), want_access); - my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), - command, - sctx->priv_user, - sctx->host_or_ip, - fields->name(), - table_name); + /* + Do not give an error message listing a column name unless the user has + privilege to see all columns. + */ + if (using_column_privileges) + my_error(ER_TABLEACCESS_DENIED_ERROR, MYF(0), + command, sctx->priv_user, + sctx->host_or_ip, table_name); + else + my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), + command, + sctx->priv_user, + sctx->host_or_ip, + fields->name(), + table_name); return 1; } diff --git a/sql/sql_base.cc b/sql/sql_base.cc index 881c6a421e8..781bbc0a553 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -5479,7 +5479,7 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name, #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Ensure that we have access rights to all fields to be inserted. */ - if (!((table && (table->grant.privilege & SELECT_ACL) || + if (!((table && !tables->view && (table->grant.privilege & SELECT_ACL) || tables->view && (tables->grant.privilege & SELECT_ACL))) && !any_privileges) { From 2cca1991bd25fe9a085ccb4d66ed39d1e054d495 Mon Sep 17 00:00:00 2001 From: "Bernt M. Johnsen" Date: Thu, 26 Feb 2009 18:17:06 +0100 Subject: [PATCH 4/5] Prepared for push (BUG#42567) --- mysql-test/r/group_by.result | 12 ++++++++++++ mysql-test/t/func_group.test | 2 ++ mysql-test/t/group_by.test | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/mysql-test/r/group_by.result b/mysql-test/r/group_by.result index 48f97aeb428..742d4b90807 100644 --- a/mysql-test/r/group_by.result +++ b/mysql-test/r/group_by.result @@ -1691,3 +1691,15 @@ FROM t1; ERROR 21000: Subquery returns more than 1 row DROP TABLE t1; SET @@sql_mode = @old_sql_mode; +SET @old_sql_mode = @@sql_mode; +SET @@sql_mode='ONLY_FULL_GROUP_BY'; +CREATE TABLE t1(i INT); +INSERT INTO t1 VALUES (1), (10); +SELECT COUNT(i) FROM t1; +COUNT(i) +2 +SELECT COUNT(i) FROM t1 WHERE i > 1; +COUNT(i) +1 +DROP TABLE t1; +SET @@sql_mode = @old_sql_mode; diff --git a/mysql-test/t/func_group.test b/mysql-test/t/func_group.test index be4e9c32686..b0a3d0feb79 100644 --- a/mysql-test/t/func_group.test +++ b/mysql-test/t/func_group.test @@ -18,6 +18,8 @@ insert into t1 values (3,5,"C"); insert into t1 values (3,6,"D"); # Test of MySQL field extension with and without matching records. +#### Note: The two following statements may fail if the execution plan +#### or optimizer is changed. The result for column c is undefined. select a,c,sum(a) from t1 group by a; select a,c,sum(a) from t1 where a > 10 group by a; select sum(a) from t1 where a > 10; diff --git a/mysql-test/t/group_by.test b/mysql-test/t/group_by.test index e3cf3ca856d..5b96213034a 100644 --- a/mysql-test/t/group_by.test +++ b/mysql-test/t/group_by.test @@ -1139,4 +1139,22 @@ DROP TABLE t1; SET @@sql_mode = @old_sql_mode; +# +# Bug#42567 Invalid GROUP BY error +# + +# Setup of the subtest +SET @old_sql_mode = @@sql_mode; +SET @@sql_mode='ONLY_FULL_GROUP_BY'; + +CREATE TABLE t1(i INT); +INSERT INTO t1 VALUES (1), (10); + +# The actual test +SELECT COUNT(i) FROM t1; +SELECT COUNT(i) FROM t1 WHERE i > 1; + +# Cleanup of subtest +DROP TABLE t1; +SET @@sql_mode = @old_sql_mode; From ee772168032cde8f6dc7abf8155f573ccd96afeb Mon Sep 17 00:00:00 2001 From: Georgi Kodinov Date: Fri, 27 Feb 2009 09:41:39 +0200 Subject: [PATCH 5/5] addendum to the fix for bug #41354: fixed the error returned by SELECT * --- mysql-test/r/grant2.result | 2 +- mysql-test/t/grant2.test | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/mysql-test/r/grant2.result b/mysql-test/r/grant2.result index 95748c89103..698e602e2e6 100644 --- a/mysql-test/r/grant2.result +++ b/mysql-test/r/grant2.result @@ -433,7 +433,7 @@ USE db1; SELECT c FROM t2; ERROR 42000: SELECT command denied to user 'mysqltest1'@'localhost' for column 'c' in table 't2' SELECT * FROM t2; -ERROR 42000: SELECT command denied to user 'mysqltest1'@'localhost' for column 'c' in table 't2' +ERROR 42000: SELECT command denied to user 'mysqltest1'@'localhost' for table 't2' SELECT * FROM t1 JOIN t2 USING (b); ERROR 42000: SELECT command denied to user 'mysqltest1'@'localhost' for column 'c' in table 't2' USE test; diff --git a/mysql-test/t/grant2.test b/mysql-test/t/grant2.test index 8f83c365170..2393bb1c6d8 100644 --- a/mysql-test/t/grant2.test +++ b/mysql-test/t/grant2.test @@ -615,7 +615,7 @@ connection conn1; USE db1; --error ER_COLUMNACCESS_DENIED_ERROR SELECT c FROM t2; ---error ER_COLUMNACCESS_DENIED_ERROR +--error ER_TABLEACCESS_DENIED_ERROR SELECT * FROM t2; --error ER_COLUMNACCESS_DENIED_ERROR SELECT * FROM t1 JOIN t2 USING (b);