diff options
Diffstat (limited to 'sql/sql_parse.cc')
-rw-r--r-- | sql/sql_parse.cc | 195 |
1 files changed, 166 insertions, 29 deletions
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index f8bc02c9f19..b360f94fa1b 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -37,7 +37,7 @@ extern "C" int gethostname(char *name, int namelen); #endif static int check_for_max_user_connections(const char *user, int u_length, - const char *host); + const char *host, uint max); static void decrease_user_connections(const char *user, const char *host); static bool check_db_used(THD *thd,TABLE_LIST *tables); static bool check_merge_table_access(THD *thd, char *db, TABLE_LIST *tables); @@ -98,6 +98,95 @@ inline bool end_active_trans(THD *thd) } +static HASH hash_user_connections; +extern pthread_mutex_t LOCK_user_conn; + +struct user_conn { + char *user; + uint len, connections, questions, max; + time_t intime; +}; + +static byte* get_key_conn(user_conn *buff, uint *length, + my_bool not_used __attribute__((unused))) +{ + *length=buff->len; + return (byte*) buff->user; +} + +#define DEF_USER_COUNT 50 + +static void free_user(struct user_conn *uc) +{ + my_free((char*) uc,MYF(0)); +} + +/* +** Check if maximum queries per hour limit has been reached +** returns 0 if OK. +*/ + +static bool check_mqh(THD *thd, const char *user, const char *host,uint max) +{ + uint temp_len; + char temp_user[USERNAME_LENGTH+HOSTNAME_LENGTH+2]; + struct user_conn *uc; + if (!user) + user=""; + if (!host) + host=""; + temp_len= (uint) (strxnmov(temp_user, sizeof(temp_user), user, "@", host, + NullS) - temp_user); +//This would be MUCH faster if there was already temp_user made in THD !!! May I ?? + (void) pthread_mutex_lock(&LOCK_user_conn); + uc = (struct user_conn *) hash_search(&hash_user_connections, + (byte*) temp_user, temp_len); + if (uc) /* user found ; check for no. of queries */ + { + bool my_start = thd->start_time != 0; + time_t check_time = (my_start) ? thd->start_time : time(NULL); + if (check_time - uc->intime >= 3600) + { + uc->questions=(uint)my_start; + uc->intime=check_time; + } + else if (uc->max && ++(uc->questions) > uc->max) + { + (void) pthread_mutex_unlock(&LOCK_user_conn); + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); // change this to appropriate message + return 1; + } + } + else + { + struct user_conn *uc= ((struct user_conn*) + my_malloc(sizeof(struct user_conn) + temp_len+1, + MYF(MY_WME))); + if (!uc) + { + send_error(¤t_thd->net, 0, NullS); // Out of memory + (void) pthread_mutex_unlock(&LOCK_user_conn); + return 1; + } + uc->user=(char*) (uc+1); + memcpy(uc->user,temp_user,temp_len+1); + uc->len = temp_len; + uc->connections = 1; + uc->questions=0; + uc->max=max; + uc->intime=current_thd->thr_create_time; + if (hash_insert(&hash_user_connections, (byte*) uc)) + { + my_free((char*) uc,0); + send_error(¤t_thd->net, 0, NullS); // Out of memory + (void) pthread_mutex_unlock(&LOCK_user_conn); + return 1; + } + } + (void) pthread_mutex_unlock(&LOCK_user_conn); + return 0; +} + /* ** Check if user is ok ** Updates: @@ -108,6 +197,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, const char *passwd, const char *db, bool check_count) { NET *net= &thd->net; + uint max=0; thd->db=0; if (!(thd->user = my_strdup(user, MYF(0)))) @@ -119,7 +209,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, passwd, thd->scramble, &thd->priv_user, protocol_version == 9 || !(thd->client_capabilities & - CLIENT_LONG_PASSWORD)); + CLIENT_LONG_PASSWORD),&max); DBUG_PRINT("info", ("Capabilities: %d packet_length: %d Host: '%s' User: '%s' Using password: %s Access: %u db: '%s'", thd->client_capabilities, thd->max_packet_length, @@ -150,6 +240,8 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, return(1); } } + if (mqh_used && max && check_mqh(thd,user,thd->host,max)) + return -1; mysql_log.write(thd,command, (thd->priv_user == thd->user ? (char*) "%s@%s on %s" : @@ -159,7 +251,7 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, db ? db : (char*) ""); thd->db_access=0; if (max_user_connections && - check_for_max_user_connections(user, strlen(user), thd->host)) + check_for_max_user_connections(user, strlen(user), thd->host, max)) return -1; if (db && db[0]) { @@ -179,28 +271,6 @@ static bool check_user(THD *thd,enum_server_command command, const char *user, ** variable that is greater then 0 */ -static HASH hash_user_connections; -extern pthread_mutex_t LOCK_user_conn; - -struct user_conn { - char *user; - uint len, connections; -}; - -static byte* get_key_conn(user_conn *buff, uint *length, - my_bool not_used __attribute__((unused))) -{ - *length=buff->len; - return (byte*) buff->user; -} - -#define DEF_USER_COUNT 50 - -static void free_user(struct user_conn *uc) -{ - my_free((char*) uc,MYF(0)); -} - void init_max_user_conn(void) { (void) hash_init(&hash_user_connections,DEF_USER_COUNT,0,0, @@ -210,7 +280,7 @@ void init_max_user_conn(void) static int check_for_max_user_connections(const char *user, int u_length, - const char *host) + const char *host, uint max) { int error=1; uint temp_len; @@ -251,7 +321,10 @@ static int check_for_max_user_connections(const char *user, int u_length, uc->user=(char*) (uc+1); memcpy(uc->user,temp_user,temp_len+1); uc->len = temp_len; - uc->connections = 1; + uc->connections = 1; + uc->questions=0; + uc->max=max; + uc->intime=current_thd->thr_create_time; if (hash_insert(&hash_user_connections, (byte*) uc)) { my_free((char*) uc,0); @@ -290,7 +363,7 @@ static void decrease_user_connections(const char *user, const char *host) dbug_assert(uc != 0); // We should always find the user if (!uc) goto end; // Safety; Something went wrong - if (! --uc->connections) + if (! --uc->connections && !mqh_used) { /* Last connection for user; Delete it */ (void) hash_delete(&hash_user_connections,(byte*) uc); @@ -306,7 +379,6 @@ void free_max_user_conn(void) hash_free(&hash_user_connections); } - /* ** check connnetion and get priviliges ** returns 0 on ok, -1 < if error is given > 0 on error. @@ -849,6 +921,12 @@ bool dispatch_command(enum enum_server_command command, THD *thd, my_pthread_setprio(pthread_self(),QUERY_PRIOR); mysql_log.write(thd,command,"%s",thd->query); DBUG_PRINT("query",("%s",thd->query)); + if (mqh_used && check_mqh(thd,thd->user,thd->host,0)) + { + error = TRUE; + net->error = 0; + break; + } mysql_parse(thd,thd->query,packet_length-1); if (!(specialflag & SPECIAL_NO_PRIOR)) my_pthread_setprio(pthread_self(),WAIT_PRIOR); @@ -959,6 +1037,8 @@ bool dispatch_command(enum enum_server_command command, THD *thd, send_error(net,0); else send_eof(net); + if (mqh_used && hash_user_connections.array.buffer == 0) + init_max_user_conn(); break; } case COM_SHUTDOWN: @@ -1092,6 +1172,8 @@ mysql_execute_command(void) (table_rules_on && tables && thd->slave_thread && !tables_ok(thd,tables))) DBUG_VOID_RETURN; + if (lex->sql_command==SQLCOM_UPDATE && select_lex->table_list.elements > 1) + lex->sql_command=SQLCOM_MULTI_UPDATE; switch (lex->sql_command) { case SQLCOM_SELECT: @@ -1727,6 +1809,59 @@ mysql_execute_command(void) close_thread_tables(thd); break; } + case SQLCOM_MULTI_UPDATE: + multi_update *result; + uint table_count; + TABLE_LIST *auxi; + if (check_access(thd,UPDATE_ACL,tables->db,&tables->grant.privilege)) + goto error; + if (grant_option && check_grant(thd,UPDATE_ACL,tables)) + goto error; + if (select_lex->item_list.elements != lex->value_list.elements) + { + send_error(&thd->net,ER_WRONG_VALUE_COUNT); + DBUG_VOID_RETURN; + } + for (auxi=(TABLE_LIST*) tables, table_count=0 ; auxi ; auxi=auxi->next) + { + table_count++; + auxi->lock_type=TL_WRITE; + } + if (select_lex->order_list.elements || (select_lex->select_limit && select_lex->select_limit < INT_MAX)) + { + send_error(&thd->net,ER_NOT_ALLOWED_COMMAND); /// will have to come up with something better eventually + DBUG_VOID_RETURN; + } + tables->grant.want_privilege=(SELECT_ACL & ~tables->grant.privilege); + if ((res=open_and_lock_tables(thd,tables))) + break; + if (!setup_fields(thd,tables,select_lex->item_list,1,0,0) && + !setup_fields(thd,tables,lex->value_list,0,0,0) && ! thd->fatal_error && + (result=new multi_update(thd,tables,select_lex->item_list,lex->duplicates, + lex->lock_option, table_count))) + { + List <Item> total_list; + List_iterator <Item> field_list(select_lex->item_list); + List_iterator <Item> value_list(lex->value_list); + Item *item; + while ((item=field_list++)) + total_list.push_back(item); + while ((item=value_list++)) + total_list.push_back(item); + + res=mysql_select(thd,tables,total_list, + select_lex->where,select_lex->ftfunc_list, + (ORDER *)NULL,(ORDER *)NULL,(Item *)NULL, + (ORDER *)NULL, + select_lex->options | thd->options | + SELECT_NO_JOIN_CACHE, + result); + delete result; + } + else + res= -1; // Error is not sent + close_thread_tables(thd); + break; case SQLCOM_DROP_TABLE: { if (check_table_access(thd,DROP_ACL,tables)) @@ -2080,6 +2215,8 @@ mysql_execute_command(void) } } } + if (mqh_used && hash_user_connections.array.buffer == 0) + init_max_user_conn(); break; } case SQLCOM_FLUSH: |