Commit 6c8ce999 authored by Robert Bindar's avatar Robert Bindar Committed by Sergei Golubchik

MDEV-13095 Implement User Account locking

Add server support for user account locking.
This patch extends the ALTER/CREATE USER statements for
denying a user's subsequent login attempts:
  ALTER USER
    user [, user2] ACCOUNT [LOCK | UNLOCK]
  CREATE USER
    user [, user2] ACCOUNT [LOCK | UNLOCK]
The SHOW CREATE USER statement was updated to display the
locking state of an user.

Closes #1006
parent d89cdfc2
create user user1@localhost;
create user user2@localhost;
#
# Only privileged users should be able to lock/unlock.
#
alter user user1@localhost account lock;
alter user user1@localhost account unlock;
create user user3@localhost account lock;
drop user user3@localhost;
connect con1,localhost,user1;
connection con1;
alter user user2@localhost account lock;
ERROR 42000: Access denied; you need (at least one of) the CREATE USER privilege(s) for this operation
disconnect con1;
connection default;
#
# ALTER USER USER1 ACCOUNT LOCK should deny the connection of user1,
# but it should allow user2 to connect.
#
alter user user1@localhost account lock;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Access denied, this account is locked
connect con2,localhost,user2;
disconnect con2;
connection default;
alter user user1@localhost account unlock;
#
# Passing an incorrect user should return an error unless
# IF EXISTS is used
#
alter user inexistentUser@localhost account lock;
ERROR HY000: Operation ALTER USER failed for 'inexistentUser'@'localhost'
alter if exists user inexistentUser@localhost account lock;
Warnings:
Error 1133 Can't find any matching row in the user table
Note 1396 Operation ALTER USER failed for 'inexistentUser'@'localhost'
#
# Passing an existing user to CREATE should not be allowed
# and it should not change the locking state of the current user
#
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost'
create user user1@localhost account lock;
ERROR HY000: Operation CREATE USER failed for 'user1'@'localhost'
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost'
#
# Passing multiple users should lock them all
#
alter user user1@localhost, user2@localhost account lock;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Access denied, this account is locked
connect(localhost,user2,,test,MYSQL_PORT,MYSQL_SOCK);
connect con2,localhost,user2;
ERROR HY000: Access denied, this account is locked
alter user user1@localhost, user2@localhost account unlock;
#
# The locking state is preserved after acl reload
#
alter user user1@localhost account lock;
flush privileges;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Access denied, this account is locked
alter user user1@localhost account unlock;
#
# JSON functions on global_priv reflect the locking state of an account
#
alter user user1@localhost account lock;
select host, user, JSON_VALUE(Priv, '$.account_locked') from mysql.global_priv where user='user1';
host user JSON_VALUE(Priv, '$.account_locked')
localhost user1 1
alter user user1@localhost account unlock;
select host, user, JSON_VALUE(Priv, '$.account_locked') from mysql.global_priv where user='user1';
host user JSON_VALUE(Priv, '$.account_locked')
localhost user1 0
#
# SHOW CREATE USER correctly displays the locking state of an user
#
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost'
alter user user1@localhost account lock;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' ACCOUNT LOCK
alter user user1@localhost account unlock;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost'
create user newuser@localhost account lock;
show create user newuser@localhost;
CREATE USER for newuser@localhost
CREATE USER 'newuser'@'localhost' ACCOUNT LOCK
drop user newuser@localhost;
#
# Users should be able to lock themselves
#
grant CREATE USER on *.* to user1@localhost;
connect con1,localhost,user1;
connection con1;
alter user user1@localhost account lock;
disconnect con1;
connection default;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Access denied, this account is locked
alter user user1@localhost account unlock;
#
# Users should be able to unlock themselves if the connections
# had been established before the accounts were locked
#
grant CREATE USER on *.* to user1@localhost;
connect con1,localhost,user1;
alter user user1@localhost account lock;
connection con1;
alter user user1@localhost account unlock;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost'
disconnect con1;
connection default;
#
# COM_CHANGE_USER should return error if the destination
# account is locked
#
alter user user1@localhost account lock;
ERROR HY000: Access denied, this account is locked
drop user user1@localhost;
drop user user2@localhost;
#
# Test user account locking
#
--source include/not_embedded.inc
create user user1@localhost;
create user user2@localhost;
--echo #
--echo # Only privileged users should be able to lock/unlock.
--echo #
alter user user1@localhost account lock;
alter user user1@localhost account unlock;
create user user3@localhost account lock;
drop user user3@localhost;
connect(con1,localhost,user1);
connection con1;
--error ER_SPECIFIC_ACCESS_DENIED_ERROR
alter user user2@localhost account lock;
disconnect con1;
connection default;
--echo #
--echo # ALTER USER USER1 ACCOUNT LOCK should deny the connection of user1,
--echo # but it should allow user2 to connect.
--echo #
alter user user1@localhost account lock;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error ER_ACCOUNT_HAS_BEEN_LOCKED
connect(con1,localhost,user1);
connect(con2,localhost,user2);
disconnect con2;
connection default;
alter user user1@localhost account unlock;
--echo #
--echo # Passing an incorrect user should return an error unless
--echo # IF EXISTS is used
--echo #
--error ER_CANNOT_USER
alter user inexistentUser@localhost account lock;
alter if exists user inexistentUser@localhost account lock;
--echo #
--echo # Passing an existing user to CREATE should not be allowed
--echo # and it should not change the locking state of the current user
--echo #
show create user user1@localhost;
--error ER_CANNOT_USER
create user user1@localhost account lock;
show create user user1@localhost;
--echo #
--echo # Passing multiple users should lock them all
--echo #
alter user user1@localhost, user2@localhost account lock;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error ER_ACCOUNT_HAS_BEEN_LOCKED
connect(con1,localhost,user1);
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error ER_ACCOUNT_HAS_BEEN_LOCKED
connect(con2,localhost,user2);
alter user user1@localhost, user2@localhost account unlock;
--echo #
--echo # The locking state is preserved after acl reload
--echo #
alter user user1@localhost account lock;
flush privileges;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error ER_ACCOUNT_HAS_BEEN_LOCKED
connect(con1,localhost,user1);
alter user user1@localhost account unlock;
--echo #
--echo # JSON functions on global_priv reflect the locking state of an account
--echo #
alter user user1@localhost account lock;
select host, user, JSON_VALUE(Priv, '$.account_locked') from mysql.global_priv where user='user1';
alter user user1@localhost account unlock;
select host, user, JSON_VALUE(Priv, '$.account_locked') from mysql.global_priv where user='user1';
--echo #
--echo # SHOW CREATE USER correctly displays the locking state of an user
--echo #
show create user user1@localhost;
alter user user1@localhost account lock;
show create user user1@localhost;
alter user user1@localhost account unlock;
show create user user1@localhost;
create user newuser@localhost account lock;
show create user newuser@localhost;
drop user newuser@localhost;
--echo #
--echo # Users should be able to lock themselves
--echo #
grant CREATE USER on *.* to user1@localhost;
connect(con1,localhost,user1);
connection con1;
alter user user1@localhost account lock;
disconnect con1;
connection default;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error ER_ACCOUNT_HAS_BEEN_LOCKED
connect(con1,localhost,user1);
alter user user1@localhost account unlock;
--echo #
--echo # Users should be able to unlock themselves if the connections
--echo # had been established before the accounts were locked
--echo #
grant CREATE USER on *.* to user1@localhost;
connect(con1,localhost,user1);
alter user user1@localhost account lock;
connection con1;
alter user user1@localhost account unlock;
show create user user1@localhost;
disconnect con1;
connection default;
--echo #
--echo # COM_CHANGE_USER should return error if the destination
--echo # account is locked
--echo #
alter user user1@localhost account lock;
--error ER_ACCOUNT_HAS_BEEN_LOCKED
--change_user user1
drop user user1@localhost;
drop user user2@localhost;
...@@ -165,5 +165,26 @@ foo % Y mysql_native_password *E8D46CE25265E545D225A8A6F1BAF642FEBEE5CB ...@@ -165,5 +165,26 @@ foo % Y mysql_native_password *E8D46CE25265E545D225A8A6F1BAF642FEBEE5CB
goo % Y mysql_native_password *F3A2A51A9B0F2BE2468926B4132313728C250DBF goo % Y mysql_native_password *F3A2A51A9B0F2BE2468926B4132313728C250DBF
ioo % Y mysql_old_password 7a8f886d28473e85 ioo % Y mysql_old_password 7a8f886d28473e85
# #
# Test account locking
#
create user user1@localhost account lock;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Access denied, this account is locked
flush privileges;
connect(localhost,user1,,test,MYSQL_PORT,MYSQL_SOCK);
connect con1,localhost,user1;
ERROR HY000: Access denied, this account is locked
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost' ACCOUNT LOCK
alter user user1@localhost account unlock;
connect con1,localhost,user1;
disconnect con1;
connection default;
show create user user1@localhost;
CREATE USER for user1@localhost
CREATE USER 'user1'@'localhost'
#
# Reset to final original state. # Reset to final original state.
# #
...@@ -88,6 +88,24 @@ select user, host, select_priv, plugin, authentication_string from mysql.user ...@@ -88,6 +88,24 @@ select user, host, select_priv, plugin, authentication_string from mysql.user
where user like "%oo" where user like "%oo"
order by user; order by user;
--echo #
--echo # Test account locking
--echo #
create user user1@localhost account lock;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error ER_ACCOUNT_HAS_BEEN_LOCKED
connect(con1,localhost,user1);
flush privileges;
--replace_result $MASTER_MYPORT MYSQL_PORT $MASTER_MYSOCK MYSQL_SOCK
--error ER_ACCOUNT_HAS_BEEN_LOCKED
connect(con1,localhost,user1);
show create user user1@localhost;
alter user user1@localhost account unlock;
connect(con1,localhost,user1);
disconnect con1;
connection default;
show create user user1@localhost;
--echo # --echo #
--echo # Reset to final original state. --echo # Reset to final original state.
--echo # --echo #
......
...@@ -643,6 +643,7 @@ ALTER TABLE user ADD plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL, ...@@ -643,6 +643,7 @@ ALTER TABLE user ADD plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL,
ALTER TABLE user MODIFY plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL, ALTER TABLE user MODIFY plugin char(64) CHARACTER SET latin1 DEFAULT '' NOT NULL,
MODIFY authentication_string TEXT NOT NULL; MODIFY authentication_string TEXT NOT NULL;
ALTER TABLE user ADD password_expired ENUM('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL; ALTER TABLE user ADD password_expired ENUM('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL;
ALTER TABLE user ADD account_locked enum('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL after password_expired;
ALTER TABLE user ADD is_role enum('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL; ALTER TABLE user ADD is_role enum('N', 'Y') COLLATE utf8_general_ci DEFAULT 'N' NOT NULL;
ALTER TABLE user ADD default_role char(80) binary DEFAULT '' NOT NULL; ALTER TABLE user ADD default_role char(80) binary DEFAULT '' NOT NULL;
ALTER TABLE user ADD max_statement_time decimal(12,6) DEFAULT 0 NOT NULL; ALTER TABLE user ADD max_statement_time decimal(12,6) DEFAULT 0 NOT NULL;
...@@ -804,6 +805,7 @@ IF 'BASE TABLE' = (select table_type from information_schema.tables where table_ ...@@ -804,6 +805,7 @@ IF 'BASE TABLE' = (select table_type from information_schema.tables where table_
'max_statement_time', max_statement_time, 'max_statement_time', max_statement_time,
'plugin', if(plugin>'',plugin,if(length(password)=16,'mysql_old_password','mysql_native_password')), 'plugin', if(plugin>'',plugin,if(length(password)=16,'mysql_old_password','mysql_native_password')),
'authentication_string', if(plugin>'' and authentication_string>'',authentication_string,password), 'authentication_string', if(plugin>'' and authentication_string>'',authentication_string,password),
'account_locked', 'Y'=account_locked,
'default_role', default_role, 'default_role', default_role,
'is_role', 'Y'=is_role)) as Priv 'is_role', 'Y'=is_role)) as Priv
FROM user; FROM user;
......
...@@ -55,6 +55,7 @@ static SYMBOL symbols[] = { ...@@ -55,6 +55,7 @@ static SYMBOL symbols[] = {
{ ">>", SYM(SHIFT_RIGHT)}, { ">>", SYM(SHIFT_RIGHT)},
{ "<=>", SYM(EQUAL_SYM)}, { "<=>", SYM(EQUAL_SYM)},
{ "ACCESSIBLE", SYM(ACCESSIBLE_SYM)}, { "ACCESSIBLE", SYM(ACCESSIBLE_SYM)},
{ "ACCOUNT", SYM(ACCOUNT_SYM)},
{ "ACTION", SYM(ACTION)}, { "ACTION", SYM(ACTION)},
{ "ADD", SYM(ADD)}, { "ADD", SYM(ADD)},
{ "ADMIN", SYM(ADMIN_SYM)}, { "ADMIN", SYM(ADMIN_SYM)},
......
...@@ -7933,3 +7933,6 @@ ER_BACKUP_UNKNOWN_STAGE ...@@ -7933,3 +7933,6 @@ ER_BACKUP_UNKNOWN_STAGE
eng "Unknown backup stage: '%s'. Stage should be one of START, FLUSH, BLOCK_DDL, BLOCK_COMMIT or END" eng "Unknown backup stage: '%s'. Stage should be one of START, FLUSH, BLOCK_DDL, BLOCK_COMMIT or END"
ER_USER_IS_BLOCKED ER_USER_IS_BLOCKED
eng "User is blocked because of too many credential errors; unblock with 'FLUSH PRIVILEGES'" eng "User is blocked because of too many credential errors; unblock with 'FLUSH PRIVILEGES'"
ER_ACCOUNT_HAS_BEEN_LOCKED
eng "Access denied, this account is locked"
rum "Acces refuzat, acest cont este blocat"
...@@ -152,6 +152,7 @@ class ACL_USER :public ACL_USER_BASE ...@@ -152,6 +152,7 @@ class ACL_USER :public ACL_USER_BASE
LEX_CSTRING default_rolename; LEX_CSTRING default_rolename;
struct AUTH { LEX_CSTRING plugin, auth_string, salt; } *auth; struct AUTH { LEX_CSTRING plugin, auth_string, salt; } *auth;
uint nauth; uint nauth;
bool account_locked;
bool alloc_auth(MEM_ROOT *root, uint n) bool alloc_auth(MEM_ROOT *root, uint n)
{ {
...@@ -864,6 +865,8 @@ class User_table: public Grant_table_base ...@@ -864,6 +865,8 @@ class User_table: public Grant_table_base
virtual int set_is_role (bool x) const = 0; virtual int set_is_role (bool x) const = 0;
virtual const char* get_default_role (MEM_ROOT *root) const = 0; virtual const char* get_default_role (MEM_ROOT *root) const = 0;
virtual int set_default_role (const char *s, size_t l) const = 0; virtual int set_default_role (const char *s, size_t l) const = 0;
virtual bool get_account_locked () const = 0;
virtual int set_account_locked (bool x) const = 0;
virtual ~User_table() {} virtual ~User_table() {}
private: private:
...@@ -1123,7 +1126,22 @@ class User_table_tabular: public User_table ...@@ -1123,7 +1126,22 @@ class User_table_tabular: public User_table
return f->store(s, l, system_charset_info); return f->store(s, l, system_charset_info);
else else
return 1; return 1;
}; }
/* On a MariaDB 10.3 user table, the account locking accessors will try to
get the content of the max_statement_time column, but they will fail due
to the typecheck in get_field. */
bool get_account_locked () const
{
Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_ENUM);
return f ? f->val_int()-1 : 0;
}
int set_account_locked (bool x) const
{
if (Field *f= get_field(end_priv_columns + 13, MYSQL_TYPE_ENUM))
return f->store(x+1, 0);
else
return 1;
}
virtual ~User_table_tabular() {} virtual ~User_table_tabular() {}
private: private:
...@@ -1416,6 +1434,10 @@ class User_table_json: public User_table ...@@ -1416,6 +1434,10 @@ class User_table_json: public User_table
{ return get_str_value(root, "default_role"); } { return get_str_value(root, "default_role"); }
int set_default_role (const char *s, size_t l) const int set_default_role (const char *s, size_t l) const
{ return set_str_value("default_role", s, l); } { return set_str_value("default_role", s, l); }
bool get_account_locked () const
{ return get_bool_value("account_locked"); }
int set_account_locked (bool x) const
{ return set_bool_value("account_locked", x); }
~User_table_json() {} ~User_table_json() {}
private: private:
...@@ -2260,6 +2282,8 @@ static bool acl_load(THD *thd, const Grant_tables& tables) ...@@ -2260,6 +2282,8 @@ static bool acl_load(THD *thd, const Grant_tables& tables)
my_init_dynamic_array(&user.role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0)); my_init_dynamic_array(&user.role_grants, sizeof(ACL_ROLE *), 0, 8, MYF(0));
user.account_locked= user_table.get_account_locked();
if (is_role) if (is_role)
{ {
if (is_invalid_role_name(username)) if (is_invalid_role_name(username))
...@@ -4327,6 +4351,13 @@ static int replace_user_table(THD *thd, const User_table &user_table, ...@@ -4327,6 +4351,13 @@ static int replace_user_table(THD *thd, const User_table &user_table,
mqh_used= (mqh_used || lex->mqh.questions || lex->mqh.updates || mqh_used= (mqh_used || lex->mqh.questions || lex->mqh.updates ||
lex->mqh.conn_per_hour || lex->mqh.user_conn || lex->mqh.conn_per_hour || lex->mqh.user_conn ||
lex->mqh.max_statement_time != 0.0); lex->mqh.max_statement_time != 0.0);
if (lex->account_options.account_locked != ACCOUNTLOCK_UNSPECIFIED)
{
bool lock_value= lex->account_options.account_locked == ACCOUNTLOCK_LOCKED;
user_table.set_account_locked(lock_value);
new_acl_user.account_locked= lock_value;
}
} }
if (old_row_exists) if (old_row_exists)
...@@ -8780,6 +8811,9 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user) ...@@ -8780,6 +8811,9 @@ bool mysql_show_create_user(THD *thd, LEX_USER *lex_user)
add_user_parameters(&result, acl_user, false); add_user_parameters(&result, acl_user, false);
if (acl_user->account_locked)
result.append(STRING_WITH_LEN(" ACCOUNT LOCK"));
protocol->prepare_for_resend(); protocol->prepare_for_resend();
protocol->store(result.ptr(), result.length(), result.charset()); protocol->store(result.ptr(), result.length(), result.charset());
if (protocol->write()) if (protocol->write())
...@@ -13641,6 +13675,12 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len) ...@@ -13641,6 +13675,12 @@ bool acl_authenticate(THD *thd, uint com_change_user_pkt_len)
DBUG_RETURN(1); DBUG_RETURN(1);
} }
if (acl_user->account_locked) {
status_var_increment(denied_connections);
my_error(ER_ACCOUNT_HAS_BEEN_LOCKED, MYF(0));
DBUG_RETURN(1);
}
/* /*
Don't allow the user to connect if he has done too many queries. Don't allow the user to connect if he has done too many queries.
As we are testing max_user_connections == 0 here, it means that we As we are testing max_user_connections == 0 here, it means that we
......
...@@ -2939,6 +2939,27 @@ class Delete_plan : public Update_plan ...@@ -2939,6 +2939,27 @@ class Delete_plan : public Update_plan
Explain_delete* save_explain_delete_data(MEM_ROOT *mem_root, THD *thd); Explain_delete* save_explain_delete_data(MEM_ROOT *mem_root, THD *thd);
}; };
enum account_lock_type
{
ACCOUNTLOCK_UNSPECIFIED,
ACCOUNTLOCK_LOCKED,
ACCOUNTLOCK_UNLOCKED
};
struct Account_options
{
Account_options()
: account_locked(ACCOUNTLOCK_UNSPECIFIED)
{ }
void reset()
{
account_locked= ACCOUNTLOCK_UNSPECIFIED;
}
account_lock_type account_locked;
};
class Query_arena_memroot; class Query_arena_memroot;
/* The state of the lex parsing. This is saved in the THD struct */ /* The state of the lex parsing. This is saved in the THD struct */
...@@ -3030,6 +3051,9 @@ struct LEX: public Query_tables_list ...@@ -3030,6 +3051,9 @@ struct LEX: public Query_tables_list
*/ */
LEX_USER *definer; LEX_USER *definer;
/* Used in ALTER/CREATE user to store account locking options */
Account_options account_options;
Table_type table_type; /* Used for SHOW CREATE */ Table_type table_type; /* Used for SHOW CREATE */
List<Key_part_spec> ref_list; List<Key_part_spec> ref_list;
List<LEX_USER> users_list; List<LEX_USER> users_list;
......
...@@ -1151,6 +1151,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); ...@@ -1151,6 +1151,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
Non-reserved keywords Non-reserved keywords
*/ */
%token <kwd> ACCOUNT_SYM /* MYSQL */
%token <kwd> ACTION /* SQL-2003-N */ %token <kwd> ACTION /* SQL-2003-N */
%token <kwd> ADMIN_SYM /* SQL-2003-N */ %token <kwd> ADMIN_SYM /* SQL-2003-N */
%token <kwd> ADDDATE_SYM /* MYSQL-FUNC */ %token <kwd> ADDDATE_SYM /* MYSQL-FUNC */
...@@ -2911,7 +2912,7 @@ create: ...@@ -2911,7 +2912,7 @@ create:
Lex->pop_select(); //main select Lex->pop_select(); //main select
} }
| create_or_replace USER_SYM opt_if_not_exists clear_privileges | create_or_replace USER_SYM opt_if_not_exists clear_privileges
grant_list opt_require_clause opt_resource_options grant_list opt_require_clause opt_resource_options opt_account_locking
{ {
if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER, if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER,
$1 | $3))) $1 | $3)))
...@@ -3318,6 +3319,7 @@ clear_privileges: ...@@ -3318,6 +3319,7 @@ clear_privileges:
lex->ssl_type= SSL_TYPE_NOT_SPECIFIED; lex->ssl_type= SSL_TYPE_NOT_SPECIFIED;
lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0; lex->ssl_cipher= lex->x509_subject= lex->x509_issuer= 0;
bzero((char *)&(lex->mqh),sizeof(lex->mqh)); bzero((char *)&(lex->mqh),sizeof(lex->mqh));
lex->account_options.reset();
} }
; ;
...@@ -7979,7 +7981,7 @@ alter: ...@@ -7979,7 +7981,7 @@ alter:
} OPTIONS_SYM '(' server_options_list ')' { } } OPTIONS_SYM '(' server_options_list ')' { }
/* ALTER USER foo is allowed for MySQL compatibility. */ /* ALTER USER foo is allowed for MySQL compatibility. */
| ALTER opt_if_exists USER_SYM clear_privileges grant_list | ALTER opt_if_exists USER_SYM clear_privileges grant_list
opt_require_clause opt_resource_options opt_require_clause opt_resource_options opt_account_locking
{ {
Lex->create_info.set($2); Lex->create_info.set($2);
Lex->sql_command= SQLCOM_ALTER_USER; Lex->sql_command= SQLCOM_ALTER_USER;
...@@ -8018,6 +8020,18 @@ alter: ...@@ -8018,6 +8020,18 @@ alter:
} }
; ;
opt_account_locking:
/* Nothing */ {}
| ACCOUNT_SYM LOCK_SYM
{
Lex->account_options.account_locked= ACCOUNTLOCK_LOCKED;
}
| ACCOUNT_SYM UNLOCK_SYM
{
Lex->account_options.account_locked= ACCOUNTLOCK_UNLOCKED;
}
;
ev_alter_on_schedule_completion: ev_alter_on_schedule_completion:
/* empty */ { $$= 0;} /* empty */ { $$= 0;}
| ON SCHEDULE_SYM ev_schedule_time { $$= 1; } | ON SCHEDULE_SYM ev_schedule_time { $$= 1; }
...@@ -15855,6 +15869,7 @@ keyword_data_type: ...@@ -15855,6 +15869,7 @@ keyword_data_type:
*/ */
keyword_sp_var_and_label: keyword_sp_var_and_label:
ACTION ACTION
| ACCOUNT_SYM
| ADDDATE_SYM | ADDDATE_SYM
| ADMIN_SYM | ADMIN_SYM
| AFTER_SYM | AFTER_SYM
......
...@@ -646,6 +646,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); ...@@ -646,6 +646,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize);
Non-reserved keywords Non-reserved keywords
*/ */
%token <kwd> ACCOUNT_SYM /* MYSQL */
%token <kwd> ACTION /* SQL-2003-N */ %token <kwd> ACTION /* SQL-2003-N */
%token <kwd> ADMIN_SYM /* SQL-2003-N */ %token <kwd> ADMIN_SYM /* SQL-2003-N */
%token <kwd> ADDDATE_SYM /* MYSQL-FUNC */ %token <kwd> ADDDATE_SYM /* MYSQL-FUNC */
...@@ -2417,7 +2418,7 @@ create: ...@@ -2417,7 +2418,7 @@ create:
Lex->pop_select(); //main select Lex->pop_select(); //main select
} }
| create_or_replace USER_SYM opt_if_not_exists clear_privileges | create_or_replace USER_SYM opt_if_not_exists clear_privileges
grant_list opt_require_clause opt_resource_options grant_list opt_require_clause opt_resource_options opt_account_locking
{ {
if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER, if (unlikely(Lex->set_command_with_check(SQLCOM_CREATE_USER,
$1 | $3))) $1 | $3)))
...@@ -8009,7 +8010,7 @@ alter: ...@@ -8009,7 +8010,7 @@ alter:
} OPTIONS_SYM '(' server_options_list ')' { } } OPTIONS_SYM '(' server_options_list ')' { }
/* ALTER USER foo is allowed for MySQL compatibility. */ /* ALTER USER foo is allowed for MySQL compatibility. */
| ALTER opt_if_exists USER_SYM clear_privileges grant_list | ALTER opt_if_exists USER_SYM clear_privileges grant_list
opt_require_clause opt_resource_options opt_require_clause opt_resource_options opt_account_locking
{ {
Lex->create_info.set($2); Lex->create_info.set($2);
Lex->sql_command= SQLCOM_ALTER_USER; Lex->sql_command= SQLCOM_ALTER_USER;
...@@ -8048,6 +8049,18 @@ alter: ...@@ -8048,6 +8049,18 @@ alter:
} }
; ;
opt_account_locking:
/* Nothing */ {}
| ACCOUNT_SYM LOCK_SYM
{
Lex->account_options.account_locked= ACCOUNTLOCK_LOCKED;
}
| ACCOUNT_SYM UNLOCK_SYM
{
Lex->account_options.account_locked= ACCOUNTLOCK_UNLOCKED;
}
;
ev_alter_on_schedule_completion: ev_alter_on_schedule_completion:
/* empty */ { $$= 0;} /* empty */ { $$= 0;}
| ON SCHEDULE_SYM ev_schedule_time { $$= 1; } | ON SCHEDULE_SYM ev_schedule_time { $$= 1; }
...@@ -15943,6 +15956,7 @@ keyword_data_type: ...@@ -15943,6 +15956,7 @@ keyword_data_type:
*/ */
keyword_sp_var_and_label: keyword_sp_var_and_label:
ACTION ACTION
| ACCOUNT_SYM
| ADDDATE_SYM | ADDDATE_SYM
| ADMIN_SYM | ADMIN_SYM
| AFTER_SYM | AFTER_SYM
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment