mirror of
https://github.com/MariaDB/server.git
synced 2025-12-24 11:21:21 +03:00
This is a large push. Included are :
* multi-table updates
* new paid feature for limiting number of queries per hour for users
* optional syntax for multi-table deletes
* optimization for SQL_CALC_FOUND_ROWS
* a small addition for CREATE .. SELECT that will be of future use
I know that all this will require many additions to documentation,
which I have not done, but I am at Arjen's disposal to help him document
all this.
libmysqld/lib_sql.cc:
This is a small change required due to new paid feature of limiting
number of queries per hour for each user.
mysql-test/r/multi_update.result:
Change of the result to accomodate multi-table updates
mysql-test/t/multi_update.test:
Change in multi-table tests to accomodate multi-table updates
BitKeeper/etc/ignore:
Added 50 sql/new.cc to the ignore list
scripts/mysql_install_db.sh:
This is a small change required due to new paid feature of limiting
number of queries per hour for each user.
sql/item_timefunc.h:
This is a small feature that will be of use later in the proper
creation of the tables out of CREATE ... SELECT...
Further changes will come after this resolve/push/test passes ..
sql/lex.h:
This is a small change required due to new paid feature of limiting
number of queries per hour for each user.
sql/mysql_priv.h:
This is a small change required due to new paid feature of limiting
number of queries per hour for each user.
sql/mysqld.cc:
This is a small change required due to new paid feature of limiting
number of queries per hour for each user.
sql/sql_acl.cc:
This is a small change required due to new paid feature of limiting
number of queries per hour for each user.
sql/sql_acl.h:
This is a small change required due to new paid feature of limiting
number of queries per hour for each user.
sql/sql_class.h:
These are changes required for multi-table updates.
sql/sql_lex.h:
Changes required for both multi-table updates and limiting number of
queries per hour (paid feature).
sql/sql_parse.cc:
Ha ! So many changes.
* multi-table updates
* limiting number of queries per hour for users
sql/sql_select.cc:
Optimisation for SQL_CALC_FOUND_ROWS, when a query involves a single
table, without WHERE or GROUP clause
sql/sql_update.cc:
multi-table updates
sql/sql_yacc.yy:
Many things :
* multi-table updates
* limiting number of queries per hour for users
* new optional syntax for multi-table deletes. This one is different
from the one demanded by user. USING clause MUST include ALL tables,
not just the ones that are used and not deleted from.
This commit is contained in:
195
sql/sql_parse.cc
195
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:
|
||||
|
||||
Reference in New Issue
Block a user