Commit f174d5a2 authored by unknown's avatar unknown
Browse files

Manual merge of patch implementing WL#1339 with main tree.


client/mysqltest.c:
  Auto merged
mysql-test/r/system_mysql_db.result:
  Auto merged
sql/lex.h:
  Auto merged
sql/set_var.cc:
  Auto merged
sql/sql_acl.cc:
  Auto merged
sql/sql_yacc.yy:
  Auto merged
sql/structs.h:
  Auto merged
mysql-test/r/grant.result:
  Manual merge
scripts/mysql_create_system_tables.sh:
  Manual merge
scripts/mysql_fix_privilege_tables.sql:
  Manual merge
sql/mysql_priv.h:
  Manual merge
sql/mysqld.cc:
  Manual merge
sql/sql_parse.cc:
  Manual merge
parents 44728324 05b709fc
Loading
Loading
Loading
Loading
+307 −107
Original line number Diff line number Diff line
@@ -437,6 +437,10 @@ my_bool mysql_rpl_probe(MYSQL *mysql __attribute__((unused))) { return 1; }
#endif
static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
				      int len);
static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val);
static int normal_handle_error(const char *query, struct st_query *q,
                               MYSQL *mysql, DYNAMIC_STRING *ds);
static int normal_handle_no_error(struct st_query *q);

static void do_eval(DYNAMIC_STRING* query_eval, const char* query)
{
@@ -951,7 +955,7 @@ static void do_exec(struct st_query* q)
      ds= &ds_res;

    while (fgets(buf, sizeof(buf), res_file))
      replace_dynstr_append_mem(ds, buf, strlen(buf));
      replace_dynstr_append(ds, buf);
  }
  error= pclose(res_file);

@@ -1613,6 +1617,29 @@ void init_manager()
}
#endif


/*
  Connect to a server doing several retries if needed.

  SYNOPSIS
    safe_connect()
      con               - connection structure to be used
      host, user, pass, - connection parameters
      db, port, sock

  NOTE
    This function will try to connect to the given server MAX_CON_TRIES
    times and sleep CON_RETRY_SLEEP seconds between attempts before
    finally giving up. This helps in situation when the client starts
    before the server (which happens sometimes).
    It will ignore any errors during these retries. One should use
    connect_n_handle_errors() if he expects a connection error and wants
    handle as if it was an error from a usual statement.

  RETURN VALUE
    0 - success, non-0 - failure
*/

int safe_connect(MYSQL* con, const char* host, const char* user,
		 const char* pass,
		 const char* db, int port, const char* sock)
@@ -1634,6 +1661,114 @@ int safe_connect(MYSQL* con, const char* host, const char* user,
}


/*
  Connect to a server and handle connection errors in case when they occur.

  SYNOPSIS
    connect_n_handle_errors()
      q                 - context of connect "query" (command)
      con               - connection structure to be used
      host, user, pass, - connection parameters
      db, port, sock
      create_conn       - out parameter, set to zero if connection was
                          not established and is not touched otherwise

  DESCRIPTION
    This function will try to establish a connection to server and handle
    possible errors in the same manner as if "connect" was usual SQL-statement
    (If error is expected it will ignore it once it occurs and log the
    "statement" to the query log).
    Unlike safe_connect() it won't do several attempts.

  RETURN VALUE
    0 - success, non-0 - failure
*/

int connect_n_handle_errors(struct st_query *q, MYSQL* con, const char* host,
                            const char* user, const char* pass,
                            const char* db, int port, const char* sock,
                            int* create_conn)
{
  DYNAMIC_STRING ds_tmp, *ds;
  int error= 0;

  /*
    Altough we ignore --require or --result before connect() command we still
    need to handle record_file because of "@result_file sql-command" syntax.
  */
  if (q->record_file[0])
  {
    init_dynamic_string(&ds_tmp, "", 16384, 65536);
    ds= &ds_tmp;
  }
  else
    ds= &ds_res;

  if (!disable_query_log)
  {
    /*
      It is nice to have connect() statement logged in result file
      in this case.
      QQ: Should we do this only if we are expecting an error ?
    */
    char port_buff[22]; /* This should be enough for any int */
    char *port_end;
    dynstr_append_mem(ds, "connect(", 8);
    replace_dynstr_append(ds, host);
    dynstr_append_mem(ds, ",", 1);
    replace_dynstr_append(ds, user);
    dynstr_append_mem(ds, ",", 1);
    replace_dynstr_append(ds, pass);
    dynstr_append_mem(ds, ",", 1);
    if (db)
      replace_dynstr_append(ds, db);
    dynstr_append_mem(ds, ",", 1);
    port_end= int10_to_str(port, port_buff, 10);
    replace_dynstr_append_mem(ds, port_buff, port_end - port_buff);
    dynstr_append_mem(ds, ",", 1);
    if (sock)
      replace_dynstr_append(ds, sock);
    dynstr_append_mem(ds, ")", 1);
    dynstr_append_mem(ds, delimiter, delimiter_length);
    dynstr_append_mem(ds, "\n", 1);
  }
  if (!mysql_real_connect(con, host, user, pass, db, port, sock ? sock: 0,
                          CLIENT_MULTI_STATEMENTS))
  {
    error= normal_handle_error("connect", q, con, ds);
    *create_conn= 0;
    goto err;
  }
  else if (normal_handle_no_error(q))
  {
    /*
      Fail if there was no error but we expected it.
      We also don't want to have connection in this case.
    */
    mysql_close(con);
    *create_conn= 0;
    error= 1;
    goto err;
  }

  if (record)
  {
    if (!q->record_file[0] && !result_file)
      die("At line %u: Missing result file", start_lineno);
    if (!result_file)
      str_to_file(q->record_file, ds->str, ds->length);
  }
  else if (q->record_file[0])
    error|= check_result(ds, q->record_file, q->require_file);

err:
  free_replace();
  if (ds == &ds_tmp)
    dynstr_free(&ds_tmp);
  return error;
}


int do_connect(struct st_query* q)
{
  char* con_name, *con_user,*con_pass, *con_host, *con_port_str,
@@ -1642,6 +1777,8 @@ int do_connect(struct st_query* q)
  char buff[FN_REFLEN];
  int con_port;
  int free_con_sock = 0;
  int error= 0;
  int create_conn= 1;

  DBUG_ENTER("do_connect");
  DBUG_PRINT("enter",("connect: %s",p));
@@ -1706,18 +1843,28 @@ int do_connect(struct st_query* q)
  /* Special database to allow one to connect without a database name */
  if (con_db && !strcmp(con_db,"*NO-ONE*"))
    con_db=0;
  if ((safe_connect(&next_con->mysql, con_host,
		    con_user, con_pass,

  if (q->abort_on_error)
  {
    if ((safe_connect(&next_con->mysql, con_host, con_user, con_pass,
		      con_db, con_port, con_sock ? con_sock: 0)))
      die("Could not open connection '%s': %s", con_name,
          mysql_error(&next_con->mysql));
  }
  else
    error= connect_n_handle_errors(q, &next_con->mysql, con_host, con_user,
                                   con_pass, con_db, con_port, con_sock,
                                   &create_conn);

  if (create_conn)
  {
    if (!(next_con->name= my_strdup(con_name, MYF(MY_WME))))
      die(NullS);
    cur_con= next_con++;
  }
  if (free_con_sock)
    my_free(con_sock, MYF(MY_WME));
  DBUG_RETURN(0);
  DBUG_RETURN(error);
}


@@ -2356,6 +2503,13 @@ static void replace_dynstr_append_mem(DYNAMIC_STRING *ds, const char *val,
  dynstr_append_mem(ds, val, len);
}

/* Append zero-terminated string to ds, with optional replace */

static void replace_dynstr_append(DYNAMIC_STRING *ds, const char *val)
{
  replace_dynstr_append_mem(ds, val, strlen(val));
}


/*
  Append all results to the dynamic string separated with '\t'
@@ -2502,92 +2656,13 @@ static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags)
	 (!(last_result= res= mysql_store_result(mysql)) &&
	  mysql_field_count(mysql)))
    {
      if (q->require_file)
      {
	abort_not_supported_test();
      }
      if (q->abort_on_error)
	die("At line %u: query '%s' failed: %d: %s", start_lineno, query,
	    mysql_errno(mysql), mysql_error(mysql));
      else
      {
	for (i=0 ; (uint) i < q->expected_errors ; i++)
	{
          if (((q->expected_errno[i].type == ERR_ERRNO) &&
               (q->expected_errno[i].code.errnum == mysql_errno(mysql))) ||
              ((q->expected_errno[i].type == ERR_SQLSTATE) &&
               (strcmp(q->expected_errno[i].code.sqlstate,mysql_sqlstate(mysql)) == 0)))
	  {
	    if (i == 0 && q->expected_errors == 1)
	    {
	      /* Only log error if there is one possible error */
	      dynstr_append_mem(ds,"ERROR ",6);
	      replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
					strlen(mysql_sqlstate(mysql)));
	      dynstr_append_mem(ds, ": ", 2);
	      replace_dynstr_append_mem(ds,mysql_error(mysql),
					strlen(mysql_error(mysql)));
	      dynstr_append_mem(ds,"\n",1);
	    }
	    /* Don't log error if we may not get an error */
            else if (q->expected_errno[0].type == ERR_SQLSTATE ||
                     (q->expected_errno[0].type == ERR_ERRNO &&
                      q->expected_errno[0].code.errnum != 0))
	      dynstr_append(ds,"Got one of the listed errors\n");
	    goto end;				/* Ok */
	  }
	}
	DBUG_PRINT("info",("i: %d  expected_errors: %d", i,
			   q->expected_errors));
	dynstr_append_mem(ds, "ERROR ",6);
	replace_dynstr_append_mem(ds, mysql_sqlstate(mysql),
				  strlen(mysql_sqlstate(mysql)));
	dynstr_append_mem(ds,": ",2);
	replace_dynstr_append_mem(ds, mysql_error(mysql),
				  strlen(mysql_error(mysql)));
	dynstr_append_mem(ds,"\n",1);
	if (i)
	{
          if (q->expected_errno[0].type == ERR_ERRNO)
            verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
                        q->query, mysql_errno(mysql), q->expected_errno[0].code.errnum);
          else
            verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...",
                        q->query, mysql_sqlstate(mysql), q->expected_errno[0].code.sqlstate);
      if (normal_handle_error(query, q, mysql, ds))
        error= 1;
      goto end;
    }
	verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql),
		    mysql_error(mysql));
	/*
	  if we do not abort on error, failure to run the query does
	  not fail the whole test case
	*/
	goto end;
      }
      /*{
	verbose_msg("failed in mysql_store_result for query '%s' (%d)", query,
	mysql_errno(mysql));
	error = 1;
	goto end;
	}*/
    }

    if (q->expected_errno[0].type == ERR_ERRNO &&
        q->expected_errno[0].code.errnum != 0)
    if (normal_handle_no_error(q))
    {
      /* Error code we wanted was != 0, i.e. not an expected success */
      verbose_msg("query '%s' succeeded - should have failed with errno %d...",
                  q->query, q->expected_errno[0].code.errnum);
      error = 1;
      goto end;
    }
    else if (q->expected_errno[0].type == ERR_SQLSTATE &&
             strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0)
    {
      /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
      verbose_msg("query '%s' succeeded - should have failed with sqlstate %s...",
                  q->query, q->expected_errno[0].code.sqlstate);
      error= 1;
      goto end;
    }
@@ -2608,8 +2683,7 @@ static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags)
	  {
	    if (i)
	      dynstr_append_mem(ds, "\t", 1);
	    replace_dynstr_append_mem(ds, field[i].name,
				      strlen(field[i].name));
	    replace_dynstr_append(ds, field[i].name);
	  }
	  dynstr_append_mem(ds, "\n", 1);
	}
@@ -2686,6 +2760,135 @@ static int run_query_normal(MYSQL* mysql, struct st_query* q, int flags)
}


/*
  Handle errors which occurred after execution of conventional (non-prepared)
  statement.

  SYNOPSIS
    normal_handle_error()
      query - query string
      q     - query context
      mysql - connection through which query was sent to server
      ds    - dynamic string which is used for output buffer

  NOTE
    If there is an unexpected error this function will abort mysqltest
    immediately.

  RETURN VALUE
    0 - OK
    1 - Some other error was expected.
*/

static int normal_handle_error(const char *query, struct st_query *q,
                               MYSQL *mysql, DYNAMIC_STRING *ds)
{
  uint i;

  DBUG_ENTER("normal_handle_error");

  if (q->require_file)
    abort_not_supported_test();

  if (q->abort_on_error)
    die("At line %u: query '%s' failed: %d: %s", start_lineno, query,
        mysql_errno(mysql), mysql_error(mysql));
  else
  {
    for (i= 0 ; (uint) i < q->expected_errors ; i++)
    {
      if (((q->expected_errno[i].type == ERR_ERRNO) &&
            (q->expected_errno[i].code.errnum == mysql_errno(mysql))) ||
          ((q->expected_errno[i].type == ERR_SQLSTATE) &&
           (strcmp(q->expected_errno[i].code.sqlstate, mysql_sqlstate(mysql)) == 0)))
      {
        if (q->expected_errors == 1)
        {
          /* Only log error if there is one possible error */
          dynstr_append_mem(ds, "ERROR ", 6);
          replace_dynstr_append(ds, mysql_sqlstate(mysql));
          dynstr_append_mem(ds, ": ", 2);
          replace_dynstr_append(ds, mysql_error(mysql));
          dynstr_append_mem(ds,"\n",1);
        }
	/* Don't log error if we may not get an error */
        else if (q->expected_errno[0].type == ERR_SQLSTATE ||
                 (q->expected_errno[0].type == ERR_ERRNO &&
                  q->expected_errno[0].code.errnum != 0))
          dynstr_append(ds,"Got one of the listed errors\n");
        /* OK */
        DBUG_RETURN(0);
      }
    }

    DBUG_PRINT("info",("i: %d  expected_errors: %d", i, q->expected_errors));

    dynstr_append_mem(ds, "ERROR ",6);
    replace_dynstr_append(ds, mysql_sqlstate(mysql));
    dynstr_append_mem(ds, ": ", 2);
    replace_dynstr_append(ds, mysql_error(mysql));
    dynstr_append_mem(ds, "\n", 1);

    if (i)
    {
      if (q->expected_errno[0].type == ERR_ERRNO)
        verbose_msg("query '%s' failed with wrong errno %d instead of %d...",
                    q->query, mysql_errno(mysql),
                    q->expected_errno[0].code.errnum);
      else
        verbose_msg("query '%s' failed with wrong sqlstate %s instead of %s...",
                    q->query, mysql_sqlstate(mysql),
                    q->expected_errno[0].code.sqlstate);
      DBUG_RETURN(1);
    }

    /*
      If we do not abort on error, failure to run the query does not fail the
      whole test case.
    */
    verbose_msg("query '%s' failed: %d: %s", q->query, mysql_errno(mysql),
                mysql_error(mysql));
    DBUG_RETURN(0);
  }
}


/*
  Handle absence of errors after execution of convetional statement.

  SYNOPSIS
    normal_handle_error()
      q - context of query

  RETURN VALUE
    0 - OK
    1 - Some error was expected from this query.
*/

static int normal_handle_no_error(struct st_query *q)
{
  DBUG_ENTER("normal_handle_no_error");

  if (q->expected_errno[0].type == ERR_ERRNO &&
      q->expected_errno[0].code.errnum != 0)
  {
    /* Error code we wanted was != 0, i.e. not an expected success */
    verbose_msg("query '%s' succeeded - should have failed with errno %d...",
                q->query, q->expected_errno[0].code.errnum);
    DBUG_RETURN(1);
  }
  else if (q->expected_errno[0].type == ERR_SQLSTATE &&
           strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0)
  {
    /* SQLSTATE we wanted was != "00000", i.e. not an expected success */
    verbose_msg("query '%s' succeeded - should have failed with sqlstate %s...",
                q->query, q->expected_errno[0].code.sqlstate);
    DBUG_RETURN(1);
  }

  DBUG_RETURN(0);
}

/****************************************************************************\
 *  If --ps-protocol run ordinary statements using prepared statemnt C API
\****************************************************************************/
@@ -2879,8 +3082,7 @@ static int run_query_stmt(MYSQL *mysql, struct st_query *q, int flags)
      {
        if (col_idx)
          dynstr_append_mem(ds, "\t", 1);
        replace_dynstr_append_mem(ds, field[col_idx].name,
                                  strlen(field[col_idx].name));
        replace_dynstr_append(ds, field[col_idx].name);
      }
      dynstr_append_mem(ds, "\n", 1);
    }
@@ -3148,11 +3350,9 @@ static int run_query_stmt_handle_error(char *query, struct st_query *q,
        {
          /* Only log error if there is one possible error */
          dynstr_append_mem(ds,"ERROR ",6);
          replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt),
                                    strlen(mysql_stmt_sqlstate(stmt)));
          replace_dynstr_append(ds, mysql_stmt_sqlstate(stmt));
          dynstr_append_mem(ds, ": ", 2);
          replace_dynstr_append_mem(ds,mysql_stmt_error(stmt),
                                    strlen(mysql_stmt_error(stmt)));
          replace_dynstr_append(ds,mysql_stmt_error(stmt));
          dynstr_append_mem(ds,"\n",1);
        }
        /* Don't log error if we may not get an error */
@@ -3166,11 +3366,9 @@ static int run_query_stmt_handle_error(char *query, struct st_query *q,
    DBUG_PRINT("info",("i: %d  expected_errors: %d", i,
                       q->expected_errors));
    dynstr_append_mem(ds, "ERROR ",6);
    replace_dynstr_append_mem(ds, mysql_stmt_sqlstate(stmt),
                              strlen(mysql_stmt_sqlstate(stmt)));
    replace_dynstr_append(ds, mysql_stmt_sqlstate(stmt));
    dynstr_append_mem(ds,": ",2);
    replace_dynstr_append_mem(ds, mysql_stmt_error(stmt),
                              strlen(mysql_stmt_error(stmt)));
    replace_dynstr_append(ds, mysql_stmt_error(stmt));
    dynstr_append_mem(ds,"\n",1);
    if (i)
    {
@@ -3452,7 +3650,9 @@ int main(int argc, char **argv)
    {
      processed = 1;
      switch (q->type) {
      case Q_CONNECT: do_connect(q); break;
      case Q_CONNECT:
        error|= do_connect(q);
        break;
      case Q_CONNECTION: select_connection(q->first_argument); break;
      case Q_DISCONNECT:
      case Q_DIRTY_CLOSE:
+1 −0
Original line number Diff line number Diff line
@@ -102,6 +102,7 @@ user CREATE TABLE `user` (
  `max_questions` int(11) unsigned NOT NULL default '0',
  `max_updates` int(11) unsigned NOT NULL default '0',
  `max_connections` int(11) unsigned NOT NULL default '0',
  `max_user_connections` int(11) unsigned NOT NULL default '0',
  PRIMARY KEY  (`Host`,`User`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='Users and global privileges'
show create table func;
+89 −0
Original line number Diff line number Diff line
drop table if exists t1;
create table t1 (i int);
delete from mysql.user where user like 'mysqltest\_%';
delete from mysql.db where user like 'mysqltest\_%';
delete from mysql.tables_priv where user like 'mysqltest\_%';
delete from mysql.columns_priv where user like 'mysqltest\_%';
flush privileges;
grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 2;
select * from t1;
i
select * from t1;
i
select * from t1;
ERROR 42000: User 'mysqltest_1' has exceeded the 'max_questions' resource (current value: 2)
select * from t1;
ERROR 42000: User 'mysqltest_1' has exceeded the 'max_questions' resource (current value: 2)
drop user mysqltest_1@localhost;
grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 2;
select * from t1;
i
select * from t1;
i
select * from t1;
i
delete from t1;
delete from t1;
delete from t1;
ERROR 42000: User 'mysqltest_1' has exceeded the 'max_updates' resource (current value: 2)
select * from t1;
i
delete from t1;
ERROR 42000: User 'mysqltest_1' has exceeded the 'max_updates' resource (current value: 2)
select * from t1;
i
drop user mysqltest_1@localhost;
grant usage on *.* to mysqltest_1@localhost with max_connections_per_hour 2;
select * from t1;
i
select * from t1;
i
connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK);
ERROR 42000: User 'mysqltest_1' has exceeded the 'max_connections' resource (current value: 2)
select * from t1;
i
connect(localhost,mysqltest_1,,test,9306,/home/dlenev/src/mysql-5.0-1339/mysql-test/var/tmp/master.sock);
ERROR 42000: User 'mysqltest_1' has exceeded the 'max_connections' resource (current value: 2)
drop user mysqltest_1@localhost;
flush privileges;
grant usage on *.* to mysqltest_1@localhost with max_user_connections 2;
select * from t1;
i
select * from t1;
i
connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK);
ERROR 42000: User 'mysqltest_1' has exceeded the 'max_user_connections' resource (current value: 2)
select * from t1;
i
grant usage on *.* to mysqltest_1@localhost with max_user_connections 3;
select * from t1;
i
connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK);
ERROR 42000: User 'mysqltest_1' has exceeded the 'max_user_connections' resource (current value: 3)
drop user mysqltest_1@localhost;
select @@session.max_user_connections, @@global.max_user_connections;
@@session.max_user_connections	@@global.max_user_connections
0	0
set session max_user_connections= 2;
ERROR HY000: Variable 'max_user_connections' is a GLOBAL variable and should be set with SET GLOBAL
set global max_user_connections= 2;
select @@session.max_user_connections, @@global.max_user_connections;
@@session.max_user_connections	@@global.max_user_connections
2	2
grant usage on *.* to mysqltest_1@localhost;
select @@session.max_user_connections, @@global.max_user_connections;
@@session.max_user_connections	@@global.max_user_connections
2	2
select * from t1;
i
connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK);
ERROR 42000: User mysqltest_1 has already more than 'max_user_connections' active connections
grant usage on *.* to mysqltest_1@localhost with max_user_connections 3;
select @@session.max_user_connections, @@global.max_user_connections;
@@session.max_user_connections	@@global.max_user_connections
3	2
connect(localhost,mysqltest_1,,test,MYSQL_PORT,MYSQL_SOCK);
ERROR 42000: User 'mysqltest_1' has exceeded the 'max_user_connections' resource (current value: 3)
set global max_user_connections= 0;
drop user mysqltest_1@localhost;
drop table t1;
+153 −0
Original line number Diff line number Diff line
#
# Test behavior of various per-account limits (aka quotas)
#

# Prepare play-ground 
--disable_warnings
drop table if exists t1;
--enable_warnings
create table t1 (i int);
# Just be sure that nothing will bother us
delete from mysql.user where user like 'mysqltest\_%';
delete from mysql.db where user like 'mysqltest\_%';
delete from mysql.tables_priv where user like 'mysqltest\_%';
delete from mysql.columns_priv where user like 'mysqltest\_%';
flush privileges;

# Test of MAX_QUERIES_PER_HOUR limit
grant usage on *.* to mysqltest_1@localhost with max_queries_per_hour 2;
connect (mqph, localhost, mysqltest_1,,);
connection mqph;
select * from t1;
select * from t1;
--error 1226
select * from t1;
connect (mqph2, localhost, mysqltest_1,,);
connection mqph2;
--error 1226
select * from t1;
# cleanup
connection default;
drop user mysqltest_1@localhost;
disconnect mqph;
disconnect mqph2;

# Test of MAX_UPDATES_PER_HOUR limit
grant usage on *.* to mysqltest_1@localhost with max_updates_per_hour 2;
connect (muph, localhost, mysqltest_1,,);
connection muph;
select * from t1;
select * from t1;
select * from t1;
delete from t1;
delete from t1;
--error 1226
delete from t1;
select * from t1;
connect (muph2, localhost, mysqltest_1,,);
connection muph2;
--error 1226
delete from t1;
select * from t1;
# Cleanup
connection default;
drop user mysqltest_1@localhost;
disconnect muph;
disconnect muph2;

# Test of MAX_CONNECTIONS_PER_HOUR limit
grant usage on *.* to mysqltest_1@localhost with max_connections_per_hour 2;
connect (mcph1, localhost, mysqltest_1,,);
connection mcph1;
select * from t1;
connect (mcph2, localhost, mysqltest_1,,);
connection mcph2;
select * from t1;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error 1226
connect (mcph3, localhost, mysqltest_1,,);
# Old connection is still ok
select * from t1;
# Let us try to close old connections and try again. This will also test that
# counters are not thrown away if there are no connections for this user.
disconnect mcph1;
disconnect mcph2;
--error 1226
connect (mcph3, localhost, mysqltest_1,,);
# Cleanup
connection default;
drop user mysqltest_1@localhost;

# Test of MAX_USER_CONNECTIONS limit
# We need this to reset internal mqh_used variable
flush privileges;
grant usage on *.* to mysqltest_1@localhost with max_user_connections 2;
connect (muc1, localhost, mysqltest_1,,);
connection muc1;
select * from t1;
connect (muc2, localhost, mysqltest_1,,);
connection muc2;
select * from t1;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error 1226
connect (muc3, localhost, mysqltest_1,,);
# Closing of one of connections should help
disconnect muc1;
connect (muc3, localhost, mysqltest_1,,);
select * from t1;
# Changing of limit should also help (and immediately) 
connection default;
grant usage on *.* to mysqltest_1@localhost with max_user_connections 3;
connect (muc4, localhost, mysqltest_1,,);
connection muc4;
select * from t1;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error 1226
connect (muc5, localhost, mysqltest_1,,);
# Clean up
connection default;
disconnect muc2;
disconnect muc3;
disconnect muc4;
drop user mysqltest_1@localhost;

# Now let us test interaction between global and per-account
# max_user_connections limits
select @@session.max_user_connections, @@global.max_user_connections;
# Local max_user_connections variable can't be set directly
# since this limit is per-account
--error 1229
set session max_user_connections= 2; 
# But it is ok to set global max_user_connections
set global max_user_connections= 2; 
select @@session.max_user_connections, @@global.max_user_connections;
# Let us check that global limit works
grant usage on *.* to mysqltest_1@localhost;
connect (muca1, localhost, mysqltest_1,,);
connection muca1;
select @@session.max_user_connections, @@global.max_user_connections;
connect (muca2, localhost, mysqltest_1,,);
connection muca2;
select * from t1;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error 1203
connect (muca3, localhost, mysqltest_1,,);
# Now we are testing that per-account limit prevails over gloabl limit
connection default;
grant usage on *.* to mysqltest_1@localhost with max_user_connections 3;
connect (muca3, localhost, mysqltest_1,,);
connection muca3;
select @@session.max_user_connections, @@global.max_user_connections;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error 1226
connect (muca4, localhost, mysqltest_1,,);
# Cleanup
connection default;
disconnect muca1;
disconnect muca2;
disconnect muca3;
set global max_user_connections= 0; 
drop user mysqltest_1@localhost;

# Final cleanup
drop table t1;
+9 −8

File changed.

Preview size limit exceeded, changes collapsed.

Loading