Commit a68e91cc authored by monty@hundin.mysql.fi's avatar monty@hundin.mysql.fi

Merge work:/home/bk/mysql-4.0 into hundin.mysql.fi:/my/bk/mysql-4.0

parents 48067f3c 17bdd9d6
...@@ -36200,15 +36200,6 @@ SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL ...@@ -36200,15 +36200,6 @@ SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL
Sets the transaction isolation level for the global, whole session or Sets the transaction isolation level for the global, whole session or
the next transaction. the next transaction.
The default behavior is to set the isolation level for the next (not
started) transaction. If you use the @code{GLOBAL} keyword, the statement
sets the default transaction level globally for all new connections
created from that point on. You will need the @strong{process}
privilege to do do this. Using the @code{SESSION} keyword sets the
default transaction level for all future transactions performed on the
current connection.
You can set the default global isolation level for @code{mysqld} with
@code{--transaction-isolation=...}. @xref{Command-line options}. @code{--transaction-isolation=...}. @xref{Command-line options}.
@node Fulltext Search, Query Cache, Transactional Commands, Reference @node Fulltext Search, Query Cache, Transactional Commands, Reference
...@@ -49224,6 +49215,75 @@ Our TODO section contains what we plan to have in 4.0. @xref{TODO MySQL 4.0}. ...@@ -49224,6 +49215,75 @@ Our TODO section contains what we plan to have in 4.0. @xref{TODO MySQL 4.0}.
@itemize @bullet @itemize @bullet
@item @item
Fixed bug in DROP DATABASE with symlink
@item
Fixed bug in EXPLAIN with LIMIT offset != 0
@item
New feature :
Management of user resources
So far, the only available method of limiting user usage of MySQL
server resources has been setting max_user_connections startup
variable to some non-zero value at MySQL startup. But this method is
strictly a global one and does not allow management of individual
users, which could be of paricular interest to Interent Service
Providers.
Therefore, management of three resources is introduced on the
individual user level :
* number of all queries per hour
* number of all updates per hour
* number of connections made per hour
Small clarification : By the updates in the above sense is considered
any command that changes any table or database. Queries in the above
context comprehend all commands that could be run by user. User in the
above context comprehends a single entry in user table, which is
uniquely identified by user and host columns.
All users are by default not limited in using the above resources,
unless the limits are GRANTed to them. These limits can be granted
ONLY by global GRANT (*.*) and with a following syntax :
GRANT ... WITH MAX_QUERIES_PER_HOUR = N1 MAX_UPDATES_PER_HOUR = N2
MAX_CONNECTIONS_PER_HOUR = N3;
It is not required that all three resources are specified. One or two
can be specified also. N1,N2 and N3 are intergers and should limit
number of times user can execute any command, update command or can
login that many times per hour.
If user reaches any of the above limits withing one hour, his
connection will be broken or refused and the appropriate error message
shall be issued.
Current values of particular user resources can be flushed (set to
zero) by issuing a grant statement with any of the above limiting
clauses, including a GRANT statement with current value(s) of tha
resource(s).
Also, current values for all users will be flushed if privileges are
reloaded or if a new flush command is issued :
flush user_resources.
Also, current values for all users will be flushed with mysqladmin
reload command.
This new feature is enabled as soon as single user is GRANTed with
some of the limiting GRANT clauses.
As a prerequisite for enabling this features, user table in mysql
database must have the additional columns, just as defined in table
creation scripts mysql_install_db and mysql_install_db.sh in scripts/
directory.
@item
New configure option --without-query-cache.
@item
Fixed bug in phrase operator @code{"..."} in boolean full-text search. Fixed bug in phrase operator @code{"..."} in boolean full-text search.
@item @item
Fixed bug that caused duplicated rows when using truncation operator Fixed bug that caused duplicated rows when using truncation operator
...@@ -81,6 +81,7 @@ enum enum_server_command {COM_SLEEP,COM_QUIT,COM_INIT_DB,COM_QUERY, ...@@ -81,6 +81,7 @@ enum enum_server_command {COM_SLEEP,COM_QUIT,COM_INIT_DB,COM_QUERY,
#define REFRESH_QUERY_CACHE 65536 #define REFRESH_QUERY_CACHE 65536
#define REFRESH_QUERY_CACHE_FREE 0x20000L /* pack query cache */ #define REFRESH_QUERY_CACHE_FREE 0x20000L /* pack query cache */
#define REFRESH_DES_KEY_FILE 0x40000L #define REFRESH_DES_KEY_FILE 0x40000L
#define REFRESH_USER_RESOURCES 0x80000L
#define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */ #define CLIENT_LONG_PASSWORD 1 /* new more secure passwords */
#define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */ #define CLIENT_FOUND_ROWS 2 /* Found instead of affected rows */
......
...@@ -229,6 +229,8 @@ static SYMBOL symbols[] = { ...@@ -229,6 +229,8 @@ static SYMBOL symbols[] = {
{ "MASTER_USER", SYM(MASTER_USER_SYM),0,0}, { "MASTER_USER", SYM(MASTER_USER_SYM),0,0},
{ "MAX_ROWS", SYM(MAX_ROWS),0,0}, { "MAX_ROWS", SYM(MAX_ROWS),0,0},
{ "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR), 0,0}, { "MAX_QUERIES_PER_HOUR", SYM(MAX_QUERIES_PER_HOUR), 0,0},
{ "MAX_UPDATES_PER_HOUR", SYM(MAX_UPDATES_PER_HOUR), 0,0},
{ "MAX_CONNECTIONS_PER_HOUR", SYM(MAX_CONNECTIONS_PER_HOUR), 0,0},
{ "MATCH", SYM(MATCH),0,0}, { "MATCH", SYM(MATCH),0,0},
{ "MEDIUMBLOB", SYM(MEDIUMBLOB),0,0}, { "MEDIUMBLOB", SYM(MEDIUMBLOB),0,0},
{ "MEDIUMTEXT", SYM(MEDIUMTEXT),0,0}, { "MEDIUMTEXT", SYM(MEDIUMTEXT),0,0},
...@@ -290,6 +292,7 @@ static SYMBOL symbols[] = { ...@@ -290,6 +292,7 @@ static SYMBOL symbols[] = {
{ "REPEATABLE", SYM(REPEATABLE_SYM),0,0}, { "REPEATABLE", SYM(REPEATABLE_SYM),0,0},
{ "REQUIRE", SYM(REQUIRE_SYM),0,0}, { "REQUIRE", SYM(REQUIRE_SYM),0,0},
{ "RESET", SYM(RESET_SYM),0,0}, { "RESET", SYM(RESET_SYM),0,0},
{ "USER_RESOURCES", SYM(RESOURCES),0,0},
{ "RESTORE", SYM(RESTORE_SYM),0,0}, { "RESTORE", SYM(RESTORE_SYM),0,0},
{ "RESTRICT", SYM(RESTRICT),0,0}, { "RESTRICT", SYM(RESTRICT),0,0},
{ "RETURNS", SYM(UDF_RETURNS_SYM),0,0}, { "RETURNS", SYM(UDF_RETURNS_SYM),0,0},
......
...@@ -58,7 +58,8 @@ class ACL_USER :public ACL_ACCESS ...@@ -58,7 +58,8 @@ class ACL_USER :public ACL_ACCESS
{ {
public: public:
acl_host_and_ip host; acl_host_and_ip host;
uint hostname_length, questions, updates; uint hostname_length;
USER_RESOURCES user_resource;
char *user,*password; char *user,*password;
ulong salt[2]; ulong salt[2];
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
...@@ -110,6 +111,32 @@ static void update_hostname(acl_host_and_ip *host, const char *hostname); ...@@ -110,6 +111,32 @@ static void update_hostname(acl_host_and_ip *host, const char *hostname);
static bool compare_hostname(const acl_host_and_ip *host, const char *hostname, static bool compare_hostname(const acl_host_and_ip *host, const char *hostname,
const char *ip); const char *ip);
extern char uc_update_queries[SQLCOM_END];
static void init_update_queries(void)
{
uc_update_queries[SQLCOM_CREATE_TABLE]=1;
uc_update_queries[SQLCOM_CREATE_INDEX]=1;
uc_update_queries[SQLCOM_ALTER_TABLE]=1;
uc_update_queries[SQLCOM_UPDATE]=1;
uc_update_queries[SQLCOM_INSERT]=1;
uc_update_queries[SQLCOM_INSERT_SELECT]=1;
uc_update_queries[SQLCOM_DELETE]=1;
uc_update_queries[SQLCOM_TRUNCATE]=1;
uc_update_queries[SQLCOM_DROP_TABLE]=1;
uc_update_queries[SQLCOM_LOAD]=1;
uc_update_queries[SQLCOM_CREATE_DB]=1;
uc_update_queries[SQLCOM_DROP_DB]=1;
uc_update_queries[SQLCOM_REPLACE]=1;
uc_update_queries[SQLCOM_REPLACE_SELECT]=1;
uc_update_queries[SQLCOM_RENAME_TABLE]=1;
uc_update_queries[SQLCOM_BACKUP_TABLE]=1;
uc_update_queries[SQLCOM_RESTORE_TABLE]=1;
uc_update_queries[SQLCOM_DELETE_MULTI]=1;
uc_update_queries[SQLCOM_DROP_INDEX]=1;
uc_update_queries[SQLCOM_MULTI_UPDATE]=1;
}
int acl_init(bool dont_read_acl_tables) int acl_init(bool dont_read_acl_tables)
{ {
THD *thd; THD *thd;
...@@ -247,14 +274,16 @@ int acl_init(bool dont_read_acl_tables) ...@@ -247,14 +274,16 @@ int acl_init(bool dont_read_acl_tables)
{ {
/* Table has new MySQL usage limits */ /* Table has new MySQL usage limits */
char *ptr = get_field(&mem, table, 21); char *ptr = get_field(&mem, table, 21);
user.questions=atoi(ptr); user.user_resource.questions=atoi(ptr);
ptr = get_field(&mem, table, 22); ptr = get_field(&mem, table, 22);
user.updates=atoi(ptr); user.user_resource.updates=atoi(ptr);
if (user.questions) ptr = get_field(&mem, table, 23);
user.user_resource.connections=atoi(ptr);
if (user.user_resource.questions || user.user_resource.updates || user.user_resource.connections)
mqh_used=1; mqh_used=1;
} }
else else
user.questions=user.updates=0; bzero(&(user.user_resource),sizeof(user.user_resource));
#ifndef TO_BE_REMOVED #ifndef TO_BE_REMOVED
if (table->fields <= 13) if (table->fields <= 13)
{ // Without grant { // Without grant
...@@ -299,6 +328,7 @@ int acl_init(bool dont_read_acl_tables) ...@@ -299,6 +328,7 @@ int acl_init(bool dont_read_acl_tables)
init_check_host(); init_check_host();
mysql_unlock_tables(thd, lock); mysql_unlock_tables(thd, lock);
init_update_queries();
thd->version--; // Force close to free memory thd->version--; // Force close to free memory
close_thread_tables(thd); close_thread_tables(thd);
delete thd; delete thd;
...@@ -442,13 +472,13 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b) ...@@ -442,13 +472,13 @@ static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b)
uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user, uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
const char *password,const char *message,char **priv_user, const char *password,const char *message,char **priv_user,
bool old_ver, uint *max_questions) bool old_ver, USER_RESOURCES *mqh)
{ {
uint user_access=NO_ACCESS; uint user_access=NO_ACCESS;
*priv_user=(char*) user; *priv_user=(char*) user;
char *ptr=0; char *ptr=0;
*max_questions=0; bzero(mqh,sizeof(USER_RESOURCES));
if (!initialized) if (!initialized)
return (uint) ~NO_ACCESS; // If no data allow anything /* purecov: tested */ return (uint) ~NO_ACCESS; // If no data allow anything /* purecov: tested */
VOID(pthread_mutex_lock(&acl_cache->lock)); VOID(pthread_mutex_lock(&acl_cache->lock));
...@@ -556,7 +586,7 @@ uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user, ...@@ -556,7 +586,7 @@ uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
#else /* HAVE_OPENSSL */ #else /* HAVE_OPENSSL */
user_access=acl_user->access; user_access=acl_user->access;
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
*max_questions=acl_user->questions; *mqh=acl_user->user_resource;
if (!acl_user->user) if (!acl_user->user)
*priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */ *priv_user=(char*) ""; // Change to anonymous user /* purecov: inspected */
break; break;
...@@ -590,7 +620,7 @@ static void acl_update_user(const char *user, const char *host, ...@@ -590,7 +620,7 @@ static void acl_update_user(const char *user, const char *host,
const char *ssl_cipher, const char *ssl_cipher,
const char *x509_issuer, const char *x509_issuer,
const char *x509_subject, const char *x509_subject,
unsigned int mqh, USER_RESOURCES *mqh,
uint privileges) uint privileges)
{ {
for (uint i=0 ; i < acl_users.elements ; i++) for (uint i=0 ; i < acl_users.elements ; i++)
...@@ -604,7 +634,7 @@ static void acl_update_user(const char *user, const char *host, ...@@ -604,7 +634,7 @@ static void acl_update_user(const char *user, const char *host,
acl_user->host.hostname && !strcmp(host,acl_user->host.hostname)) acl_user->host.hostname && !strcmp(host,acl_user->host.hostname))
{ {
acl_user->access=privileges; acl_user->access=privileges;
acl_user->questions=mqh; acl_user->user_resource=*mqh;
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
acl_user->ssl_type=ssl_type; acl_user->ssl_type=ssl_type;
acl_user->ssl_cipher=ssl_cipher; acl_user->ssl_cipher=ssl_cipher;
...@@ -634,7 +664,7 @@ static void acl_insert_user(const char *user, const char *host, ...@@ -634,7 +664,7 @@ static void acl_insert_user(const char *user, const char *host,
const char *ssl_cipher, const char *ssl_cipher,
const char *x509_issuer, const char *x509_issuer,
const char *x509_subject, const char *x509_subject,
unsigned int mqh, USER_RESOURCES *mqh,
uint privileges) uint privileges)
{ {
ACL_USER acl_user; ACL_USER acl_user;
...@@ -642,7 +672,7 @@ static void acl_insert_user(const char *user, const char *host, ...@@ -642,7 +672,7 @@ static void acl_insert_user(const char *user, const char *host,
update_hostname(&acl_user.host,strdup_root(&mem,host)); update_hostname(&acl_user.host,strdup_root(&mem,host));
acl_user.password=0; acl_user.password=0;
acl_user.access=privileges; acl_user.access=privileges;
acl_user.questions=mqh; acl_user.user_resource = *mqh;
acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user); acl_user.sort=get_sort(2,acl_user.host.hostname,acl_user.user);
acl_user.hostname_length=(uint) strlen(acl_user.host.hostname); acl_user.hostname_length=(uint) strlen(acl_user.host.hostname);
#ifdef HAVE_OPENSSL #ifdef HAVE_OPENSSL
...@@ -1151,7 +1181,14 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, ...@@ -1151,7 +1181,14 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
DBUG_ENTER("replace_user_table"); DBUG_ENTER("replace_user_table");
if (combo.password.str && combo.password.str[0]) if (combo.password.str && combo.password.str[0])
{
if (combo.password.length <= HASH_PASSWORD_LENGTH)
{
send_error(&thd->net, ER_PASSWORD_NO_MATCH);
DBUG_RETURN(1);
}
password=combo.password.str; password=combo.password.str;
}
else else
{ {
password=empty_string; password=empty_string;
...@@ -1233,10 +1270,16 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo, ...@@ -1233,10 +1270,16 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
} }
} }
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
if (table->fields >= 23 && thd->lex.mqh) if (table->fields >= 23)
{ {
table->field[21]->store((longlong) thd->lex.mqh); USER_RESOURCES mqh = thd->lex.mqh;
mqh_used=1; if (mqh.questions)
table->field[21]->store((longlong) mqh.questions);
if (mqh.updates)
table->field[22]->store((longlong) mqh.updates);
if (mqh.connections)
table->field[23]->store((longlong) mqh.connections);
mqh_used = mqh_used || mqh.questions || mqh.updates || mqh.connections;
} }
if (old_row_exists) if (old_row_exists)
{ {
...@@ -1276,7 +1319,7 @@ end: ...@@ -1276,7 +1319,7 @@ end:
thd->lex.ssl_cipher, thd->lex.ssl_cipher,
thd->lex.x509_issuer, thd->lex.x509_issuer,
thd->lex.x509_subject, thd->lex.x509_subject,
thd->lex.mqh, &thd->lex.mqh,
rights); rights);
else else
acl_insert_user(combo.user.str,combo.host.str,password, acl_insert_user(combo.user.str,combo.host.str,password,
...@@ -1284,7 +1327,7 @@ end: ...@@ -1284,7 +1327,7 @@ end:
thd->lex.ssl_cipher, thd->lex.ssl_cipher,
thd->lex.x509_issuer, thd->lex.x509_issuer,
thd->lex.x509_subject, thd->lex.x509_subject,
thd->lex.mqh, &thd->lex.mqh,
rights); rights);
} }
table->file->index_end(); table->file->index_end();
...@@ -2691,11 +2734,25 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) ...@@ -2691,11 +2734,25 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
#endif /* HAVE_OPENSSL */ #endif /* HAVE_OPENSSL */
if (want_access & GRANT_ACL) if (want_access & GRANT_ACL)
global.append(" WITH GRANT OPTION",18); global.append(" WITH GRANT OPTION",18);
else if (acl_user->questions) if (acl_user->user_resource.questions)
{ {
char buff[65], *p; // just as in int2str char buff[65], *p; // just as in int2str
global.append(" WITH MAX_QUERIES_PER_HOUR = ",29); global.append(" WITH MAX_QUERIES_PER_HOUR = ",29);
p=int2str(acl_user->questions,buff,10); p=int2str(acl_user->user_resource.questions,buff,10);
global.append(buff,p-buff);
}
if (acl_user->user_resource.updates)
{
char buff[65], *p; // just as in int2str
global.append(" WITH MAX_UPDATES_PER_HOUR = ",29);
p=int2str(acl_user->user_resource.updates,buff,10);
global.append(buff,p-buff);
}
if (acl_user->user_resource.connections)
{
char buff[65], *p; // just as in int2str
global.append(" WITH MAX_CONNECTIONS_PER_HOUR = ",33);
p=int2str(acl_user->user_resource.connections,buff,10);
global.append(buff,p-buff); global.append(buff,p-buff);
} }
thd->packet.length(0); thd->packet.length(0);
...@@ -2860,16 +2917,17 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user) ...@@ -2860,16 +2917,17 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
} }
uint get_mqh(const char *user, const char *host) void get_mqh(const char *user, const char *host, USER_CONN *uc)
{ {
if (!initialized) return 0;
ACL_USER *acl_user; ACL_USER *acl_user;
acl_user= find_acl_user(host,user); if (initialized && (acl_user= find_acl_user(host,user)))
return (acl_user) ? acl_user->questions : 0; uc->user_resources= acl_user->user_resource;
else
bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
} }
/***************************************************************************** /*****************************************************************************
** Instantiate used templates ** Instantiate used templates
*****************************************************************************/ *****************************************************************************/
......
...@@ -61,7 +61,7 @@ uint acl_get(const char *host, const char *ip, const char *bin_ip, ...@@ -61,7 +61,7 @@ uint acl_get(const char *host, const char *ip, const char *bin_ip,
const char *user, const char *db); const char *user, const char *db);
uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user, uint acl_getroot(THD *thd, const char *host, const char *ip, const char *user,
const char *password,const char *scramble,char **priv_user, const char *password,const char *scramble,char **priv_user,
bool old_ver, uint *max); bool old_ver, USER_RESOURCES *max);
bool acl_check_host(const char *host, const char *ip); bool acl_check_host(const char *host, const char *ip);
bool change_password(THD *thd, const char *host, const char *user, bool change_password(THD *thd, const char *host, const char *user,
char *password); char *password);
...@@ -82,4 +82,4 @@ bool check_grant_db(THD *thd,const char *db); ...@@ -82,4 +82,4 @@ bool check_grant_db(THD *thd,const char *db);
uint get_table_grant(THD *thd, TABLE_LIST *table); uint get_table_grant(THD *thd, TABLE_LIST *table);
uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field); uint get_column_grant(THD *thd, TABLE_LIST *table, Field *field);
int mysql_show_grants(THD *thd, LEX_USER *user); int mysql_show_grants(THD *thd, LEX_USER *user);
uint get_mqh(const char *user, const char *host); void get_mqh(const char *user, const char *host, USER_CONN *uc);
...@@ -147,7 +147,7 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0), ...@@ -147,7 +147,7 @@ THD::THD():user_time(0),fatal_error(0),last_insert_id_used(0),
/* Initialize sub structures */ /* Initialize sub structures */
bzero((char*) &mem_root,sizeof(mem_root)); bzero((char*) &mem_root,sizeof(mem_root));
bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root)); bzero((char*) &transaction.mem_root,sizeof(transaction.mem_root));
user_connect=(UC *)0; user_connect=(USER_CONN *)0;
hash_init(&user_vars, USER_VARS_HASH_SIZE, 0, 0, hash_init(&user_vars, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key, (hash_get_key) get_var_key,
(void (*)(void*)) free_var,0); (void (*)(void*)) free_var,0);
......
...@@ -382,7 +382,7 @@ public: ...@@ -382,7 +382,7 @@ public:
ha_rows select_limit,offset_limit,default_select_limit,cuted_fields, ha_rows select_limit,offset_limit,default_select_limit,cuted_fields,
max_join_size, sent_row_count, examined_row_count; max_join_size, sent_row_count, examined_row_count;
table_map used_tables; table_map used_tables;
UC *user_connect; USER_CONN *user_connect;
ulong query_id,version, inactive_timeout,options,thread_id; ulong query_id,version, inactive_timeout,options,thread_id;
long dbug_thread_id; long dbug_thread_id;
pthread_t real_id; pthread_t real_id;
......
...@@ -283,13 +283,20 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, ...@@ -283,13 +283,20 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
*/ */
if (!found_other_files) if (!found_other_files)
{ {
char tmp_path[FN_REFLEN]; char tmp_path[FN_REFLEN], *pos;
char *path=unpack_filename(tmp_path,org_path); char *path=unpack_filename(tmp_path,org_path);
#ifdef HAVE_READLINK #ifdef HAVE_READLINK
int linkcount = readlink(path,filePath,sizeof(filePath)-1); int error;
if (linkcount > 0) // If the path was a symbolic link
/* Remove end FN_LIBCHAR as this causes problem on Linux in readlink */
pos=strend(path);
if (pos > path && pos[-1] == FN_LIBCHAR)
*--pos=0;
if ((error=my_readlink(filePath, path, MYF(MY_WME))) < 0)
DBUG_RETURN(-1);
if (!error)
{ {
*(filePath + linkcount) = '\0';
if (my_delete(path,MYF(!level ? MY_WME : 0))) if (my_delete(path,MYF(!level ? MY_WME : 0)))
{ {
/* Don't give errors if we can't delete 'RAID' directory */ /* Don't give errors if we can't delete 'RAID' directory */
...@@ -297,11 +304,12 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db, ...@@ -297,11 +304,12 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
DBUG_RETURN(deleted); DBUG_RETURN(deleted);
DBUG_RETURN(-1); DBUG_RETURN(-1);
} }
path=filePath; /* Delete directory symbolic link pointed at */
path= filePath;
} }
#endif #endif
/* Remove last FN_LIBCHAR to not cause a problem on OS/2 */ /* Remove last FN_LIBCHAR to not cause a problem on OS/2 */
char *pos=strend(path); pos=strend(path);
if (pos > path && pos[-1] == FN_LIBCHAR) if (pos > path && pos[-1] == FN_LIBCHAR)
*--pos=0; *--pos=0;
/* Don't give errors if we can't delete 'RAID' directory */ /* Don't give errors if we can't delete 'RAID' directory */
......
...@@ -176,6 +176,7 @@ typedef struct st_lex { ...@@ -176,6 +176,7 @@ typedef struct st_lex {
HA_CHECK_OPT check_opt; // check/repair options HA_CHECK_OPT check_opt; // check/repair options
HA_CREATE_INFO create_info; HA_CREATE_INFO create_info;
LEX_MASTER_INFO mi; // used by CHANGE MASTER LEX_MASTER_INFO mi; // used by CHANGE MASTER
USER_RESOURCES mqh;
ulong thread_id,type; ulong thread_id,type;
enum_sql_command sql_command; enum_sql_command sql_command;
enum lex_states next_state; enum lex_states next_state;
...@@ -184,7 +185,7 @@ typedef struct st_lex { ...@@ -184,7 +185,7 @@ typedef struct st_lex {
enum enum_ha_read_modes ha_read_mode; enum enum_ha_read_modes ha_read_mode;
enum ha_rkey_function ha_rkey_mode; enum ha_rkey_function ha_rkey_mode;
enum enum_enable_or_disable alter_keys_onoff; enum enum_enable_or_disable alter_keys_onoff;
uint grant,grant_tot_col,which_columns, union_option, mqh; uint grant,grant_tot_col,which_columns, union_option;
thr_lock_type lock_option; thr_lock_type lock_option;
bool drop_primary,drop_if_exists,local_file; bool drop_primary,drop_if_exists,local_file;
bool in_comment,ignore_space,verbose,simple_alter, option_type; bool in_comment,ignore_space,verbose,simple_alter, option_type;
......
...@@ -59,9 +59,9 @@ extern "C" pthread_mutex_t THR_LOCK_keycache; ...@@ -59,9 +59,9 @@ extern "C" pthread_mutex_t THR_LOCK_keycache;
extern "C" int gethostname(char *name, int namelen); extern "C" int gethostname(char *name, int namelen);
#endif #endif
static int check_for_max_user_connections(UC *uc); static int check_for_max_user_connections(USER_CONN *uc);
static bool check_mqh(THD *thd); static bool check_mqh(THD *thd);
static void decrease_user_connections(UC *uc); static void decrease_user_connections(USER_CONN *uc);
static bool check_db_used(THD *thd,TABLE_LIST *tables); static bool check_db_used(THD *thd,TABLE_LIST *tables);
static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables);
static bool check_dup(const char *db, const char *name, TABLE_LIST *tables); static bool check_dup(const char *db, const char *name, TABLE_LIST *tables);
...@@ -126,18 +126,19 @@ extern pthread_mutex_t LOCK_user_conn; ...@@ -126,18 +126,19 @@ extern pthread_mutex_t LOCK_user_conn;
static int get_or_create_user_conn(THD *thd, const char *user, static int get_or_create_user_conn(THD *thd, const char *user,
const char *host, const char *host,
uint max_questions) USER_RESOURCES *mqh)
{ {
int return_val=0; int return_val=0;
uint temp_len; uint temp_len, user_len, host_len;
char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
struct user_conn *uc; struct user_conn *uc;
DBUG_ASSERT(user != 0); DBUG_ASSERT(user != 0);
DBUG_ASSERT(host != 0); DBUG_ASSERT(host != 0);
temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user)-1, user, "@", host, user_len=strlen(user);
NullS) - temp_user); host_len=strlen(host);
temp_len= (strmov(strmov(temp_user, user)+1, host) - temp_user)+1;
(void) pthread_mutex_lock(&LOCK_user_conn); (void) pthread_mutex_lock(&LOCK_user_conn);
if (!(uc = (struct user_conn *) hash_search(&hash_user_connections, if (!(uc = (struct user_conn *) hash_search(&hash_user_connections,
(byte*) temp_user, temp_len))) (byte*) temp_user, temp_len)))
...@@ -153,10 +154,14 @@ static int get_or_create_user_conn(THD *thd, const char *user, ...@@ -153,10 +154,14 @@ static int get_or_create_user_conn(THD *thd, const char *user,
} }
uc->user=(char*) (uc+1); uc->user=(char*) (uc+1);
memcpy(uc->user,temp_user,temp_len+1); memcpy(uc->user,temp_user,temp_len+1);
uc->user_len= user_len;
uc->host=uc->user + uc->user_len + 1;
uc->len = temp_len; uc->len = temp_len;
uc->connections = 1; uc->connections = 1;
uc->questions=0; uc->questions=uc->updates=uc->conn_per_hour=0;
uc->max_questions=max_questions; uc->user_resources=*mqh;
if (mqh->connections > max_user_connections)
uc->user_resources.connections = max_user_connections;
uc->intime=thd->thr_create_time; uc->intime=thd->thr_create_time;
if (hash_insert(&hash_user_connections, (byte*) uc)) if (hash_insert(&hash_user_connections, (byte*) uc))
{ {
...@@ -184,9 +189,9 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, ...@@ -184,9 +189,9 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
const char *passwd, const char *db, bool check_count) const char *passwd, const char *db, bool check_count)
{ {
NET *net= &thd->net; NET *net= &thd->net;
uint max_questions=0;
thd->db=0; thd->db=0;
thd->db_length=0; thd->db_length=0;
USER_RESOURCES ur;
if (!(thd->user = my_strdup(user, MYF(0)))) if (!(thd->user = my_strdup(user, MYF(0))))
{ {
...@@ -197,7 +202,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, ...@@ -197,7 +202,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
passwd, thd->scramble, &thd->priv_user, passwd, thd->scramble, &thd->priv_user,
protocol_version == 9 || protocol_version == 9 ||
!(thd->client_capabilities & !(thd->client_capabilities &
CLIENT_LONG_PASSWORD),&max_questions); CLIENT_LONG_PASSWORD),&ur);
DBUG_PRINT("info", DBUG_PRINT("info",
("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'", ("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'",
thd->client_capabilities, thd->max_packet_length, thd->client_capabilities, thd->max_packet_length,
...@@ -237,9 +242,9 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, ...@@ -237,9 +242,9 @@ static bool check_user(THD *thd,enum_server_command command, const char *user,
db ? db : (char*) ""); db ? db : (char*) "");
thd->db_access=0; thd->db_access=0;
/* Don't allow user to connect if he has done too many queries */ /* Don't allow user to connect if he has done too many queries */
if ((max_questions || max_user_connections) && get_or_create_user_conn(thd,user,thd->host_or_ip,max_questions)) if ((ur.questions || ur.updates || ur.connections) && get_or_create_user_conn(thd,user,thd->host_or_ip,&ur))
return -1; return -1;
if (max_user_connections && thd->user_connect && if (thd->user_connect && thd->user_connect->user_resources.connections &&
check_for_max_user_connections(thd->user_connect)) check_for_max_user_connections(thd->user_connect))
return -1; return -1;
if (db && db[0]) if (db && db[0])
...@@ -279,32 +284,43 @@ void init_max_user_conn(void) ...@@ -279,32 +284,43 @@ void init_max_user_conn(void)
} }
static int check_for_max_user_connections(UC *uc) static int check_for_max_user_connections(USER_CONN *uc)
{ {
int error=0; int error=0;
DBUG_ENTER("check_for_max_user_connections"); DBUG_ENTER("check_for_max_user_connections");
if (max_user_connections <= (uint) uc->connections) if (max_user_connections && ( max_user_connections <= (uint) uc->connections))
{ {
net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, uc->user); net_printf(&(current_thd->net),ER_TOO_MANY_USER_CONNECTIONS, uc->user);
error=1; error=1;
goto end; goto end;
} }
uc->connections++; uc->connections++;
if (uc->user_resources.connections && uc->conn_per_hour++ >= uc->user_resources.connections)
{
net_printf(&current_thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_connections",
(long) uc->user_resources.connections);
error=1;
goto end;
}
end: end:
DBUG_RETURN(error); DBUG_RETURN(error);
} }
static void decrease_user_connections(UC *uc) static void decrease_user_connections(USER_CONN *uc)
{ {
if (!max_user_connections) /* if (!max_user_connections)
return; return;
*/
DBUG_ENTER("decrease_user_connections"); DBUG_ENTER("decrease_user_connections");
if (mqh_used)
if (!--uc->connections && !mqh_used) {
if (uc->conn_per_hour)
uc->conn_per_hour--;
}
else if (!--uc->connections)
{ {
/* Last connection for user; Delete it */ /* Last connection for user; Delete it */
(void) pthread_mutex_lock(&LOCK_user_conn); (void) pthread_mutex_lock(&LOCK_user_conn);
...@@ -325,70 +341,92 @@ void free_max_user_conn(void) ...@@ -325,70 +341,92 @@ void free_max_user_conn(void)
Check if maximum queries per hour limit has been reached Check if maximum queries per hour limit has been reached
returns 0 if OK. returns 0 if OK.
In theory we would need a mutex in the UC structure for this to be 100 % In theory we would need a mutex in the USER_CONN structure for this to be 100 %
safe, but as the worst scenario is that we would miss counting a couple of safe, but as the worst scenario is that we would miss counting a couple of
queries, this isn't critical. queries, this isn't critical.
*/ */
char uc_update_queries[SQLCOM_END];
static bool check_mqh(THD *thd) static bool check_mqh(THD *thd)
{ {
bool error=0; bool error=0;
DBUG_ENTER("check_mqh"); DBUG_ENTER("check_mqh");
UC *uc=thd->user_connect; USER_CONN *uc=thd->user_connect;
DBUG_ASSERT(uc != 0); DBUG_ASSERT(uc != 0);
uint check_command = thd->lex.sql_command;
if (check_command < (uint) SQLCOM_END)
{
if (uc->user_resources.updates && uc_update_queries[check_command] &&
++(uc->updates) > uc->user_resources.updates)
{
net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_updates",
(long) uc->user_resources.updates);
error=1;
goto end;
}
}
else
{
bool my_start = thd->start_time != 0; bool my_start = thd->start_time != 0;
time_t check_time = (my_start) ? thd->start_time : time(NULL); time_t check_time = (my_start) ? thd->start_time : time(NULL);
if (check_time - uc->intime >= 3600) if (check_time - uc->intime >= 3600)
{ {
(void) pthread_mutex_lock(&LOCK_user_conn); (void) pthread_mutex_lock(&LOCK_user_conn);
uc->questions=1; uc->questions=1;
uc->updates=0;
uc->conn_per_hour=0;
uc->intime=check_time; uc->intime=check_time;
(void) pthread_mutex_unlock(&LOCK_user_conn); (void) pthread_mutex_unlock(&LOCK_user_conn);
} }
else if (uc->max_questions && ++(uc->questions) > uc->max_questions) else if (uc->user_resources.questions && ++(uc->questions) > uc->user_resources.questions)
{ {
net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_questions", net_printf(&thd->net, ER_USER_LIMIT_REACHED, uc->user, "max_questions",
(long) uc->max_questions); (long) uc->user_resources.questions);
error=1; error=1;
goto end; goto end;
} }
}
end: end:
DBUG_RETURN(error); DBUG_RETURN(error);
} }
static void reset_mqh(THD *thd, LEX_USER *lu, uint mq) static void reset_mqh(THD *thd, LEX_USER *lu, USER_RESOURCES *mqh, bool get_them=false)
{ {
(void) pthread_mutex_lock(&LOCK_user_conn); (void) pthread_mutex_lock(&LOCK_user_conn);
if (lu) // for GRANT if (lu) // for GRANT
{ {
UC *uc; USER_CONN *uc;
uint temp_len=lu->user.length+lu->host.length+2; uint temp_len=lu->user.length+lu->host.length+2;
char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2];
memcpy(temp_user,lu->user.str,lu->user.length); memcpy(temp_user,lu->user.str,lu->user.length);
memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length); memcpy(temp_user+lu->user.length+1,lu->host.str,lu->host.length);
temp_user[lu->user.length]=temp_user[temp_len-1]=0; temp_user[lu->user.length]='\0'; temp_user[temp_len-1]=0;
if ((uc = (struct user_conn *) hash_search(&hash_user_connections, if ((uc = (struct user_conn *) hash_search(&hash_user_connections,
(byte*) temp_user, temp_len))) (byte*) temp_user, temp_len-1)))
{ {
uc->questions=0; uc->questions=0;
uc->max_questions=mq; uc->user_resources=*mqh;
uc->updates=0;
uc->conn_per_hour=0;
} }
} }
else // for FLUSH PRIVILEGES else // for FLUSH PRIVILEGES and FLUSH USER_RESOURCES
{ {
for (uint idx=0;idx < hash_user_connections.records; idx++) for (uint idx=0;idx < hash_user_connections.records; idx++)
{ {
char user[USERNAME_LENGTH+1]; USER_CONN *uc=(struct user_conn *) hash_element(&hash_user_connections, idx);
char *where; if (get_them)
UC *uc=(struct user_conn *) hash_element(&hash_user_connections, idx); get_mqh(uc->user,uc->host,uc);
where=strchr(uc->user,'@'); uc->questions=0;
strmake(user,uc->user,where - uc->user); uc->updates=0;
uc->max_questions=get_mqh(user,where+1); uc->conn_per_hour=0;
} }
} }
(void) pthread_mutex_unlock(&LOCK_user_conn); (void) pthread_mutex_unlock(&LOCK_user_conn);
...@@ -708,7 +746,7 @@ pthread_handler_decl(handle_bootstrap,arg) ...@@ -708,7 +746,7 @@ pthread_handler_decl(handle_bootstrap,arg)
thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1); thd->query= thd->memdup_w_gap(buff, length+1, thd->db_length+1);
thd->query[length] = '\0'; thd->query[length] = '\0';
thd->query_id=query_id++; thd->query_id=query_id++;
if (thd->user_connect && check_mqh(thd)) if (mqh_used && thd->user_connect && check_mqh(thd))
{ {
thd->net.error = 0; thd->net.error = 0;
close_thread_tables(thd); // Free tables close_thread_tables(thd); // Free tables
...@@ -895,7 +933,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -895,7 +933,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
char *save_user= thd->user; char *save_user= thd->user;
char *save_priv_user= thd->priv_user; char *save_priv_user= thd->priv_user;
char *save_db= thd->db; char *save_db= thd->db;
UC *save_uc= thd->user_connect; USER_CONN *save_uc= thd->user_connect;
if ((uint) ((uchar*) db - net->read_pos) > packet_length) if ((uint) ((uchar*) db - net->read_pos) > packet_length)
{ // Check if protocol is ok { // Check if protocol is ok
...@@ -948,7 +986,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -948,7 +986,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
my_pthread_setprio(pthread_self(),QUERY_PRIOR); my_pthread_setprio(pthread_self(),QUERY_PRIOR);
mysql_log.write(thd,command,"%s",thd->query); mysql_log.write(thd,command,"%s",thd->query);
DBUG_PRINT("query",("%s",thd->query)); DBUG_PRINT("query",("%s",thd->query));
if (thd->user_connect && check_mqh(thd)) if (mqh_used && thd->user_connect && check_mqh(thd))
{ {
error = TRUE; // Abort client error = TRUE; // Abort client
net->error = 0; // Don't give abort message net->error = 0; // Don't give abort message
...@@ -1073,8 +1111,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, ...@@ -1073,8 +1111,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
send_error(net,0); send_error(net,0);
else else
send_eof(net); send_eof(net);
if (mqh_used)
reset_mqh(thd,(LEX_USER *) NULL, 0);
break; break;
} }
case COM_SHUTDOWN: case COM_SHUTDOWN:
...@@ -2316,12 +2352,12 @@ mysql_execute_command(void) ...@@ -2316,12 +2352,12 @@ mysql_execute_command(void)
Query_log_event qinfo(thd, thd->query); Query_log_event qinfo(thd, thd->query);
mysql_bin_log.write(&qinfo); mysql_bin_log.write(&qinfo);
} }
if (mqh_used && lex->mqh) if (mqh_used && (lex->mqh.questions || lex->mqh.updates || lex->mqh.connections) && lex->sql_command == SQLCOM_GRANT)
{ {
List_iterator <LEX_USER> str_list(lex->users_list); List_iterator <LEX_USER> str_list(lex->users_list);
LEX_USER *user; LEX_USER *user;
while ((user=str_list++)) while ((user=str_list++))
reset_mqh(thd,user,lex->mqh); reset_mqh(thd,user,&(lex->mqh));
} }
} }
} }
...@@ -2726,10 +2762,17 @@ mysql_parse(THD *thd,char *inBuf,uint length) ...@@ -2726,10 +2762,17 @@ mysql_parse(THD *thd,char *inBuf,uint length)
{ {
LEX *lex=lex_start(thd, (uchar*) inBuf, length); LEX *lex=lex_start(thd, (uchar*) inBuf, length);
if (!yyparse() && ! thd->fatal_error) if (!yyparse() && ! thd->fatal_error)
{
if (mqh_used && thd->user_connect && check_mqh(thd))
{
thd->net.error = 0;
}
else
{ {
mysql_execute_command(); mysql_execute_command();
query_cache_end_of_result(&thd->net); query_cache_end_of_result(&thd->net);
} }
}
else else
query_cache_abort(&thd->net); query_cache_abort(&thd->net);
thd->proc_info="freeing items"; thd->proc_info="freeing items";
...@@ -3272,6 +3315,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) ...@@ -3272,6 +3315,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables)
{ {
acl_reload(); acl_reload();
grant_reload(); grant_reload();
if (mqh_used)
reset_mqh(thd,(LEX_USER *) NULL, 0, true);
} }
if (options & REFRESH_LOG) if (options & REFRESH_LOG)
{ {
...@@ -3325,6 +3370,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables) ...@@ -3325,6 +3370,8 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables)
result=1; result=1;
UNLOCK_ACTIVE_MI; UNLOCK_ACTIVE_MI;
} }
if (options & REFRESH_USER_RESOURCES)
reset_mqh(thd,(LEX_USER *) NULL, 0);
return result; return result;
} }
......
...@@ -501,8 +501,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds, ...@@ -501,8 +501,8 @@ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
select_distinct=0; select_distinct=0;
} }
else if (select_distinct && join.tables - join.const_tables == 1 && else if (select_distinct && join.tables - join.const_tables == 1 &&
(thd->select_limit == HA_POS_ERROR || ((thd->select_limit == HA_POS_ERROR ||
(join.select_options & OPTION_FOUND_ROWS) || (join.select_options & OPTION_FOUND_ROWS)) &&
order && order &&
!(skip_sort_order= !(skip_sort_order=
test_if_skip_sort_order(&join.join_tab[join.const_tables], test_if_skip_sort_order(&join.join_tab[join.const_tables],
...@@ -2363,7 +2363,7 @@ make_simple_join(JOIN *join,TABLE *tmp_table) ...@@ -2363,7 +2363,7 @@ make_simple_join(JOIN *join,TABLE *tmp_table)
join->send_records=(ha_rows) 0; join->send_records=(ha_rows) 0;
join->group=0; join->group=0;
join->do_send_rows = 1; join->do_send_rows = 1;
join->row_limit=HA_POS_ERROR; join->row_limit=join->thd->select_limit;
join_tab->cache.buff=0; /* No cacheing */ join_tab->cache.buff=0; /* No cacheing */
join_tab->table=tmp_table; join_tab->table=tmp_table;
...@@ -4899,7 +4899,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)), ...@@ -4899,7 +4899,7 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
JOIN_TAB *jt=join->join_tab; JOIN_TAB *jt=join->join_tab;
if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group if ((join->tables == 1) && !join->tmp_table && !join->sort_and_group
&& !join->send_group_parts && !join->having && !jt->select_cond && && !join->send_group_parts && !join->having && !jt->select_cond &&
!(jt->table->file->table_flags() & HA_NOT_EXACT_COUNT)) !(jt->table->file->table_flags() & HA_NOT_EXACT_COUNT) && (jt->records < INT_MAX32))
{ {
/* Join over all rows in table; Return number of found rows */ /* Join over all rows in table; Return number of found rows */
join->select_options ^= OPTION_FOUND_ROWS; join->select_options ^= OPTION_FOUND_ROWS;
...@@ -6990,6 +6990,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order, ...@@ -6990,6 +6990,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
/* Don't log this into the slow query log */ /* Don't log this into the slow query log */
select_lex->options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED); select_lex->options&= ~(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED);
thd->offset_limit=0;
if (thd->lex.select == select_lex) if (thd->lex.select == select_lex)
{ {
field_list.push_back(new Item_empty_string("table",NAME_LEN)); field_list.push_back(new Item_empty_string("table",NAME_LEN));
......
...@@ -245,7 +245,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -245,7 +245,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token RELAY_LOG_POS_SYM %token RELAY_LOG_POS_SYM
%token MATCH %token MATCH
%token MAX_ROWS %token MAX_ROWS
%token MAX_CONNECTIONS_PER_HOUR
%token MAX_QUERIES_PER_HOUR %token MAX_QUERIES_PER_HOUR
%token MAX_UPDATES_PER_HOUR
%token MEDIUM_SYM %token MEDIUM_SYM
%token MERGE_SYM %token MERGE_SYM
%token MIN_ROWS %token MIN_ROWS
...@@ -289,6 +291,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); ...@@ -289,6 +291,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token RENAME %token RENAME
%token REPEATABLE_SYM %token REPEATABLE_SYM
%token REQUIRE_SYM %token REQUIRE_SYM
%token RESOURCES
%token RESTORE_SYM %token RESTORE_SYM
%token RESTRICT %token RESTRICT
%token REVOKE %token REVOKE
...@@ -3046,7 +3049,9 @@ keyword: ...@@ -3046,7 +3049,9 @@ keyword:
| MASTER_USER_SYM {} | MASTER_USER_SYM {}
| MASTER_PASSWORD_SYM {} | MASTER_PASSWORD_SYM {}
| MASTER_CONNECT_RETRY_SYM {} | MASTER_CONNECT_RETRY_SYM {}
| MAX_CONNECTIONS_PER_HOUR {}
| MAX_QUERIES_PER_HOUR {} | MAX_QUERIES_PER_HOUR {}
| MAX_UPDATES_PER_HOUR {}
| MEDIUM_SYM {} | MEDIUM_SYM {}
| MERGE_SYM {} | MERGE_SYM {}
| MINUTE_SYM {} | MINUTE_SYM {}
...@@ -3080,6 +3085,7 @@ keyword: ...@@ -3080,6 +3085,7 @@ keyword:
| REPAIR {} | REPAIR {}
| REPEATABLE_SYM {} | REPEATABLE_SYM {}
| RESET_SYM {} | RESET_SYM {}
| RESOURCES {}
| RESTORE_SYM {} | RESTORE_SYM {}
| ROLLBACK_SYM {} | ROLLBACK_SYM {}
| ROWS_SYM {} | ROWS_SYM {}
...@@ -3449,7 +3455,7 @@ grant: ...@@ -3449,7 +3455,7 @@ grant:
lex->select->db=0; lex->select->db=0;
lex->ssl_type=SSL_TYPE_NONE; lex->ssl_type=SSL_TYPE_NONE;
lex->ssl_cipher=lex->x509_subject=lex->x509_issuer=0; lex->ssl_cipher=lex->x509_subject=lex->x509_issuer=0;
lex->mqh=0; bzero(&(lex->mqh),sizeof(lex->mqh));
} }
grant_privileges ON opt_table TO_SYM user_list grant_privileges ON opt_table TO_SYM user_list
require_clause grant_options; require_clause grant_options;
...@@ -3649,10 +3655,18 @@ grant_option_list: ...@@ -3649,10 +3655,18 @@ grant_option_list:
grant_option: grant_option:
GRANT OPTION { Lex->grant |= GRANT_ACL;} GRANT OPTION { Lex->grant |= GRANT_ACL;}
| MAX_QUERIES_PER_HOUR EQ NUM | MAX_QUERIES_PER_HOUR EQ ULONG_NUM
{ {
Lex->mqh=atoi($3.str); Lex->mqh.questions=$3;
}; }
| MAX_UPDATES_PER_HOUR EQ ULONG_NUM
{
Lex->mqh.updates=$3;
}
| MAX_CONNECTIONS_PER_HOUR EQ ULONG_NUM
{
Lex->mqh.connections=$3;
}
begin: begin:
BEGIN_SYM { Lex->sql_command = SQLCOM_BEGIN;} opt_work; BEGIN_SYM { Lex->sql_command = SQLCOM_BEGIN;} opt_work;
......
...@@ -162,13 +162,16 @@ typedef struct st_lex_user { ...@@ -162,13 +162,16 @@ typedef struct st_lex_user {
} LEX_USER; } LEX_USER;
typedef struct user_resources {
uint questions, updates, connections;
} USER_RESOURCES;
typedef struct user_conn { typedef struct user_conn {
char *user; char *user, *host;
uint len, connections, questions, max_questions; uint len, connections, conn_per_hour, updates, questions, user_len;
USER_RESOURCES user_resources;
time_t intime; time_t intime;
} UC; } USER_CONN;
/* Bits in form->update */ /* Bits in form->update */
#define REG_MAKE_DUPP 1 /* Make a copy of record when read */ #define REG_MAKE_DUPP 1 /* Make a copy of record when read */
#define REG_NEW_RECORD 2 /* Write a new record if not found */ #define REG_NEW_RECORD 2 /* Write a new record if not found */
......
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