mirror of
https://github.com/MariaDB/server.git
synced 2025-01-18 04:53:01 +01:00
Manual merge.
This commit is contained in:
commit
536cb92195
17 changed files with 766 additions and 159 deletions
|
@ -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,
|
||||
con_db, con_port, con_sock ? con_sock: 0)))
|
||||
die("Could not open connection '%s': %s", con_name,
|
||||
mysql_error(&next_con->mysql));
|
||||
|
||||
if (!(next_con->name = my_strdup(con_name, MYF(MY_WME))))
|
||||
die(NullS);
|
||||
cur_con = next_con++;
|
||||
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,93 +2656,14 @@ 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);
|
||||
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)
|
||||
{
|
||||
/* 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;
|
||||
if (normal_handle_error(query, q, mysql, ds))
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
else if (q->expected_errno[0].type == ERR_SQLSTATE &&
|
||||
strcmp(q->expected_errno[0].code.sqlstate,"00000") != 0)
|
||||
|
||||
if (normal_handle_no_error(q))
|
||||
{
|
||||
/* 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;
|
||||
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 @@ end:
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
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:
|
||||
|
|
|
@ -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 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
|
||||
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 max_user_connections
|
||||
localhost mysqltest_1 N N N N N N N N N N N N N N N N N N N N N 0 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'
|
||||
|
|
|
@ -611,6 +611,7 @@ 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
|
||||
max_user_connections select,insert,update,references
|
||||
use test;
|
||||
create function sub1(i int) returns int
|
||||
return i+1;
|
||||
|
|
|
@ -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
mysql-test/r/user_limits.result
Normal file
89
mysql-test/r/user_limits.result
Normal file
|
@ -0,0 +1,89 @@
|
|||
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
mysql-test/t/user_limits.test
Normal file
153
mysql-test/t/user_limits.test
Normal file
|
@ -0,0 +1,153 @@
|
|||
#
|
||||
# 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;
|
|
@ -153,6 +153,7 @@ then
|
|||
c_u="$c_u max_questions int(11) unsigned DEFAULT 0 NOT NULL,"
|
||||
c_u="$c_u max_updates int(11) unsigned DEFAULT 0 NOT NULL,"
|
||||
c_u="$c_u max_connections int(11) unsigned DEFAULT 0 NOT NULL,"
|
||||
c_u="$c_u max_user_connections int(11) unsigned DEFAULT 0 NOT NULL,"
|
||||
c_u="$c_u PRIMARY KEY Host (Host,User)"
|
||||
c_u="$c_u ) engine=MyISAM"
|
||||
c_u="$c_u CHARACTER SET utf8 COLLATE utf8_bin"
|
||||
|
@ -160,24 +161,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','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);
|
||||
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,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,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,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','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,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','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,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','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);"
|
||||
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,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,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,0);"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
|
|
@ -197,6 +197,11 @@ ALTER TABLE db ADD Execute_priv enum('N','Y') DEFAULT 'N' NOT NULL AFTER Alter_r
|
|||
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;
|
||||
|
||||
#
|
||||
# Add max_user_connections resource limit
|
||||
#
|
||||
ALTER TABLE user ADD max_user_connections int(11) unsigned DEFAULT '0' NOT NULL AFTER max_connections;
|
||||
|
||||
#
|
||||
# Create some possible missing tables
|
||||
#
|
||||
|
|
|
@ -304,6 +304,7 @@ static SYMBOL symbols[] = {
|
|||
{ "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR)},
|
||||
{ "MAX_ROWS", SYM(MAX_ROWS)},
|
||||
{ "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR)},
|
||||
{ "MAX_USER_CONNECTIONS", SYM(MAX_USER_CONNECTIONS_SYM)},
|
||||
{ "MEDIUM", SYM(MEDIUM_SYM)},
|
||||
{ "MEDIUMBLOB", SYM(MEDIUMBLOB)},
|
||||
{ "MEDIUMINT", SYM(MEDIUMINT)},
|
||||
|
|
|
@ -1024,6 +1024,7 @@ 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 my_bool opt_old_style_user_limits;
|
||||
extern uint opt_crash_binlog_innodb;
|
||||
extern char *shared_memory_base_name, *mysqld_unix_port;
|
||||
extern bool opt_enable_shared_memory;
|
||||
|
|
|
@ -306,6 +306,12 @@ my_bool lower_case_file_system= 0;
|
|||
my_bool opt_innodb_safe_binlog= 0;
|
||||
my_bool opt_large_pages= 0;
|
||||
uint opt_large_page_size= 0;
|
||||
my_bool opt_old_style_user_limits= 0;
|
||||
/*
|
||||
True if there is at least one per-hour limit for some user, so we should check
|
||||
them before each query (and possibly reset counters when hour is changed).
|
||||
False otherwise.
|
||||
*/
|
||||
volatile bool mqh_used = 0;
|
||||
my_bool sp_automatic_privileges= 1;
|
||||
|
||||
|
@ -4215,7 +4221,8 @@ enum options_mysqld
|
|||
OPT_SP_AUTOMATIC_PRIVILEGES,
|
||||
OPT_AUTO_INCREMENT, OPT_AUTO_INCREMENT_OFFSET,
|
||||
OPT_ENABLE_LARGE_PAGES,
|
||||
OPT_TIMED_MUTEXES
|
||||
OPT_TIMED_MUTEXES,
|
||||
OPT_OLD_STYLE_USER_LIMITS
|
||||
};
|
||||
|
||||
|
||||
|
@ -4655,6 +4662,10 @@ Disable with --skip-ndbcluster (will save memory).",
|
|||
"Only use one thread (for debugging under Linux).", 0, 0, 0, GET_NO_ARG,
|
||||
NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
#endif
|
||||
{"old-style-user-limits", OPT_OLD_STYLE_USER_LIMITS,
|
||||
"Enable old-style user limits (before 5.0.3 user resources were counted per each user+host vs. per account)",
|
||||
(gptr*) &opt_old_style_user_limits, (gptr*) &opt_old_style_user_limits,
|
||||
0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
|
||||
{"pid-file", OPT_PID_FILE, "Pid file used by safe_mysqld.",
|
||||
(gptr*) &pidfile_name_ptr, (gptr*) &pidfile_name_ptr, 0, GET_STR,
|
||||
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
|
||||
|
|
|
@ -252,8 +252,7 @@ sys_var_long_ptr sys_max_relay_log_size("max_relay_log_size",
|
|||
fix_max_relay_log_size);
|
||||
sys_var_thd_ulong sys_max_sort_length("max_sort_length",
|
||||
&SV::max_sort_length);
|
||||
sys_var_long_ptr sys_max_user_connections("max_user_connections",
|
||||
&max_user_connections);
|
||||
sys_var_max_user_conn sys_max_user_connections("max_user_connections");
|
||||
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",
|
||||
|
@ -2526,6 +2525,51 @@ void sys_var_thd_time_zone::set_default(THD *thd, enum_var_type type)
|
|||
thd->variables.time_zone= global_system_variables.time_zone;
|
||||
}
|
||||
|
||||
|
||||
bool sys_var_max_user_conn::check(THD *thd, set_var *var)
|
||||
{
|
||||
if (var->type == OPT_GLOBAL)
|
||||
return sys_var_thd::check(thd, var);
|
||||
else
|
||||
{
|
||||
/*
|
||||
Per-session values of max_user_connections can't be set directly.
|
||||
QQ: May be we should have a separate error message for this?
|
||||
*/
|
||||
my_error(ER_GLOBAL_VARIABLE, MYF(0), name);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
bool sys_var_max_user_conn::update(THD *thd, set_var *var)
|
||||
{
|
||||
DBUG_ASSERT(var->type == OPT_GLOBAL);
|
||||
pthread_mutex_lock(&LOCK_global_system_variables);
|
||||
max_user_connections= var->save_result.ulonglong_value;
|
||||
pthread_mutex_unlock(&LOCK_global_system_variables);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void sys_var_max_user_conn::set_default(THD *thd, enum_var_type type)
|
||||
{
|
||||
DBUG_ASSERT(type == OPT_GLOBAL);
|
||||
pthread_mutex_lock(&LOCK_global_system_variables);
|
||||
max_user_connections= (ulong) option_limits->def_value;
|
||||
pthread_mutex_unlock(&LOCK_global_system_variables);
|
||||
}
|
||||
|
||||
|
||||
byte *sys_var_max_user_conn::value_ptr(THD *thd, enum_var_type type,
|
||||
LEX_STRING *base)
|
||||
{
|
||||
if (type != OPT_GLOBAL &&
|
||||
thd->user_connect && thd->user_connect->user_resources.user_conn)
|
||||
return (byte*) &(thd->user_connect->user_resources.user_conn);
|
||||
return (byte*) &(max_user_connections);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Functions to update thd->options bits
|
||||
*/
|
||||
|
|
|
@ -727,6 +727,23 @@ public:
|
|||
Time_zone **get_tz_ptr(THD *thd, enum_var_type type);
|
||||
};
|
||||
|
||||
|
||||
class sys_var_max_user_conn : public sys_var_thd
|
||||
{
|
||||
public:
|
||||
sys_var_max_user_conn(const char *name_arg):
|
||||
sys_var_thd(name_arg) {}
|
||||
bool check(THD *thd, set_var *var);
|
||||
bool update(THD *thd, set_var *var);
|
||||
bool check_default(enum_var_type type)
|
||||
{
|
||||
return type != OPT_GLOBAL || !option_limits;
|
||||
}
|
||||
void set_default(THD *thd, enum_var_type type);
|
||||
SHOW_TYPE type() { return SHOW_LONG; }
|
||||
byte *value_ptr(THD *thd, enum_var_type type, LEX_STRING *base);
|
||||
};
|
||||
|
||||
/****************************************************************************
|
||||
Classes for parsing of the SET command
|
||||
****************************************************************************/
|
||||
|
|
|
@ -343,10 +343,19 @@ my_bool acl_init(THD *org_thd, bool dont_read_acl_tables)
|
|||
ptr = get_field(&mem, table->field[next_field++]);
|
||||
user.user_resource.updates=ptr ? atoi(ptr) : 0;
|
||||
ptr = get_field(&mem, table->field[next_field++]);
|
||||
user.user_resource.connections=ptr ? atoi(ptr) : 0;
|
||||
user.user_resource.conn_per_hour= ptr ? atoi(ptr) : 0;
|
||||
if (user.user_resource.questions || user.user_resource.updates ||
|
||||
user.user_resource.connections)
|
||||
user.user_resource.conn_per_hour)
|
||||
mqh_used=1;
|
||||
|
||||
if (table->fields >= 34)
|
||||
{
|
||||
/* Starting from 5.0.3 we have max_user_connections field */
|
||||
ptr= get_field(&mem, table->field[next_field++]);
|
||||
user.user_resource.user_conn= ptr ? atoi(ptr) : 0;
|
||||
}
|
||||
else
|
||||
user.user_resource.user_conn= 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -934,12 +943,14 @@ static void acl_update_user(const char *user, const char *host,
|
|||
!my_strcasecmp(system_charset_info, host, acl_user->host.hostname))
|
||||
{
|
||||
acl_user->access=privileges;
|
||||
if (mqh->bits & 1)
|
||||
if (mqh->specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
|
||||
acl_user->user_resource.questions=mqh->questions;
|
||||
if (mqh->bits & 2)
|
||||
if (mqh->specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
|
||||
acl_user->user_resource.updates=mqh->updates;
|
||||
if (mqh->bits & 4)
|
||||
acl_user->user_resource.connections=mqh->connections;
|
||||
if (mqh->specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
|
||||
acl_user->user_resource.conn_per_hour= mqh->conn_per_hour;
|
||||
if (mqh->specified_limits & USER_RESOURCES::USER_CONNECTIONS)
|
||||
acl_user->user_resource.user_conn= mqh->user_conn;
|
||||
if (ssl_type != SSL_TYPE_NOT_SPECIFIED)
|
||||
{
|
||||
acl_user->ssl_type= ssl_type;
|
||||
|
@ -1622,7 +1633,8 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
|
|||
if (combo.password.str) // If password given
|
||||
table->field[2]->store(password, password_len, system_charset_info);
|
||||
else if (!rights && !revoke_grant &&
|
||||
lex->ssl_type == SSL_TYPE_NOT_SPECIFIED && !lex->mqh.bits)
|
||||
lex->ssl_type == SSL_TYPE_NOT_SPECIFIED &&
|
||||
!lex->mqh.specified_limits)
|
||||
{
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
@ -1684,13 +1696,16 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
|
|||
}
|
||||
|
||||
USER_RESOURCES mqh= lex->mqh;
|
||||
if (mqh.bits & 1)
|
||||
if (mqh.specified_limits & USER_RESOURCES::QUERIES_PER_HOUR)
|
||||
table->field[28]->store((longlong) mqh.questions);
|
||||
if (mqh.bits & 2)
|
||||
if (mqh.specified_limits & USER_RESOURCES::UPDATES_PER_HOUR)
|
||||
table->field[29]->store((longlong) mqh.updates);
|
||||
if (mqh.bits & 4)
|
||||
table->field[30]->store((longlong) mqh.connections);
|
||||
mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections;
|
||||
if (mqh.specified_limits & USER_RESOURCES::CONNECTIONS_PER_HOUR)
|
||||
table->field[30]->store((longlong) mqh.conn_per_hour);
|
||||
if (table->fields >= 34 &&
|
||||
(mqh.specified_limits & USER_RESOURCES::USER_CONNECTIONS))
|
||||
table->field[33]->store((longlong) mqh.user_conn);
|
||||
mqh_used= mqh_used || mqh.questions || mqh.updates || mqh.conn_per_hour;
|
||||
}
|
||||
if (old_row_exists)
|
||||
{
|
||||
|
@ -3817,8 +3832,10 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
|
|||
}
|
||||
}
|
||||
if ((want_access & GRANT_ACL) ||
|
||||
(acl_user->user_resource.questions | acl_user->user_resource.updates |
|
||||
acl_user->user_resource.connections))
|
||||
(acl_user->user_resource.questions ||
|
||||
acl_user->user_resource.updates ||
|
||||
acl_user->user_resource.conn_per_hour ||
|
||||
acl_user->user_resource.user_conn))
|
||||
{
|
||||
global.append(" WITH",5);
|
||||
if (want_access & GRANT_ACL)
|
||||
|
@ -3827,8 +3844,10 @@ bool mysql_show_grants(THD *thd,LEX_USER *lex_user)
|
|||
"MAX_QUERIES_PER_HOUR");
|
||||
add_user_option(&global, acl_user->user_resource.updates,
|
||||
"MAX_UPDATES_PER_HOUR");
|
||||
add_user_option(&global, acl_user->user_resource.connections,
|
||||
add_user_option(&global, acl_user->user_resource.conn_per_hour,
|
||||
"MAX_CONNECTIONS_PER_HOUR");
|
||||
add_user_option(&global, acl_user->user_resource.user_conn,
|
||||
"MAX_USER_CONNECTIONS");
|
||||
}
|
||||
protocol->prepare_for_resend();
|
||||
protocol->store(global.ptr(),global.length(),global.charset());
|
||||
|
|
|
@ -175,14 +175,10 @@ static int get_or_create_user_conn(THD *thd, const char *user,
|
|||
}
|
||||
uc->user=(char*) (uc+1);
|
||||
memcpy(uc->user,temp_user,temp_len+1);
|
||||
uc->user_len= user_len;
|
||||
uc->host=uc->user + uc->user_len + 1;
|
||||
uc->host= uc->user + user_len + 1;
|
||||
uc->len = temp_len;
|
||||
uc->connections = 1;
|
||||
uc->questions=uc->updates=uc->conn_per_hour=0;
|
||||
uc->connections= uc->questions= uc->updates= uc->conn_per_hour= 0;
|
||||
uc->user_resources=*mqh;
|
||||
if (max_user_connections && mqh->connections > max_user_connections)
|
||||
uc->user_resources.connections = max_user_connections;
|
||||
uc->intime=thd->thr_create_time;
|
||||
if (my_hash_insert(&hash_user_connections, (byte*) uc))
|
||||
{
|
||||
|
@ -355,12 +351,16 @@ int check_user(THD *thd, enum enum_server_command command,
|
|||
thd->db_access=0;
|
||||
|
||||
/* Don't allow user to connect if he has done too many queries */
|
||||
if ((ur.questions || ur.updates || ur.connections ||
|
||||
if ((ur.questions || ur.updates || ur.conn_per_hour || ur.user_conn ||
|
||||
max_user_connections) &&
|
||||
get_or_create_user_conn(thd,thd->user,thd->host_or_ip,&ur))
|
||||
get_or_create_user_conn(thd,
|
||||
opt_old_style_user_limits ? thd->user : thd->priv_user,
|
||||
opt_old_style_user_limits ? thd->host_or_ip : thd->priv_host,
|
||||
&ur))
|
||||
DBUG_RETURN(-1);
|
||||
if (thd->user_connect &&
|
||||
(thd->user_connect->user_resources.connections ||
|
||||
(thd->user_connect->user_resources.conn_per_hour ||
|
||||
thd->user_connect->user_resources.user_conn ||
|
||||
max_user_connections) &&
|
||||
check_for_max_user_connections(thd, thd->user_connect))
|
||||
DBUG_RETURN(-1);
|
||||
|
@ -451,19 +451,28 @@ static int check_for_max_user_connections(THD *thd, USER_CONN *uc)
|
|||
DBUG_ENTER("check_for_max_user_connections");
|
||||
|
||||
(void) pthread_mutex_lock(&LOCK_user_conn);
|
||||
if (max_user_connections &&
|
||||
if (max_user_connections && !uc->user_resources.user_conn &&
|
||||
max_user_connections < (uint) uc->connections)
|
||||
{
|
||||
net_printf_error(thd, ER_TOO_MANY_USER_CONNECTIONS, uc->user);
|
||||
error=1;
|
||||
goto end;
|
||||
}
|
||||
if (uc->user_resources.connections &&
|
||||
uc->user_resources.connections <= uc->conn_per_hour)
|
||||
if (uc->user_resources.user_conn &&
|
||||
uc->user_resources.user_conn < uc->connections)
|
||||
{
|
||||
net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
|
||||
"max_user_connections",
|
||||
(long) uc->user_resources.user_conn);
|
||||
error= 1;
|
||||
goto end;
|
||||
}
|
||||
if (uc->user_resources.conn_per_hour &&
|
||||
uc->user_resources.conn_per_hour <= uc->conn_per_hour)
|
||||
{
|
||||
net_printf_error(thd, ER_USER_LIMIT_REACHED, uc->user,
|
||||
"max_connections",
|
||||
(long) uc->user_resources.connections);
|
||||
(long) uc->user_resources.conn_per_hour);
|
||||
error=1;
|
||||
goto end;
|
||||
}
|
||||
|
@ -3526,7 +3535,7 @@ create_error:
|
|||
Query_log_event qinfo(thd, thd->query, thd->query_length, 0, FALSE);
|
||||
mysql_bin_log.write(&qinfo);
|
||||
}
|
||||
if (mqh_used && lex->sql_command == SQLCOM_GRANT)
|
||||
if (lex->sql_command == SQLCOM_GRANT)
|
||||
{
|
||||
List_iterator <LEX_USER> str_list(lex->users_list);
|
||||
LEX_USER *user;
|
||||
|
@ -5698,8 +5707,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
|
|||
{
|
||||
acl_reload(thd);
|
||||
grant_reload(thd);
|
||||
if (mqh_used)
|
||||
reset_mqh((LEX_USER *) NULL,TRUE);
|
||||
reset_mqh((LEX_USER *)NULL, TRUE);
|
||||
}
|
||||
#endif
|
||||
if (options & REFRESH_LOG)
|
||||
|
|
|
@ -334,6 +334,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
|
|||
%token MAX_CONNECTIONS_PER_HOUR
|
||||
%token MAX_QUERIES_PER_HOUR
|
||||
%token MAX_UPDATES_PER_HOUR
|
||||
%token MAX_USER_CONNECTIONS_SYM
|
||||
%token MEDIUM_SYM
|
||||
%token MIN_ROWS
|
||||
%token MUTEX_SYM
|
||||
|
@ -6990,6 +6991,7 @@ keyword:
|
|||
| MAX_CONNECTIONS_PER_HOUR {}
|
||||
| MAX_QUERIES_PER_HOUR {}
|
||||
| MAX_UPDATES_PER_HOUR {}
|
||||
| MAX_USER_CONNECTIONS_SYM {}
|
||||
| MEDIUM_SYM {}
|
||||
| MERGE_SYM {}
|
||||
| MICROSECOND_SYM {}
|
||||
|
@ -7815,18 +7817,23 @@ grant_option:
|
|||
| MAX_QUERIES_PER_HOUR ULONG_NUM
|
||||
{
|
||||
Lex->mqh.questions=$2;
|
||||
Lex->mqh.bits |= 1;
|
||||
Lex->mqh.specified_limits|= USER_RESOURCES::QUERIES_PER_HOUR;
|
||||
}
|
||||
| MAX_UPDATES_PER_HOUR ULONG_NUM
|
||||
{
|
||||
Lex->mqh.updates=$2;
|
||||
Lex->mqh.bits |= 2;
|
||||
Lex->mqh.specified_limits|= USER_RESOURCES::UPDATES_PER_HOUR;
|
||||
}
|
||||
| MAX_CONNECTIONS_PER_HOUR ULONG_NUM
|
||||
{
|
||||
Lex->mqh.connections=$2;
|
||||
Lex->mqh.bits |= 4;
|
||||
Lex->mqh.conn_per_hour= $2;
|
||||
Lex->mqh.specified_limits|= USER_RESOURCES::CONNECTIONS_PER_HOUR;
|
||||
}
|
||||
| MAX_USER_CONNECTIONS_SYM ULONG_NUM
|
||||
{
|
||||
Lex->mqh.user_conn= $2;
|
||||
Lex->mqh.specified_limits|= USER_RESOURCES::USER_CONNECTIONS;
|
||||
}
|
||||
;
|
||||
|
||||
begin:
|
||||
|
|
|
@ -206,16 +206,65 @@ typedef struct st_lex_user {
|
|||
} LEX_USER;
|
||||
|
||||
|
||||
/*
|
||||
This structure specifies the maximum amount of resources which
|
||||
can be consumed by each account. Zero value of a member means
|
||||
there is no limit.
|
||||
*/
|
||||
typedef struct user_resources {
|
||||
uint questions, updates, connections, bits;
|
||||
/* Maximum number of queries/statements per hour. */
|
||||
uint questions;
|
||||
/*
|
||||
Maximum number of updating statements per hour (which statements are
|
||||
updating is defined by uc_update_queries array).
|
||||
*/
|
||||
uint updates;
|
||||
/* Maximum number of connections established per hour. */
|
||||
uint conn_per_hour;
|
||||
/* Maximum number of concurrent connections. */
|
||||
uint user_conn;
|
||||
/*
|
||||
Values of this enum and specified_limits member are used by the
|
||||
parser to store which user limits were specified in GRANT statement.
|
||||
*/
|
||||
enum {QUERIES_PER_HOUR= 1, UPDATES_PER_HOUR= 2, CONNECTIONS_PER_HOUR= 4,
|
||||
USER_CONNECTIONS= 8};
|
||||
uint specified_limits;
|
||||
} USER_RESOURCES;
|
||||
|
||||
|
||||
/*
|
||||
This structure is used for counting resources consumed and for checking
|
||||
them against specified user limits.
|
||||
*/
|
||||
typedef struct user_conn {
|
||||
char *user, *host;
|
||||
uint len, connections, conn_per_hour, updates, questions, user_len;
|
||||
/*
|
||||
Pointer to user+host key (pair separated by '\0') defining the entity
|
||||
for which resources are counted (By default it is user account thus
|
||||
priv_user/priv_host pair is used. If --old-style-user-limits option
|
||||
is enabled, resources are counted for each user+host separately).
|
||||
*/
|
||||
char *user;
|
||||
/* Pointer to host part of the key. */
|
||||
char *host;
|
||||
/* Total length of the key. */
|
||||
uint len;
|
||||
/* Current amount of concurrent connections for this account. */
|
||||
uint connections;
|
||||
/*
|
||||
Current number of connections per hour, number of updating statements
|
||||
per hour and total number of statements per hour for this account.
|
||||
*/
|
||||
uint conn_per_hour, updates, questions;
|
||||
/* Maximum amount of resources which account is allowed to consume. */
|
||||
USER_RESOURCES user_resources;
|
||||
/*
|
||||
The moment of time when per hour counters were reset last time
|
||||
(i.e. start of "hour" for conn_per_hour, updates, questions counters).
|
||||
*/
|
||||
time_t intime;
|
||||
} USER_CONN;
|
||||
|
||||
/* Bits in form->update */
|
||||
#define REG_MAKE_DUPP 1 /* Make a copy of record when read */
|
||||
#define REG_NEW_RECORD 2 /* Write a new record if not found */
|
||||
|
|
Loading…
Reference in a new issue