summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/examples/ha_example.cc2
-rw-r--r--sql/field_conv.cc5
-rw-r--r--sql/ha_berkeley.cc20
-rw-r--r--sql/ha_berkeley.h5
-rw-r--r--sql/ha_heap.h2
-rw-r--r--sql/ha_innodb.cc410
-rw-r--r--sql/ha_innodb.h1
-rw-r--r--sql/ha_ndbcluster.cc16
-rw-r--r--sql/handler.cc73
-rw-r--r--sql/handler.h9
-rw-r--r--sql/init.cc3
-rw-r--r--sql/item.cc62
-rw-r--r--sql/item.h43
-rw-r--r--sql/item_cmpfunc.cc31
-rw-r--r--sql/item_cmpfunc.h9
-rw-r--r--sql/item_create.cc6
-rw-r--r--sql/item_create.h1
-rw-r--r--sql/item_func.cc89
-rw-r--r--sql/item_func.h17
-rw-r--r--sql/item_strfunc.cc17
-rw-r--r--sql/item_subselect.cc189
-rw-r--r--sql/item_subselect.h7
-rw-r--r--sql/item_sum.cc4
-rw-r--r--sql/item_timefunc.cc81
-rw-r--r--sql/item_timefunc.h35
-rw-r--r--sql/lex.h3
-rw-r--r--sql/lock.cc6
-rw-r--r--sql/log_event.cc16
-rw-r--r--sql/log_event.h6
-rw-r--r--sql/mysql_priv.h69
-rw-r--r--sql/mysqld.cc27
-rw-r--r--sql/set_var.cc22
-rw-r--r--sql/share/Makefile.am2
-rw-r--r--sql/share/errmsg.txt29
-rw-r--r--sql/slave.cc4
-rw-r--r--sql/sp.cc168
-rw-r--r--sql/sp.h11
-rw-r--r--sql/sp_cache.cc224
-rw-r--r--sql/sp_cache.h101
-rw-r--r--sql/sp_head.cc652
-rw-r--r--sql/sp_head.h2
-rw-r--r--sql/sp_rcontext.cc10
-rw-r--r--sql/sp_rcontext.h8
-rw-r--r--sql/sql_acl.cc63
-rw-r--r--sql/sql_base.cc1977
-rw-r--r--sql/sql_cache.cc10
-rw-r--r--sql/sql_class.cc225
-rw-r--r--sql/sql_class.h106
-rw-r--r--sql/sql_db.cc139
-rw-r--r--sql/sql_delete.cc11
-rw-r--r--sql/sql_derived.cc5
-rw-r--r--sql/sql_handler.cc2
-rw-r--r--sql/sql_help.cc32
-rw-r--r--sql/sql_insert.cc191
-rw-r--r--sql/sql_lex.cc25
-rw-r--r--sql/sql_lex.h49
-rw-r--r--sql/sql_list.h7
-rw-r--r--sql/sql_load.cc1
-rw-r--r--sql/sql_olap.cc2
-rw-r--r--sql/sql_parse.cc531
-rw-r--r--sql/sql_prepare.cc33
-rw-r--r--sql/sql_select.cc48
-rw-r--r--sql/sql_select.h14
-rw-r--r--sql/sql_show.cc484
-rw-r--r--sql/sql_table.cc3
-rw-r--r--sql/sql_test.cc2
-rw-r--r--sql/sql_trigger.cc36
-rw-r--r--sql/sql_trigger.h51
-rw-r--r--sql/sql_union.cc20
-rw-r--r--sql/sql_update.cc17
-rw-r--r--sql/sql_view.cc110
-rw-r--r--sql/sql_view.h2
-rw-r--r--sql/sql_yacc.yy249
-rw-r--r--sql/table.cc475
-rw-r--r--sql/table.h212
75 files changed, 5228 insertions, 2401 deletions
diff --git a/sql/examples/ha_example.cc b/sql/examples/ha_example.cc
index a7e193b9730..dfc2fa7a260 100644
--- a/sql/examples/ha_example.cc
+++ b/sql/examples/ha_example.cc
@@ -67,7 +67,7 @@
#pragma implementation // gcc: Class implementation
#endif
-#include <mysql_priv.h>
+#include "../mysql_priv.h"
#ifdef HAVE_EXAMPLE_DB
#include "ha_example.h"
diff --git a/sql/field_conv.cc b/sql/field_conv.cc
index fc7347ef9af..40f3ff85c58 100644
--- a/sql/field_conv.cc
+++ b/sql/field_conv.cc
@@ -640,7 +640,10 @@ void field_conv(Field *to,Field *from)
(!(to->table->in_use->variables.sql_mode &
(MODE_NO_ZERO_IN_DATE | MODE_NO_ZERO_DATE | MODE_INVALID_DATES)) ||
to->type() != FIELD_TYPE_DATE &&
- to->type() != FIELD_TYPE_DATETIME))
+ to->type() != FIELD_TYPE_DATETIME) &&
+ (from->real_type() != MYSQL_TYPE_VARCHAR ||
+ ((Field_varstring*)from)->length_bytes ==
+ ((Field_varstring*)to)->length_bytes))
{ // Identical fields
#ifdef HAVE_purify
/* This may happen if one does 'UPDATE ... SET x=x' */
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
index d69f9d74a01..9a0e0ed1488 100644
--- a/sql/ha_berkeley.cc
+++ b/sql/ha_berkeley.cc
@@ -958,8 +958,6 @@ int ha_berkeley::write_row(byte * record)
{
DB_TXN *sub_trans = transaction;
/* Don't use sub transactions in temporary tables */
- ulong thd_options= (table->s->tmp_table == NO_TMP_TABLE ?
- table->in_use->options : 0);
for (uint retry=0 ; retry < berkeley_trans_retry ; retry++)
{
key_map changed_keys(0);
@@ -1070,7 +1068,7 @@ int ha_berkeley::key_cmp(uint keynr, const byte * old_row,
int ha_berkeley::update_primary_key(DB_TXN *trans, bool primary_key_changed,
const byte * old_row, DBT *old_key,
const byte * new_row, DBT *new_key,
- ulong thd_options, bool local_using_ignore)
+ bool local_using_ignore)
{
DBT row;
int error;
@@ -1119,8 +1117,7 @@ int ha_berkeley::update_primary_key(DB_TXN *trans, bool primary_key_changed,
int ha_berkeley::restore_keys(DB_TXN *trans, key_map *changed_keys,
uint primary_key,
const byte *old_row, DBT *old_key,
- const byte *new_row, DBT *new_key,
- ulong thd_options)
+ const byte *new_row, DBT *new_key)
{
int error;
DBT tmp_key;
@@ -1130,7 +1127,7 @@ int ha_berkeley::restore_keys(DB_TXN *trans, key_map *changed_keys,
/* Restore the old primary key, and the old row, but don't ignore
duplicate key failure */
if ((error=update_primary_key(trans, TRUE, new_row, new_key,
- old_row, old_key, thd_options, FALSE)))
+ old_row, old_key, FALSE)))
goto err; /* purecov: inspected */
/* Remove the new key, and put back the old key
@@ -1167,8 +1164,6 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row)
DBT prim_key, key, old_prim_key;
int error;
DB_TXN *sub_trans;
- ulong thd_options= (table->s->tmp_table == NO_TMP_TABLE ?
- table->in_use->options : 0);
bool primary_key_changed;
DBUG_ENTER("update_row");
LINT_INIT(error);
@@ -1204,7 +1199,7 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row)
if (!(error=update_primary_key(sub_trans, primary_key_changed,
old_row, &old_prim_key,
new_row, &prim_key,
- thd_options, using_ignore)))
+ using_ignore)))
{
// Update all other keys
for (uint keynr=0 ; keynr < table->s->keys ; keynr++)
@@ -1239,8 +1234,7 @@ int ha_berkeley::update_row(const byte * old_row, byte * new_row)
int new_error = 0;
if (!changed_keys.is_clear_all())
new_error=restore_keys(transaction, &changed_keys, primary_key,
- old_row, &old_prim_key, new_row, &prim_key,
- thd_options);
+ old_row, &old_prim_key, new_row, &prim_key);
if (new_error)
{
/* This shouldn't happen */
@@ -1342,8 +1336,6 @@ int ha_berkeley::delete_row(const byte * record)
int error;
DBT row, prim_key;
key_map keys= table->s->keys_in_use;
- ulong thd_options= (table->s->tmp_table == NO_TMP_TABLE ?
- table->in_use->options : 0);
DBUG_ENTER("delete_row");
statistic_increment(table->in_use->status_var.ha_delete_count,&LOCK_status);
@@ -2150,7 +2142,7 @@ ha_rows ha_berkeley::records_in_range(uint keynr, key_range *start_key,
end_pos=end_range.less+end_range.equal;
rows=(end_pos-start_pos)*records;
DBUG_PRINT("exit",("rows: %g",rows));
- DBUG_RETURN(rows <= 1.0 ? (ha_rows) 1 : (ha_rows) rows);
+ DBUG_RETURN((ha_rows)(rows <= 1.0 ? 1 : rows));
}
diff --git a/sql/ha_berkeley.h b/sql/ha_berkeley.h
index ee8f4b80049..aab76accefa 100644
--- a/sql/ha_berkeley.h
+++ b/sql/ha_berkeley.h
@@ -74,13 +74,12 @@ class ha_berkeley: public handler
DBT *prim_key, key_map *keys);
int restore_keys(DB_TXN *trans, key_map *changed_keys, uint primary_key,
const byte *old_row, DBT *old_key,
- const byte *new_row, DBT *new_key,
- ulong thd_options);
+ const byte *new_row, DBT *new_key);
int key_cmp(uint keynr, const byte * old_row, const byte * new_row);
int update_primary_key(DB_TXN *trans, bool primary_key_changed,
const byte * old_row, DBT *old_key,
const byte * new_row, DBT *prim_key,
- ulong thd_options, bool local_using_ignore);
+ bool local_using_ignore);
int read_row(int error, char *buf, uint keynr, DBT *row, DBT *key, bool);
DBT *get_pos(DBT *to, byte *pos);
diff --git a/sql/ha_heap.h b/sql/ha_heap.h
index a57343fb543..24097460a24 100644
--- a/sql/ha_heap.h
+++ b/sql/ha_heap.h
@@ -43,6 +43,8 @@ public:
return ((table->key_info[inx].algorithm == HA_KEY_ALG_BTREE) ? "BTREE" :
"HASH");
}
+ /* Rows also use a fixed-size format */
+ enum row_type get_row_type() const { return ROW_TYPE_FIXED; }
const char **bas_ext() const;
ulong table_flags() const
{
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index 93d830d9fb1..df58a722e9a 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -48,6 +48,10 @@ have disables the InnoDB inlining in this file. */
pthread_mutex_t innobase_share_mutex, /* to protect innobase_open_files */
prepare_commit_mutex; /* to force correct commit order in
binlog */
+ulong commit_threads= 0;
+pthread_mutex_t commit_threads_m;
+pthread_cond_t commit_cond;
+pthread_mutex_t commit_cond_m;
bool innodb_inited= 0;
/*-----------------------------------------------------------------*/
@@ -550,19 +554,20 @@ innobase_mysql_end_print_arbitrary_thd(void)
}
/*****************************************************************
-Prints info of a THD object (== user session thread) to the
-standard output. NOTE that /mysql/innobase/trx/trx0trx.c must contain
-the prototype for this function! */
+Prints info of a THD object (== user session thread) to the given file.
+NOTE that /mysql/innobase/trx/trx0trx.c must contain the prototype for
+this function! */
extern "C"
void
innobase_mysql_print_thd(
/*=====================*/
- FILE* f, /* in: output stream */
- void* input_thd)/* in: pointer to a MySQL THD object */
+ FILE* f, /* in: output stream */
+ void* input_thd, /* in: pointer to a MySQL THD object */
+ uint max_query_len) /* in: max query length to print, or 0 to
+ use the default max length */
{
const THD* thd;
const char* s;
- char buf[301];
thd = (const THD*) input_thd;
@@ -589,25 +594,47 @@ innobase_mysql_print_thd(
}
if ((s = thd->query)) {
- /* determine the length of the query string */
- uint32 i, len;
+ /* 3100 is chosen because currently 3000 is the maximum
+ max_query_len we ever give this. */
+ char buf[3100];
+ uint len;
+
+ /* If buf is too small, we dynamically allocate storage
+ in this. */
+ char* dyn_str = NULL;
+
+ /* Points to buf or dyn_str. */
+ char* str = buf;
- len = thd->query_length;
-
- if (len > 300) {
- len = 300; /* ADDITIONAL SAFETY: print at most
- 300 chars to reduce the probability of
- a seg fault if there is a race in
- thd->query_length in MySQL; after
- May 14, 2004 probably no race any more,
- but better be safe */
+ if (max_query_len == 0)
+ {
+ /* ADDITIONAL SAFETY: the default is to print at
+ most 300 chars to reduce the probability of a
+ seg fault if there is a race in
+ thd->query_length in MySQL; after May 14, 2004
+ probably no race any more, but better be
+ safe */
+ max_query_len = 300;
+ }
+
+ len = min(thd->query_length, max_query_len);
+
+ if (len > (sizeof(buf) - 1))
+ {
+ dyn_str = my_malloc(len + 1, MYF(0));
+ str = dyn_str;
}
- /* Use strmake to reduce the timeframe
- for a race, compared to fwrite() */
- i= (uint) (strmake(buf, s, len) - buf);
+ /* Use strmake to reduce the timeframe for a race,
+ compared to fwrite() */
+ len = (uint) (strmake(str, s, len) - str);
putc('\n', f);
- fwrite(buf, 1, i, f);
+ fwrite(str, 1, len, f);
+
+ if (dyn_str)
+ {
+ my_free(dyn_str, MYF(0));
+ }
}
putc('\n', f);
@@ -948,9 +975,9 @@ innobase_query_caching_of_table_permitted(
trx = check_trx_exists(thd);
if (trx->has_search_latch) {
ut_print_timestamp(stderr);
- fprintf(stderr,
-" InnoDB: Error: the calling thread is holding the adaptive search\n"
-"InnoDB: latch though calling innobase_query_caching_of_table_permitted\n");
+ sql_print_error("The calling thread is holding the adaptive "
+ "search, latch though calling "
+ "innobase_query_caching_of_table_permitted.");
}
innobase_release_stat_resources(trx);
@@ -1266,9 +1293,8 @@ innobase_init(void)
&srv_log_group_home_dirs);
if (ret == FALSE || innobase_mirrored_log_groups != 1) {
- fprintf(stderr,
- "InnoDB: syntax error in innodb_log_group_home_dir\n"
- "InnoDB: or a wrong number of mirrored log groups\n");
+ sql_print_error("syntax error in innodb_log_group_home_dir, or a "
+ "wrong number of mirrored log groups");
my_free(internal_innobase_data_file_path,
MYF(MY_ALLOW_ZERO_PTR));
@@ -1367,6 +1393,9 @@ innobase_init(void)
(hash_get_key) innobase_get_key, 0, 0);
pthread_mutex_init(&innobase_share_mutex, MY_MUTEX_INIT_FAST);
pthread_mutex_init(&prepare_commit_mutex, MY_MUTEX_INIT_FAST);
+ pthread_mutex_init(&commit_threads_m, MY_MUTEX_INIT_FAST);
+ pthread_mutex_init(&commit_cond_m, MY_MUTEX_INIT_FAST);
+ pthread_cond_init(&commit_cond, NULL);
innodb_inited= 1;
/* If this is a replication slave and we needed to do a crash recovery,
@@ -1416,6 +1445,9 @@ innobase_end(void)
MYF(MY_ALLOW_ZERO_PTR));
pthread_mutex_destroy(&innobase_share_mutex);
pthread_mutex_destroy(&prepare_commit_mutex);
+ pthread_mutex_destroy(&commit_threads_m);
+ pthread_mutex_destroy(&commit_cond_m);
+ pthread_cond_destroy(&commit_cond);
}
DBUG_RETURN(err);
@@ -1542,8 +1574,10 @@ innobase_commit(
reserve the kernel mutex, we have to release the search system latch
first to obey the latching order. */
- innobase_release_stat_resources(trx);
-
+ if (trx->has_search_latch) {
+ trx_search_latch_release_if_reserved(trx);
+ }
+
/* The flag trx->active_trans is set to 1 in
1. ::external_lock(),
@@ -1562,11 +1596,9 @@ innobase_commit(
if (trx->active_trans == 0
&& trx->conc_state != TRX_NOT_STARTED) {
- fprintf(stderr,
-"InnoDB: Error: trx->active_trans == 0\n"
-"InnoDB: but trx->conc_state != TRX_NOT_STARTED\n");
+ sql_print_error("trx->active_trans == 0, but trx->conc_state != "
+ "TRX_NOT_STARTED");
}
-
if (all
|| (!(thd->options & (OPTION_NOT_AUTOCOMMIT | OPTION_BEGIN)))) {
@@ -1575,18 +1607,43 @@ innobase_commit(
/* We need current binlog position for ibbackup to work.
Note, the position is current because of prepare_commit_mutex */
+retry:
+ if (srv_commit_concurrency > 0)
+ {
+ pthread_mutex_lock(&commit_cond_m);
+ commit_threads++;
+ if (commit_threads > srv_commit_concurrency)
+ {
+ commit_threads--;
+ pthread_cond_wait(&commit_cond, &commit_cond_m);
+ pthread_mutex_unlock(&commit_cond_m);
+ goto retry;
+ }
+ else
+ pthread_mutex_unlock(&commit_cond_m);
+ }
+
trx->mysql_log_file_name = mysql_bin_log.get_log_fname();
trx->mysql_log_offset =
(ib_longlong)mysql_bin_log.get_log_file()->pos_in_file;
innobase_commit_low(trx);
+ if (srv_commit_concurrency > 0)
+ {
+ pthread_mutex_lock(&commit_cond_m);
+ commit_threads--;
+ pthread_cond_signal(&commit_cond);
+ pthread_mutex_unlock(&commit_cond_m);
+ }
+
if (trx->active_trans == 2) {
pthread_mutex_unlock(&prepare_commit_mutex);
}
+
trx->active_trans = 0;
-
+
} else {
/* We just mark the SQL statement ended and do not do a
transaction commit */
@@ -1606,7 +1663,11 @@ innobase_commit(
/* Tell the InnoDB server that there might be work for utility
threads: */
+ if (trx->declared_to_be_inside_innodb) {
+ /* Release our possible ticket in the FIFO */
+ srv_conc_force_exit_innodb(trx);
+ }
srv_active_wake_master_thread();
DBUG_RETURN(0);
@@ -1813,21 +1874,23 @@ try_again:
if (ret != 0) {
ut_print_timestamp(stderr);
- fprintf(stderr,
-" InnoDB: Error: MySQL synchronous replication\n"
-"InnoDB: was not able to send the binlog to the slave within the\n"
-"InnoDB: timeout %lu. We assume that the slave has become inaccessible,\n"
-"InnoDB: and switch off synchronous replication until the communication.\n"
-"InnoDB: to the slave works again.\n",
- thd->variables.sync_replication_timeout);
- fprintf(stderr,
-"InnoDB: MySQL synchronous replication has sent binlog\n"
-"InnoDB: to the slave up to file %s, position %lu\n", innobase_repl_file_name,
- (ulong)innobase_repl_pos);
- fprintf(stderr,
-"InnoDB: This transaction needs it to be sent up to\n"
-"InnoDB: file %s, position %lu\n", trx->repl_wait_binlog_name,
- (ulong)trx->repl_wait_binlog_pos);
+ sql_print_error("MySQL synchronous replication was "
+ "not able to send the binlog to the "
+ "slave within the timeout %lu. We "
+ "assume that the slave has become "
+ "inaccessible, and switch off "
+ "synchronous replication until the "
+ "communication to the slave works "
+ "again. MySQL synchronous replication "
+ "has sent binlog to the slave up to "
+ "file %s, position %lu. This "
+ "transaction needs it to be sent up "
+ "to file %s, position %lu.",
+ thd->variables.sync_replication_timeout,
+ innobase_repl_file_name,
+ (ulong) innobase_repl_pos,
+ trx->repl_wait_binlog_name,
+ (ulong) trx->repl_wait_binlog_pos);
innobase_repl_state = 0;
@@ -1878,9 +1941,9 @@ innobase_repl_report_sent_binlog(
if (innobase_repl_state == 0) {
ut_print_timestamp(stderr);
- fprintf(stderr,
-" InnoDB: Switching MySQL synchronous replication on again at\n"
-"InnoDB: binlog file %s, position %lu\n", log_file_name, (ulong)end_offset);
+ sql_print_warning("Switching MySQL synchronous replication on "
+ "again at binlog file %s, position %lu",
+ log_file_name, (ulong) end_offset);
innobase_repl_state = 1;
}
@@ -1897,14 +1960,14 @@ innobase_repl_report_sent_binlog(
|| (cmp == 0 && end_offset < innobase_repl_pos)) {
ut_print_timestamp(stderr);
- fprintf(stderr,
-" InnoDB: Error: MySQL synchronous replication has sent binlog\n"
-"InnoDB: to the slave up to file %s, position %lu\n", innobase_repl_file_name,
- (ulong)innobase_repl_pos);
- fprintf(stderr,
-"InnoDB: but now MySQL reports that it sent the binlog only up to\n"
-"InnoDB: file %s, position %lu\n", log_file_name, (ulong)end_offset);
-
+ sql_print_error("MySQL synchronous replication has "
+ "sent binlog to the slave up to file "
+ "%s, position %lu, but now MySQL "
+ "reports that it sent the binlog only "
+ "up to file %s, position %lu",
+ innobase_repl_file_name,
+ (ulong) innobase_repl_pos,
+ log_file_name, (ulong) end_offset);
}
}
@@ -2146,16 +2209,21 @@ innobase_close_connection(
ut_a(trx);
- if (trx->conc_state != TRX_NOT_STARTED) {
- ut_print_timestamp(stderr);
-
- fprintf(stderr,
-" InnoDB: Warning: MySQL is closing a connection"
-"InnoDB: that has an active InnoDB transaction. We roll back that\n"
-"InnoDB: transaction. %lu row modifications to roll back.\n",
- (ulong)trx->undo_no.low);
+ if (trx->active_trans == 0
+ && trx->conc_state != TRX_NOT_STARTED) {
+
+ sql_print_error("trx->active_trans == 0, but trx->conc_state != "
+ "TRX_NOT_STARTED");
}
+
+ if (trx->conc_state != TRX_NOT_STARTED &&
+ global_system_variables.log_warnings)
+ sql_print_warning("MySQL is closing a connection that has an active "
+ "InnoDB transaction. %lu row modifications will "
+ "roll back.",
+ (ulong)trx->undo_no.low);
+
innobase_rollback_trx(trx);
trx_free_for_mysql(trx);
@@ -2306,15 +2374,17 @@ ha_innobase::open(
norm_name, NULL);
if (NULL == ib_table) {
ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB error:\n"
-"Cannot find table %s from the internal data dictionary\n"
-"of InnoDB though the .frm file for the table exists. Maybe you\n"
-"have deleted and recreated InnoDB data files but have forgotten\n"
-"to delete the corresponding .frm files of InnoDB tables, or you\n"
-"have moved .frm files to another database?\n"
-"Look from section 15.1 of http://www.innodb.com/ibman.html\n"
-"how you can resolve the problem.\n",
- norm_name);
+ sql_print_error("Cannot find table %s from the internal data "
+ "dictionary\nof InnoDB though the .frm file "
+ "for the table exists. Maybe you\nhave "
+ "deleted and recreated InnoDB data files but "
+ "have forgotten\nto delete the corresponding "
+ ".frm files of InnoDB tables, or you\n"
+ "have moved .frm files to another database?\n"
+ "Look from section 15.1 of "
+ "http://www.innodb.com/ibman.html\n"
+ "how you can resolve the problem.\n",
+ norm_name);
free_share(share);
my_free((char*) upd_buff, MYF(0));
my_errno = ENOENT;
@@ -2324,14 +2394,15 @@ ha_innobase::open(
if (ib_table->ibd_file_missing && !thd->tablespace_op) {
ut_print_timestamp(stderr);
- fprintf(stderr, " InnoDB error:\n"
-"MySQL is trying to open a table handle but the .ibd file for\n"
-"table %s does not exist.\n"
-"Have you deleted the .ibd file from the database directory under\n"
-"the MySQL datadir, or have you used DISCARD TABLESPACE?\n"
-"Look from section 15.1 of http://www.innodb.com/ibman.html\n"
-"how you can resolve the problem.\n",
- norm_name);
+ sql_print_error("MySQL is trying to open a table handle but "
+ "the .ibd file for\ntable %s does not exist.\n"
+ "Have you deleted the .ibd file from the "
+ "database directory under\nthe MySQL datadir, "
+ "or have you used DISCARD TABLESPACE?\n"
+ "Look from section 15.1 of "
+ "http://www.innodb.com/ibman.html\n"
+ "how you can resolve the problem.\n",
+ norm_name);
free_share(share);
my_free((char*) upd_buff, MYF(0));
my_errno = ENOENT;
@@ -2357,9 +2428,8 @@ ha_innobase::open(
if (!row_table_got_default_clust_index(ib_table)) {
if (primary_key >= MAX_KEY) {
- fprintf(stderr,
- "InnoDB: Error: table %s has a primary key in InnoDB\n"
- "InnoDB: data dictionary, but not in MySQL!\n", name);
+ sql_print_error("Table %s has a primary key in InnoDB data "
+ "dictionary, but not in MySQL!", name);
}
((row_prebuilt_t*)innobase_prebuilt)
@@ -2373,16 +2443,15 @@ ha_innobase::open(
ref_length = table->key_info[primary_key].key_length;
} else {
if (primary_key != MAX_KEY) {
- fprintf(stderr,
- "InnoDB: Error: table %s has no primary key in InnoDB\n"
- "InnoDB: data dictionary, but has one in MySQL!\n"
- "InnoDB: If you created the table with a MySQL\n"
- "InnoDB: version < 3.23.54 and did not define a primary\n"
- "InnoDB: key, but defined a unique key with all non-NULL\n"
- "InnoDB: columns, then MySQL internally treats that key\n"
- "InnoDB: as the primary key. You can fix this error by\n"
- "InnoDB: dump + DROP + CREATE + reimport of the table.\n",
- name);
+ sql_print_error("Table %s has no primary key in InnoDB data "
+ "dictionary, but has one in MySQL! If you "
+ "created the table with a MySQL version < "
+ "3.23.54 and did not define a primary key, "
+ "but defined a unique key with all non-NULL "
+ "columns, then MySQL internally treats that "
+ "key as the primary key. You can fix this "
+ "error by dump + DROP + CREATE + reimport "
+ "of the table.", name);
}
((row_prebuilt_t*)innobase_prebuilt)
@@ -2399,10 +2468,9 @@ ha_innobase::open(
and it will never be updated anyway. */
if (key_used_on_scan != MAX_KEY) {
- fprintf(stderr,
-"InnoDB: Warning: table %s key_used_on_scan is %lu even though there is no\n"
-"InnoDB: primary key inside InnoDB.\n",
- name, (ulong)key_used_on_scan);
+ sql_print_warning("Table %s key_used_on_scan is %lu even "
+ "though there is no primary key inside "
+ "InnoDB.", name, (ulong) key_used_on_scan);
}
}
@@ -2557,9 +2625,10 @@ innobase_mysql_cmp(
charset = get_charset(charset_number, MYF(MY_WME));
if (charset == NULL) {
- fprintf(stderr,
-"InnoDB: fatal error: InnoDB needs charset %lu for doing a comparison,\n"
-"InnoDB: but MySQL cannot find that charset.\n", (ulong)charset_number);
+ sql_print_error("InnoDB needs charset %lu for doing "
+ "a comparison, but MySQL cannot "
+ "find that charset.",
+ (ulong) charset_number);
ut_a(0);
}
}
@@ -3126,11 +3195,11 @@ ha_innobase::write_row(
if (prebuilt->trx !=
(trx_t*) current_thd->ha_data[innobase_hton.slot]) {
- fprintf(stderr,
-"InnoDB: Error: the transaction object for the table handle is at\n"
-"InnoDB: %p, but for the current thread it is at %p\n",
- prebuilt->trx,
- (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+ sql_print_error("The transaction object for the table handle is at "
+ "%p, but for the current thread it is at %p",
+ prebuilt->trx,
+ (trx_t*) current_thd->ha_data[innobase_hton.slot]);
+
fputs("InnoDB: Dump of 200 bytes around prebuilt: ", stderr);
ut_print_buf(stderr, ((const byte*)prebuilt) - 100, 200);
fputs("\n"
@@ -3617,9 +3686,9 @@ ha_innobase::unlock_row(void)
if (last_query_id != user_thd->query_id) {
ut_print_timestamp(stderr);
- fprintf(stderr,
-" InnoDB: Error: last_query_id is %lu != user_thd_query_id is %lu\n",
- (ulong)last_query_id, (ulong)user_thd->query_id);
+ sql_print_error("last_query_id is %lu != user_thd_query_id is "
+ "%lu", (ulong) last_query_id,
+ (ulong) user_thd->query_id);
mem_analyze_corruption((byte *) prebuilt->trx);
ut_error;
}
@@ -3912,9 +3981,10 @@ ha_innobase::change_active_index(
}
if (!prebuilt->index) {
- sql_print_error(
-"Innodb could not find key n:o %u with name %s from dict cache for table %s",
- keynr, key ? key->name : "NULL", prebuilt->table->name);
+ sql_print_error("Innodb could not find key n:o %u with name %s "
+ "from dict cache for table %s",
+ keynr, key ? key->name : "NULL",
+ prebuilt->table->name);
DBUG_RETURN(1);
}
@@ -4280,9 +4350,8 @@ ha_innobase::position(
table. */
if (len != ref_length) {
- fprintf(stderr,
- "InnoDB: Error: stored ref len is %lu, but table ref len is %lu\n",
- (ulong)len, (ulong)ref_length);
+ sql_print_error("Stored ref len is %lu, but table ref len is %lu",
+ (ulong) len, (ulong) ref_length);
}
}
@@ -4487,11 +4556,13 @@ create_index(
|| col_type == DATA_FLOAT
|| col_type == DATA_DOUBLE
|| col_type == DATA_DECIMAL) {
- fprintf(stderr,
-"InnoDB: error: MySQL is trying to create a column prefix index field\n"
-"InnoDB: on an inappropriate data type. Table name %s, column name %s.\n",
- table_name, key_part->field->field_name);
-
+ sql_print_error("MySQL is trying to create a column "
+ "prefix index field, on an "
+ "inappropriate data type. Table "
+ "name %s, column name %s.",
+ table_name,
+ key_part->field->field_name);
+
prefix_len = 0;
}
} else {
@@ -5282,6 +5353,7 @@ ha_innobase::info(
dict_table_t* ib_table;
dict_index_t* index;
ha_rows rec_per_key;
+ ib_longlong n_rows;
ulong j;
ulong i;
char path[FN_REFLEN];
@@ -5346,7 +5418,30 @@ ha_innobase::info(
}
if (flag & HA_STATUS_VARIABLE) {
- records = (ha_rows)ib_table->stat_n_rows;
+ n_rows = ib_table->stat_n_rows;
+
+ /* Because we do not protect stat_n_rows by any mutex in a
+ delete, it is theoretically possible that the value can be
+ smaller than zero! TODO: fix this race.
+
+ The MySQL optimizer seems to assume in a left join that n_rows
+ is an accurate estimate if it is zero. Of course, it is not,
+ since we do not have any locks on the rows yet at this phase.
+ Since SHOW TABLE STATUS seems to call this function with the
+ HA_STATUS_TIME flag set, while the left join optizer does not
+ set that flag, we add one to a zero value if the flag is not
+ set. That way SHOW TABLE STATUS will show the best estimate,
+ while the optimizer never sees the table empty. */
+
+ if (n_rows < 0) {
+ n_rows = 0;
+ }
+
+ if (n_rows == 0 && !(flag & HA_STATUS_TIME)) {
+ n_rows++;
+ }
+
+ records = (ha_rows)n_rows;
deleted = 0;
data_file_length = ((ulonglong)
ib_table->stat_clustered_index_size)
@@ -5374,12 +5469,14 @@ ha_innobase::info(
for (i = 0; i < table->s->keys; i++) {
if (index == NULL) {
ut_print_timestamp(stderr);
- fprintf(stderr,
-" InnoDB: Error: table %s contains less indexes inside InnoDB\n"
-"InnoDB: than are defined in the MySQL .frm file. Have you mixed up\n"
-"InnoDB: .frm files from different installations? See section\n"
-"InnoDB: 15.1 at http://www.innodb.com/ibman.html\n",
- ib_table->name);
+ sql_print_error("Table %s contains less "
+ "indexes inside InnoDB than "
+ "are defined in the MySQL "
+ ".frm file. Have you mixed up "
+ ".frm files from different "
+ "installations? See section "
+ "15.1 at http://www.innodb.com/ibman.html",
+ ib_table->name);
break;
}
@@ -5387,15 +5484,21 @@ ha_innobase::info(
if (j + 1 > index->n_uniq) {
ut_print_timestamp(stderr);
- fprintf(stderr,
-" InnoDB: Error: index %s of %s has %lu columns unique inside InnoDB\n"
-"InnoDB: but MySQL is asking statistics for %lu columns. Have you mixed up\n"
-"InnoDB: .frm files from different installations? See section\n"
-"InnoDB: 15.1 at http://www.innodb.com/ibman.html\n",
- index->name,
- ib_table->name,
- (unsigned long) index->n_uniq,
- j + 1);
+ sql_print_error("Index %s of %s has "
+ "%lu columns unique "
+ "inside InnoDB, but "
+ "MySQL is asking "
+ "statistics for %lu "
+ "columns. Have you "
+ "mixed up .frm files "
+ "from different "
+ "installations? See "
+ "section 15.1 at "
+ "http://www.innodb.com/ibman.html",
+ index->name,
+ ib_table->name,
+ (unsigned long)
+ index->n_uniq, j + 1);
break;
}
@@ -5943,9 +6046,9 @@ ha_innobase::start_stmt(
if (prebuilt->stored_select_lock_type != LOCK_S
&& prebuilt->stored_select_lock_type != LOCK_X) {
- fprintf(stderr,
-"InnoDB: Error: stored_select_lock_type is %lu inside ::start_stmt()!\n",
- prebuilt->stored_select_lock_type);
+ sql_print_error("stored_select_lock_type is %lu inside "
+ "::start_stmt()!",
+ prebuilt->stored_select_lock_type);
/* Set the value to LOCK_X: this is just fault
tolerance, we do not know what the correct value
@@ -6721,9 +6824,8 @@ ha_innobase::innobase_read_and_init_auto_inc(
error = 0;
} else {
/* This should not happen in a consistent read */
- fprintf(stderr,
-"InnoDB: Error: consistent read of auto-inc column returned %lu\n",
- (ulong)error);
+ sql_print_error("Consistent read of auto-inc column "
+ "returned %lu", (ulong) error);
auto_inc = -1;
goto func_exit;
@@ -6780,9 +6882,8 @@ ha_innobase::get_auto_increment()
initialized. */
ut_print_timestamp(stderr);
- fprintf(stderr,
- " InnoDB: Error: error %lu in ::get_auto_increment()\n",
- (ulong)error);
+ sql_print_error("Error %lu in ::get_auto_increment()",
+ (ulong) error);
return(~(ulonglong) 0);
}
@@ -6857,8 +6958,8 @@ ha_innobase::cmp_ref(
return(result);
}
- ref1 += key_part->length;
- ref2 += key_part->length;
+ ref1 += key_part->store_length;
+ ref2 += key_part->store_length;
}
return(0);
@@ -7039,7 +7140,7 @@ innobase_xa_prepare(
return(0);
}
- trx->xid=thd->transaction.xid;
+ trx->xid=thd->transaction.xid_state.xid;
/* Release a possible FIFO ticket and search latch. Since we will
reserve the kernel mutex, we have to release the search system latch
@@ -7049,9 +7150,8 @@ innobase_xa_prepare(
if (trx->active_trans == 0 && trx->conc_state != TRX_NOT_STARTED) {
- fprintf(stderr,
-"InnoDB: Error: trx->active_trans == 0\n"
-"InnoDB: but trx->conc_state != TRX_NOT_STARTED\n");
+ sql_print_error("trx->active_trans == 0, but trx->conc_state != "
+ "TRX_NOT_STARTED");
}
if (all
diff --git a/sql/ha_innodb.h b/sql/ha_innodb.h
index b0a463798d9..3ff925e30de 100644
--- a/sql/ha_innodb.h
+++ b/sql/ha_innodb.h
@@ -244,6 +244,7 @@ extern ulong srv_n_spin_wait_rounds;
extern ulong srv_n_free_tickets_to_enter;
extern ulong srv_thread_sleep_delay;
extern ulong srv_thread_concurrency;
+extern ulong srv_commit_concurrency;
}
extern TYPELIB innobase_lock_typelib;
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 959b8d100bb..9fb1a25cea7 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -2921,7 +2921,16 @@ void ha_ndbcluster::info(uint flag)
errkey= m_dupkey;
}
if (flag & HA_STATUS_AUTO)
+ {
DBUG_PRINT("info", ("HA_STATUS_AUTO"));
+ if (m_table)
+ {
+ Ndb *ndb= get_ndb();
+
+ auto_increment_value=
+ ndb->readAutoIncrementValue((const NDBTAB *) m_table);
+ }
+ }
DBUG_VOID_RETURN;
}
@@ -3243,7 +3252,10 @@ int ha_ndbcluster::external_lock(THD *thd, int lock_type)
DBUG_PRINT("info", ("Table schema version: %d",
tab->getObjectVersion()));
// Check if thread has stale local cache
- if (tab->getObjectStatus() == NdbDictionary::Object::Invalid)
+ // New transaction must not use old tables... (trans != 0)
+ // Running might...
+ if ((trans && tab->getObjectStatus() != NdbDictionary::Object::Retrieved)
+ || tab->getObjectStatus() == NdbDictionary::Object::Invalid)
{
invalidate_dictionary_cache(FALSE);
if (!(tab= dict->getTable(m_tabname, &tab_info)))
@@ -6947,6 +6959,8 @@ ha_ndbcluster::build_scan_filter_predicate(Ndb_cond * &cond,
break;
Ndb_item *a= cond->next->ndb_item;
Ndb_item *b, *field, *value= NULL;
+ LINT_INIT(field);
+
switch (cond->ndb_item->argument_count()) {
case 1:
field=
diff --git a/sql/handler.cc b/sql/handler.cc
index 3c64d33c371..451615bead1 100644
--- a/sql/handler.cc
+++ b/sql/handler.cc
@@ -609,8 +609,8 @@ void trans_register_ha(THD *thd, bool all, handlerton *ht_arg)
trans->ht[trans->nht++]=ht_arg;
DBUG_ASSERT(*ht == ht_arg);
trans->no_2pc|=(ht_arg->prepare==0);
- if (thd->transaction.xid.is_null())
- thd->transaction.xid.set(thd->query_id);
+ if (thd->transaction.xid_state.xid.is_null())
+ thd->transaction.xid_state.xid.set(thd->query_id);
DBUG_VOID_RETURN;
}
@@ -657,7 +657,7 @@ int ha_commit_trans(THD *thd, bool all)
THD_TRANS *trans= all ? &thd->transaction.all : &thd->transaction.stmt;
bool is_real_trans= all || thd->transaction.all.nht == 0;
handlerton **ht= trans->ht;
- my_xid xid= thd->transaction.xid.get_my_xid();
+ my_xid xid= thd->transaction.xid_state.xid.get_my_xid();
DBUG_ENTER("ha_commit_trans");
if (thd->in_sub_stmt)
@@ -757,7 +757,7 @@ int ha_commit_one_phase(THD *thd, bool all)
trans->nht=0;
trans->no_2pc=0;
if (is_real_trans)
- thd->transaction.xid.null();
+ thd->transaction.xid_state.xid.null();
if (all)
{
#ifdef HAVE_QUERY_CACHE
@@ -813,7 +813,7 @@ int ha_rollback_trans(THD *thd, bool all)
trans->nht=0;
trans->no_2pc=0;
if (is_real_trans)
- thd->transaction.xid.null();
+ thd->transaction.xid_state.xid.null();
if (all)
{
thd->variables.tx_isolation=thd->session_tx_isolation;
@@ -1007,6 +1007,7 @@ int ha_recover(HASH *commit_list)
char buf[XIDDATASIZE*4+6]; // see xid_to_str
sql_print_information("ignore xid %s", xid_to_str(buf, list+i));
#endif
+ xid_cache_insert(list+i, XA_PREPARED);
found_foreign_xids++;
continue;
}
@@ -1070,10 +1071,8 @@ bool mysql_xa_recover(THD *thd)
{
List<Item> field_list;
Protocol *protocol= thd->protocol;
- handlerton **ht= handlertons, **end_ht=ht+total_ha;
- bool error=TRUE;
- int len, got;
- XID *list=0;
+ int i=0;
+ XID_STATE *xs;
DBUG_ENTER("mysql_xa_recover");
field_list.push_back(new Item_int("formatID",0,11));
@@ -1083,48 +1082,30 @@ bool mysql_xa_recover(THD *thd)
if (protocol->send_fields(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
- DBUG_RETURN(TRUE);
-
- for (len= MAX_XID_LIST_SIZE ; list==0 && len > MIN_XID_LIST_SIZE; len/=2)
- {
- list=(XID *)my_malloc(len*sizeof(XID), MYF(0));
- }
- if (!list)
- {
- my_error(ER_OUTOFMEMORY, MYF(0), len);
DBUG_RETURN(1);
- }
- for ( ; ht < end_ht ; ht++)
+ pthread_mutex_lock(&LOCK_xid_cache);
+ while ((xs= (XID_STATE*)hash_element(&xid_cache, i++)))
{
- if (!(*ht)->recover)
- continue;
- while ((got=(*(*ht)->recover)(list, len)) > 0 )
+ if (xs->xa_state==XA_PREPARED)
{
- XID *xid, *end;
- for (xid=list, end=list+got; xid < end; xid++)
+ protocol->prepare_for_resend();
+ protocol->store_longlong((longlong)xs->xid.formatID, FALSE);
+ protocol->store_longlong((longlong)xs->xid.gtrid_length, FALSE);
+ protocol->store_longlong((longlong)xs->xid.bqual_length, FALSE);
+ protocol->store(xs->xid.data, xs->xid.gtrid_length+xs->xid.bqual_length,
+ &my_charset_bin);
+ if (protocol->write())
{
- if (xid->get_my_xid())
- continue; // skip "our" xids
- protocol->prepare_for_resend();
- protocol->store_longlong((longlong)xid->formatID, FALSE);
- protocol->store_longlong((longlong)xid->gtrid_length, FALSE);
- protocol->store_longlong((longlong)xid->bqual_length, FALSE);
- protocol->store(xid->data, xid->gtrid_length+xid->bqual_length,
- &my_charset_bin);
- if (protocol->write())
- goto err;
+ pthread_mutex_unlock(&LOCK_xid_cache);
+ DBUG_RETURN(1);
}
- if (got < len)
- break;
}
}
- error=FALSE;
+ pthread_mutex_unlock(&LOCK_xid_cache);
send_eof(thd);
-err:
- my_free((gptr)list, MYF(0));
- DBUG_RETURN(error);
+ DBUG_RETURN(0);
}
/*
@@ -1827,7 +1808,7 @@ void handler::print_error(int error, myf errflag)
}
case HA_ERR_NULL_IN_SPATIAL:
textno= ER_UNKNOWN_ERROR;
- DBUG_VOID_RETURN;
+ break;
case HA_ERR_FOUND_DUPP_UNIQUE:
textno=ER_DUP_UNIQUE;
break;
@@ -1850,8 +1831,8 @@ void handler::print_error(int error, myf errflag)
textno=ER_CRASHED_ON_REPAIR;
break;
case HA_ERR_OUT_OF_MEM:
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), errflag);
- DBUG_VOID_RETURN;
+ textno=ER_OUT_OF_RESOURCES;
+ break;
case HA_ERR_WRONG_COMMAND:
textno=ER_ILLEGAL_HA;
break;
@@ -1862,10 +1843,8 @@ void handler::print_error(int error, myf errflag)
textno=ER_UNSUPPORTED_EXTENSION;
break;
case HA_ERR_RECORD_FILE_FULL:
- textno=ER_RECORD_FILE_FULL;
- break;
case HA_ERR_INDEX_FILE_FULL:
- textno= errno;
+ textno=ER_RECORD_FILE_FULL;
break;
case HA_ERR_LOCK_WAIT_TIMEOUT:
textno=ER_LOCK_WAIT_TIMEOUT;
diff --git a/sql/handler.h b/sql/handler.h
index 90f3709d2bb..38bb79dbdbc 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -240,11 +240,11 @@ struct xid_t {
char data[XIDDATASIZE]; // not \0-terminated !
bool eq(struct xid_t *xid)
- { return !memcmp(this, xid, sizeof(long)*3+gtrid_length+bqual_length); }
+ { return !memcmp(this, xid, length()); }
bool eq(long g, long b, const char *d)
{ return g == gtrid_length && b == bqual_length && !memcmp(d, data, g+b); }
void set(struct xid_t *xid)
- { memcpy(this, xid, sizeof(long)*3+xid->gtrid_length+xid->bqual_length); }
+ { memcpy(this, xid, xid->length()); }
void set(long f, const char *g, long gl, const char *b, long bl)
{
formatID= f;
@@ -283,6 +283,11 @@ struct xid_t {
!memcmp(data, MYSQL_XID_PREFIX, MYSQL_XID_PREFIX_LEN) ?
quick_get_my_xid() : 0;
}
+ uint length()
+ {
+ return sizeof(formatID)+sizeof(gtrid_length)+sizeof(bqual_length)+
+ gtrid_length+bqual_length;
+ }
};
typedef struct xid_t XID;
diff --git a/sql/init.cc b/sql/init.cc
index 4beb8db0c6f..e53eeab8902 100644
--- a/sql/init.cc
+++ b/sql/init.cc
@@ -39,12 +39,11 @@ void unireg_init(ulong options)
#endif
VOID(strmov(reg_ext,".frm"));
- specialflag=SPECIAL_SAME_DB_NAME;
+ specialflag=SPECIAL_SAME_DB_NAME | options; /* Set options from argv */
/* Make a tab of powers of 10 */
for (i=0,nr=1.0; i < array_elements(log_10) ; i++)
{ /* It's used by filesort... */
log_10[i]= nr ; nr*= 10.0;
}
- specialflag|=options; /* Set options from argv */
DBUG_VOID_RETURN;
}
diff --git a/sql/item.cc b/sql/item.cc
index d00fcb75d32..f25734fb549 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -1016,14 +1016,18 @@ bool DTCollation::aggregate(DTCollation &dt, uint flags)
; // Do nothing
}
else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
- derivation < dt.derivation &&
- collation->state & MY_CS_UNICODE)
+ collation->state & MY_CS_UNICODE &&
+ (derivation < dt.derivation ||
+ (derivation == dt.derivation &&
+ !(dt.collation->state & MY_CS_UNICODE))))
{
// Do nothing
}
else if ((flags & MY_COLL_ALLOW_SUPERSET_CONV) &&
- dt.derivation < derivation &&
- dt.collation->state & MY_CS_UNICODE)
+ dt.collation->state & MY_CS_UNICODE &&
+ (dt.derivation < derivation ||
+ (dt.derivation == derivation &&
+ !(collation->state & MY_CS_UNICODE))))
{
set(dt);
}
@@ -2134,7 +2138,7 @@ bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
CHARSET_INFO *tocs= thd->variables.collation_connection;
uint32 dummy_offset;
- value.cs_info.character_set_client= fromcs;
+ value.cs_info.character_set_of_placeholder= fromcs;
/*
Setup source and destination character sets so that they
are different only if conversion is necessary: this will
@@ -2452,10 +2456,17 @@ const String *Item_param::query_val_str(String* str) const
buf= str->c_ptr_quick();
ptr= buf;
- *ptr++= '\'';
- ptr+= escape_string_for_mysql(str_value.charset(), ptr, 0,
- str_value.ptr(), str_value.length());
- *ptr++= '\'';
+ if (value.cs_info.character_set_client->escape_with_backslash_is_dangerous)
+ {
+ ptr= str_to_hex(ptr, str_value.ptr(), str_value.length());
+ }
+ else
+ {
+ *ptr++= '\'';
+ ptr+= escape_string_for_mysql(str_value.charset(), ptr, 0,
+ str_value.ptr(), str_value.length());
+ *ptr++='\'';
+ }
str->length((uint32) (ptr - buf));
break;
}
@@ -2485,10 +2496,10 @@ bool Item_param::convert_str_value(THD *thd)
here only if conversion is really necessary.
*/
if (value.cs_info.final_character_set_of_str_value !=
- value.cs_info.character_set_client)
+ value.cs_info.character_set_of_placeholder)
{
rc= thd->convert_string(&str_value,
- value.cs_info.character_set_client,
+ value.cs_info.character_set_of_placeholder,
value.cs_info.final_character_set_of_str_value);
}
else
@@ -3067,7 +3078,9 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
expression to 'reference', i.e. it substitute that expression instead
of this Item_field
*/
- if ((from_field= find_field_in_tables(thd, this, context->table_list,
+ if ((from_field= find_field_in_tables(thd, this,
+ context->first_name_resolution_table,
+ context->last_name_resolution_table,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
!any_privileges &&
@@ -3076,13 +3089,13 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
not_found_field)
{
/*
- If there is an outer contexts (outer selects, but current select is
+ If there are outer contexts (outer selects, but current select is
not derived table or view) try to resolve this reference in the
outer contexts.
We treat each subselect as a separate namespace, so that different
- subselects may contain columns with the same names. The subselects are
- searched starting from the innermost.
+ subselects may contain columns with the same names. The subselects
+ are searched starting from the innermost.
*/
Name_resolution_context *last_checked_context= context;
Item **ref= (Item **) not_found_item;
@@ -3111,7 +3124,10 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
(!select->with_sum_func &&
select->group_list.elements == 0)) &&
(from_field= find_field_in_tables(thd, this,
- outer_context->table_list,
+ outer_context->
+ first_name_resolution_table,
+ outer_context->
+ last_name_resolution_table,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
outer_context->
@@ -3186,7 +3202,9 @@ bool Item_field::fix_fields(THD *thd, Item **reference)
else
{
/* Call find_field_in_tables only to report the error */
- find_field_in_tables(thd, this, context->table_list,
+ find_field_in_tables(thd, this,
+ context->first_name_resolution_table,
+ context->last_name_resolution_table,
reference, REPORT_ALL_ERRORS,
!any_privileges &&
context->check_privileges, TRUE);
@@ -4359,7 +4377,10 @@ bool Item_ref::fix_fields(THD *thd, Item **reference)
expression instead of this Item_ref
*/
from_field= find_field_in_tables(thd, this,
- outer_context->table_list,
+ outer_context->
+ first_name_resolution_table,
+ outer_context->
+ last_name_resolution_table,
reference,
IGNORE_EXCEPT_NON_UNIQUE,
outer_context->check_privileges,
@@ -4978,9 +4999,8 @@ void Item_trigger_field::setup_field(THD *thd, TABLE *table)
Try to find field by its name and if it will be found
set field_idx properly.
*/
- (void)find_field_in_real_table(thd, table, field_name,
- (uint) strlen(field_name),
- 0, 0, &field_idx);
+ (void)find_field_in_table(thd, table, field_name, (uint) strlen(field_name),
+ 0, 0, &field_idx);
thd->set_query_id= save_set_query_id;
triggers= table->triggers;
}
diff --git a/sql/item.h b/sql/item.h
index f0ffb160553..ebcd5a9da33 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -237,7 +237,7 @@ void view_error_processor(THD *thd, void *data);
structure before and after INSERT/CREATE and its SELECT to make correct
field name resolution.
*/
-struct Name_resolution_context
+struct Name_resolution_context: Sql_alloc
{
/*
The name resolution context to search in when an Item cannot be
@@ -254,6 +254,19 @@ struct Name_resolution_context
name resolution of different parts of the statement.
*/
TABLE_LIST *table_list;
+ /*
+ In most cases the two table references below replace 'table_list' above
+ for the purpose of name resolution. The first and last name resolution
+ table references allow us to search only in a sub-tree of the nested
+ join tree in a FROM clause. This is needed for NATURAL JOIN, JOIN ... USING
+ and JOIN ... ON.
+ */
+ TABLE_LIST *first_name_resolution_table;
+ /*
+ Last table to search in the list of leaf table references that begins
+ with first_name_resolution_table.
+ */
+ TABLE_LIST *last_name_resolution_table;
/*
SELECT_LEX item belong to, in case of merged VIEW it can differ from
@@ -293,11 +306,13 @@ struct Name_resolution_context
{
resolve_in_select_list= FALSE;
error_processor= &dummy_error_processor;
+ first_name_resolution_table= NULL;
+ last_name_resolution_table= NULL;
}
void resolve_in_table_list_only(TABLE_LIST *tables)
{
- table_list= tables;
+ table_list= first_name_resolution_table= tables;
resolve_in_select_list= FALSE;
}
@@ -657,7 +672,8 @@ public:
current value and pointer passed via parameter otherwise.
*/
virtual Item **this_item_addr(THD *thd, Item **addr) { return addr; }
- virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */
+ /* For SPs mostly. */
+ virtual Item *this_const_item() const { return const_cast<Item*>(this); }
// Row emulation
virtual uint cols() { return 1; }
@@ -828,6 +844,10 @@ public:
void print(String *str);
virtual bool change_context_processor(byte *cntx)
{ context= (Name_resolution_context *)cntx; return FALSE; }
+ friend bool insert_fields(THD *thd, Name_resolution_context *context,
+ const char *db_name,
+ const char *table_name, List_iterator<Item> *it,
+ bool any_privileges);
};
class Item_equal;
@@ -1006,6 +1026,7 @@ public:
struct CONVERSION_INFO
{
CHARSET_INFO *character_set_client;
+ CHARSET_INFO *character_set_of_placeholder;
/*
This points at character set of connection if conversion
to it is required (i. e. if placeholder typecode is not BLOB).
@@ -1129,7 +1150,8 @@ public:
void cleanup() {}
void print(String *str);
Item_num *neg() { value= -value; return this; }
- uint decimal_precision() const { return (uint)(max_length - test(value < 0)); }
+ uint decimal_precision() const
+ { return (uint)(max_length - test(value < 0)); }
bool eq(const Item *, bool binary_cmp) const;
};
@@ -1566,6 +1588,15 @@ public:
bool val_bool();
bool get_date(TIME *ltime, uint fuzzydate);
void print(String *str);
+ /*
+ we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE
+ */
+ table_map used_tables() const
+ {
+ return (depended_from ?
+ OUTER_REF_TABLE_BIT :
+ (*ref)->used_tables() | RAND_TABLE_BIT);
+ }
};
class Item_null_helper :public Item_ref_null_helper
@@ -1647,7 +1678,9 @@ public:
longlong val_int()
{
int err;
- return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(),str_value.length(),10, (char**) 0,&err);
+ return null_value ? LL(0) : my_strntoll(str_value.charset(),str_value.ptr(),
+ str_value.length(),10, (char**) 0,
+ &err);
}
String *val_str(String*);
my_decimal *val_decimal(my_decimal *);
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index d430d0d3c23..9443a2949d8 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -614,19 +614,38 @@ int Arg_comparator::compare_e_int_diff_signedness()
int Arg_comparator::compare_row()
{
int res= 0;
+ bool was_null= 0;
(*a)->bring_value();
(*b)->bring_value();
uint n= (*a)->cols();
for (uint i= 0; i<n; i++)
{
- if ((res= comparators[i].compare()))
- return res;
+ res= comparators[i].compare();
if (owner->null_value)
- return -1;
+ {
+ // NULL was compared
+ if (owner->abort_on_null)
+ return -1; // We do not need correct NULL returning
+ was_null= 1;
+ owner->null_value= 0;
+ res= 0; // continue comparison (maybe we will meet explicit difference)
+ }
+ else if (res)
+ return res;
}
- return res;
+ if (was_null)
+ {
+ /*
+ There was NULL(s) in comparison in some parts, but there was not
+ explicit difference in other parts, so we have to return NULL
+ */
+ owner->null_value= 1;
+ return -1;
+ }
+ return 0;
}
+
int Arg_comparator::compare_e_row()
{
(*a)->bring_value();
@@ -1903,6 +1922,8 @@ in_row::~in_row()
byte *in_row::get_value(Item *item)
{
tmp.store_value(item);
+ if (item->is_null())
+ return 0;
return (byte *)&tmp;
}
@@ -2791,7 +2812,7 @@ bool Item_func_like::fix_fields(THD *thd, Item **ref)
/* If we are on execution stage */
String *escape_str= escape_item->val_str(&tmp_value1);
escape= escape_str ? *(escape_str->ptr()) : '\\';
-
+
/*
We could also do boyer-more for non-const items, but as we would have to
recompute the tables for each row it's not worth it.
diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h
index 75c8411b844..6b0cf3e80c2 100644
--- a/sql/item_cmpfunc.h
+++ b/sql/item_cmpfunc.h
@@ -193,10 +193,11 @@ class Item_bool_func2 :public Item_int_func
protected:
Arg_comparator cmp;
String tmp_value1,tmp_value2;
+ bool abort_on_null;
public:
Item_bool_func2(Item *a,Item *b)
- :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1) {}
+ :Item_int_func(a,b), cmp(tmp_arg, tmp_arg+1), abort_on_null(FALSE) {}
void fix_length_and_dec();
void set_cmp_func()
{
@@ -210,6 +211,7 @@ public:
bool is_bool_func() { return 1; }
CHARSET_INFO *compare_collation() { return cmp.cmp_collation.collation; }
uint decimal_precision() const { return 1; }
+ void top_level_item() { abort_on_null=1; }
friend class Arg_comparator;
};
@@ -913,6 +915,11 @@ public:
longlong val_int();
const char *func_name() const { return "<is_not_null_test>"; }
void update_used_tables();
+ /*
+ we add RAND_TABLE_BIT to prevent moving this item from HAVING to WHERE
+ */
+ table_map used_tables() const
+ { return used_tables_cache | RAND_TABLE_BIT; }
};
diff --git a/sql/item_create.cc b/sql/item_create.cc
index b7d8d50f9b3..77476e41d0b 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -354,6 +354,12 @@ Item *create_func_sha(Item* a)
return new Item_func_sha(a);
}
+Item *create_func_sleep(Item* a)
+{
+ current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return new Item_func_sleep(a);
+}
+
Item *create_func_space(Item *a)
{
CHARSET_INFO *cs= current_thd->variables.collation_connection;
diff --git a/sql/item_create.h b/sql/item_create.h
index 0a9af144ec0..d757318bfc1 100644
--- a/sql/item_create.h
+++ b/sql/item_create.h
@@ -83,6 +83,7 @@ Item *create_func_sec_to_time(Item* a);
Item *create_func_sign(Item* a);
Item *create_func_sin(Item* a);
Item *create_func_sha(Item* a);
+Item *create_func_sleep(Item* a);
Item *create_func_soundex(Item* a);
Item *create_func_space(Item *);
Item *create_func_sqrt(Item* a);
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 71e0f29ffc7..d3b53db2d54 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -1873,6 +1873,9 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref)
Allocate rand structure once: we must use thd->current_arena
to create rand in proper mem_root if it's a prepared statement or
stored procedure.
+
+ No need to send a Rand log event if seed was given eg: RAND(seed),
+ as it will be replicated in the query as such.
*/
if (!rand && !(rand= (struct rand_struct*)
thd->current_arena->alloc(sizeof(*rand))))
@@ -1895,16 +1898,16 @@ bool Item_func_rand::fix_fields(THD *thd,Item **ref)
else
{
/*
- No need to send a Rand log event if seed was given eg: RAND(seed),
- as it will be replicated in the query as such.
-
Save the seed only the first time RAND() is used in the query
Once events are forwarded rather than recreated,
the following can be skipped if inside the slave thread
*/
- thd->rand_used=1;
- thd->rand_saved_seed1=thd->rand.seed1;
- thd->rand_saved_seed2=thd->rand.seed2;
+ if (!thd->rand_used)
+ {
+ thd->rand_used= 1;
+ thd->rand_saved_seed1= thd->rand.seed1;
+ thd->rand_saved_seed2= thd->rand.seed2;
+ }
rand= &thd->rand;
}
return FALSE;
@@ -3259,6 +3262,43 @@ void Item_func_benchmark::print(String *str)
str->append(')');
}
+/* This function is just used to create tests with time gaps */
+
+longlong Item_func_sleep::val_int()
+{
+ THD *thd= current_thd;
+ struct timespec abstime;
+ pthread_cond_t cond;
+ int error;
+
+ DBUG_ASSERT(fixed == 1);
+
+ double time= args[0]->val_real();
+ set_timespec_nsec(abstime, (ulonglong)(time * ULL(1000000000)));
+
+ pthread_cond_init(&cond, NULL);
+ pthread_mutex_lock(&LOCK_user_locks);
+
+ thd->mysys_var->current_mutex= &LOCK_user_locks;
+ thd->mysys_var->current_cond= &cond;
+
+ while (!thd->killed &&
+ (error= pthread_cond_timedwait(&cond, &LOCK_user_locks,
+ &abstime)) != ETIMEDOUT &&
+ error != EINVAL) ;
+
+ pthread_mutex_lock(&thd->mysys_var->mutex);
+ thd->mysys_var->current_mutex= 0;
+ thd->mysys_var->current_cond= 0;
+ pthread_mutex_unlock(&thd->mysys_var->mutex);
+
+ pthread_mutex_unlock(&LOCK_user_locks);
+ pthread_cond_destroy(&cond);
+
+ return (error == ETIMEDOUT) ? 0 : 1;
+}
+
+
#define extra_size sizeof(double)
static user_var_entry *get_variable(HASH *hash, LEX_STRING &name,
@@ -4654,10 +4694,9 @@ Item_func_sp::execute(Item **itp)
{
DBUG_ENTER("Item_func_sp::execute");
THD *thd= current_thd;
- ulong old_client_capabilites;
int res= -1;
- bool save_in_sub_stmt= thd->in_sub_stmt;
- my_bool save_no_send_ok;
+ Sub_statement_state statement_state;
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
st_sp_security_context save_ctx;
#endif
@@ -4668,38 +4707,21 @@ Item_func_sp::execute(Item **itp)
goto error;
}
- old_client_capabilites= thd->client_capabilities;
- thd->client_capabilities &= ~CLIENT_MULTI_RESULTS;
-
-#ifndef EMBEDDED_LIBRARY
- save_no_send_ok= thd->net.no_send_ok;
- thd->net.no_send_ok= TRUE;
-#endif
-
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_routine_access(thd, EXECUTE_ACL,
m_sp->m_db.str, m_sp->m_name.str, 0, 0))
- goto error_check;
+ goto error;
sp_change_security_context(thd, m_sp, &save_ctx);
if (save_ctx.changed &&
check_routine_access(thd, EXECUTE_ACL,
m_sp->m_db.str, m_sp->m_name.str, 0, 0))
goto error_check_ctx;
#endif
- /*
- Like for SPs, we don't binlog the substatements. If the statement which
- called this function is an update statement, it will be binlogged; but if
- it's not (e.g. SELECT myfunc()) it won't be binlogged (documented known
- problem).
- */
-
- tmp_disable_binlog(thd); /* don't binlog the substatements */
- thd->in_sub_stmt= TRUE;
+ thd->reset_sub_statement_state(&statement_state, SUB_STMT_FUNCTION);
res= m_sp->execute_function(thd, args, arg_count, itp);
+ thd->restore_sub_statement_state(&statement_state);
- thd->in_sub_stmt= save_in_sub_stmt;
- reenable_binlog(thd);
if (res && mysql_bin_log.is_open() &&
(m_sp->m_chistics->daccess == SP_CONTAINS_SQL ||
m_sp->m_chistics->daccess == SP_MODIFIES_SQL_DATA))
@@ -4712,15 +4734,6 @@ error_check_ctx:
sp_restore_security_context(thd, m_sp, &save_ctx);
#endif
- thd->client_capabilities|= old_client_capabilites & CLIENT_MULTI_RESULTS;
-
-error_check:
-#ifndef EMBEDDED_LIBRARY
- thd->net.no_send_ok= save_no_send_ok;
-#endif
-
- thd->client_capabilities|= old_client_capabilites & CLIENT_MULTI_RESULTS;
-
error:
DBUG_RETURN(res);
}
diff --git a/sql/item_func.h b/sql/item_func.h
index e8db9d70ae7..384cb486f7c 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -874,6 +874,7 @@ public:
}
};
+
class Item_func_benchmark :public Item_int_func
{
ulong loop_count;
@@ -888,6 +889,22 @@ public:
};
+class Item_func_sleep :public Item_int_func
+{
+public:
+ Item_func_sleep(Item *a) :Item_int_func(a) {}
+ bool const_item() const { return 0; }
+ const char *func_name() const { return "sleep"; }
+ void update_used_tables()
+ {
+ Item_int_func::update_used_tables();
+ used_tables_cache|= RAND_TABLE_BIT;
+ }
+ longlong val_int();
+};
+
+
+
#ifdef HAVE_DLOPEN
class Item_udf_func :public Item_func
diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc
index 256090d3e61..094a0c56319 100644
--- a/sql/item_strfunc.cc
+++ b/sql/item_strfunc.cc
@@ -1177,11 +1177,23 @@ String *Item_func_substr_index::val_str(String *str)
}
}
else
- { // Start counting at end
- for (offset=res->length() ; ; offset-=delimeter_length-1)
+ {
+ /*
+ Negative index, start counting at the end
+ */
+ for (offset=res->length(); offset ;)
{
+ /*
+ this call will result in finding the position pointing to one
+ address space less than where the found substring is located
+ in res
+ */
if ((int) (offset=res->strrstr(*delimeter,offset)) < 0)
return res; // Didn't find, return org string
+ /*
+ At this point, we've searched for the substring
+ the number of times as supplied by the index value
+ */
if (!++count)
{
offset+=delimeter_length;
@@ -1558,6 +1570,7 @@ Item *Item_func_sysconst::safe_charset_converter(CHARSET_INFO *tocs)
return NULL;
}
conv->str_value.copy();
+ conv->str_value.mark_as_const();
return conv;
}
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 903f4c953a2..7e47de494db 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -969,6 +969,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
argument (reference) to fix_fields()
*/
select_lex->where= join->conds= and_items(join->conds, item);
+ select_lex->where->top_level_item();
/*
we do not check join->conds->fixed, because Item_and can't be fixed
after creation
@@ -1032,8 +1033,12 @@ Item_in_subselect::single_value_transformer(JOIN *join,
Item_subselect::trans_res
Item_in_subselect::row_value_transformer(JOIN *join)
{
- Item *item= 0;
SELECT_LEX *select_lex= join->select_lex;
+ Item *having_item= 0;
+ uint cols_num= left_expr->cols();
+ bool is_having_used= (join->having || select_lex->with_sum_func ||
+ select_lex->group_list.first ||
+ !select_lex->table_list.elements);
DBUG_ENTER("Item_in_subselect::row_value_transformer");
if (select_lex->item_list.elements != left_expr->cols())
@@ -1065,66 +1070,164 @@ Item_in_subselect::row_value_transformer(JOIN *join)
}
select_lex->uncacheable|= UNCACHEABLE_DEPENDENT;
+ if (is_having_used)
{
- uint n= left_expr->cols();
- List_iterator_fast<Item> li(select_lex->item_list);
- for (uint i= 0; i < n; i++)
+ /*
+ (l1, l2, l3) IN (SELECT v1, v2, v3 ... HAVING having) =>
+ EXISTS (SELECT ... HAVING having and
+ (l1 = v1 or is null v1) and
+ (l2 = v2 or is null v2) and
+ (l3 = v3 or is null v3) and
+ is_not_null_test(v1) and
+ is_not_null_test(v2) and
+ is_not_null_test(v3))
+ where is_not_null_test used to register nulls in case if we have
+ not found matching to return correct NULL value
+ */
+ Item *item_having_part2= 0;
+ for (uint i= 0; i < cols_num; i++)
{
DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed);
if (select_lex->ref_pointer_array[i]->
check_cols(left_expr->el(i)->cols()))
DBUG_RETURN(RES_ERROR);
- Item *func= new Item_ref_null_helper(&select_lex->context,
- this,
- select_lex->ref_pointer_array+i,
- (char *) "<no matter>",
- (char *) "<list ref>");
- func=
- eq_creator.create(new Item_direct_ref(&select_lex->context,
- (*optimizer->get_cache())->
- addr(i),
- (char *)"<no matter>",
- (char *)in_left_expr_name),
- func);
- item= and_items(item, func);
+ Item *item_eq=
+ new Item_func_eq(new
+ Item_direct_ref(&select_lex->context,
+ (*optimizer->get_cache())->
+ addr(i),
+ (char *)"<no matter>",
+ (char *)in_left_expr_name),
+ new
+ Item_direct_ref(&select_lex->context,
+ select_lex->ref_pointer_array + i,
+ (char *)"<no matter>",
+ (char *)"<list ref>")
+ );
+ Item *item_isnull=
+ new Item_func_isnull(new
+ Item_direct_ref(&select_lex->context,
+ select_lex->
+ ref_pointer_array+i,
+ (char *)"<no matter>",
+ (char *)"<list ref>")
+ );
+ having_item=
+ and_items(having_item,
+ new Item_cond_or(item_eq, item_isnull));
+ item_having_part2=
+ and_items(item_having_part2,
+ new
+ Item_is_not_null_test(this,
+ new
+ Item_direct_ref(&select_lex->context,
+ select_lex->
+ ref_pointer_array + i,
+ (char *)"<no matter>",
+ (char *)"<list ref>")
+ )
+ );
+ item_having_part2->top_level_item();
}
+ having_item= and_items(having_item, item_having_part2);
+ having_item->top_level_item();
}
- if (join->having || select_lex->with_sum_func ||
- select_lex->group_list.first ||
- !select_lex->table_list.elements)
+ else
{
/*
- AND can't be changed during fix_fields()
- we can assign select_lex->having here, and pass 0 as last
- argument (reference) to fix_fields()
+ (l1, l2, l3) IN (SELECT v1, v2, v3 ... WHERE where) =>
+ EXISTS (SELECT ... WHERE where and
+ (l1 = v1 or is null v1) and
+ (l2 = v2 or is null v2) and
+ (l3 = v3 or is null v3)
+ HAVING is_not_null_test(v1) and
+ is_not_null_test(v2) and
+ is_not_null_test(v3))
+ where is_not_null_test register NULLs values but reject rows
+
+ in case when we do not need correct NULL, we have simplier construction:
+ EXISTS (SELECT ... WHERE where and
+ (l1 = v1) and
+ (l2 = v2) and
+ (l3 = v3)
*/
- select_lex->having= join->having= and_items(join->having, item);
- select_lex->having_fix_field= 1;
+ Item *where_item= 0;
+ for (uint i= 0; i < cols_num; i++)
+ {
+ Item *item, *item_isnull;
+ DBUG_ASSERT(left_expr->fixed && select_lex->ref_pointer_array[i]->fixed);
+ if (select_lex->ref_pointer_array[i]->
+ check_cols(left_expr->el(i)->cols()))
+ DBUG_RETURN(RES_ERROR);
+ item=
+ new Item_func_eq(new
+ Item_direct_ref(&select_lex->context,
+ (*optimizer->get_cache())->
+ addr(i),
+ (char *)"<no matter>",
+ (char *)in_left_expr_name),
+ new
+ Item_direct_ref(&select_lex->context,
+ select_lex->
+ ref_pointer_array+i,
+ (char *)"<no matter>",
+ (char *)"<list ref>")
+ );
+ if (!abort_on_null)
+ {
+ having_item=
+ and_items(having_item,
+ new
+ Item_is_not_null_test(this,
+ new
+ Item_direct_ref(&select_lex->context,
+ select_lex->
+ ref_pointer_array + i,
+ (char *)"<no matter>",
+ (char *)"<list ref>")
+ )
+ );
+ item_isnull= new
+ Item_func_isnull(new
+ Item_direct_ref(&select_lex->context,
+ select_lex->
+ ref_pointer_array+i,
+ (char *)"<no matter>",
+ (char *)"<list ref>")
+ );
+
+ item= new Item_cond_or(item, item_isnull);
+ }
+
+ where_item= and_items(where_item, item);
+ }
/*
- join->having can't be fixed after creation, so we do not check
- join->having->fixed
+ AND can't be changed during fix_fields()
+ we can assign select_lex->where here, and pass 0 as last
+ argument (reference) to fix_fields()
*/
- if (join->having->fix_fields(thd, 0))
- {
- select_lex->having_fix_field= 0;
+ select_lex->where= join->conds= and_items(join->conds, where_item);
+ select_lex->where->top_level_item();
+ if (join->conds->fix_fields(thd, 0))
DBUG_RETURN(RES_ERROR);
- }
- select_lex->having_fix_field= 0;
}
- else
+ if (having_item)
{
+ bool res;
+ select_lex->having= join->having= and_items(join->having, having_item);
+ select_lex->having->top_level_item();
/*
AND can't be changed during fix_fields()
we can assign select_lex->having here, and pass 0 as last
argument (reference) to fix_fields()
*/
- select_lex->where= join->conds= and_items(join->conds, item);
- /*
- join->conds can't be fixed after creation, so we do not check
- join->conds->fixed
- */
- if (join->conds->fix_fields(thd, 0))
+ select_lex->having_fix_field= 1;
+ res= join->having->fix_fields(thd, 0);
+ select_lex->having_fix_field= 0;
+ if (res)
+ {
DBUG_RETURN(RES_ERROR);
+ }
}
DBUG_RETURN(RES_OK);
@@ -1271,6 +1374,14 @@ void Item_allany_subselect::print(String *str)
}
+void subselect_engine::set_thd(THD *thd_arg)
+{
+ thd= thd_arg;
+ if (result)
+ result->set_thd(thd_arg);
+}
+
+
subselect_single_select_engine::
subselect_single_select_engine(st_select_lex *select,
select_subselect *result,
diff --git a/sql/item_subselect.h b/sql/item_subselect.h
index 0b5736169fa..46623f76170 100644
--- a/sql/item_subselect.h
+++ b/sql/item_subselect.h
@@ -299,8 +299,11 @@ public:
virtual ~subselect_engine() {}; // to satisfy compiler
virtual void cleanup()= 0;
- // set_thd should be called before prepare()
- void set_thd(THD *thd_arg) { thd= thd_arg; }
+ /*
+ Also sets "thd" for subselect_engine::result.
+ Should be called before prepare().
+ */
+ void set_thd(THD *thd_arg);
THD * get_thd() { return thd; }
virtual int prepare()= 0;
virtual void fix_length_and_dec(Item_cache** row)= 0;
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index bbcea4705fa..e86d4f0d8ba 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -2291,7 +2291,7 @@ bool Item_sum_count_distinct::setup(THD *thd)
DBUG_ASSERT(table == 0);
if (!(table= create_tmp_table(thd, tmp_table_param, list, (ORDER*) 0, 1,
0,
- select_lex->options | thd->options,
+ (select_lex->options | thd->options),
HA_POS_ERROR, (char*)"")))
return TRUE;
table->file->extra(HA_EXTRA_NO_ROWS); // Don't update rows
@@ -3073,7 +3073,7 @@ bool Item_func_group_concat::setup(THD *thd)
*/
if (!(table= create_tmp_table(thd, tmp_table_param, all_fields,
(ORDER*) 0, 0, TRUE,
- select_lex->options | thd->options,
+ (select_lex->options | thd->options),
HA_POS_ERROR, (char*) "")))
DBUG_RETURN(TRUE);
table->file->extra(HA_EXTRA_NO_ROWS);
diff --git a/sql/item_timefunc.cc b/sql/item_timefunc.cc
index a6fbbee6f23..0d9e23ff0a1 100644
--- a/sql/item_timefunc.cc
+++ b/sql/item_timefunc.cc
@@ -1476,9 +1476,9 @@ void Item_func_now_utc::store_now_in_TIME(TIME *now_time)
bool Item_func_now::get_date(TIME *res,
- uint fuzzy_date __attribute__((unused)))
+ uint fuzzy_date __attribute__((unused)))
{
- *res=ltime;
+ *res= ltime;
return 0;
}
@@ -1491,6 +1491,70 @@ int Item_func_now::save_in_field(Field *to, bool no_conversions)
}
+/*
+ Converts current time in my_time_t to TIME represenatation for local
+ time zone. Defines time zone (local) used for whole SYSDATE function.
+*/
+void Item_func_sysdate_local::store_now_in_TIME(TIME *now_time)
+{
+ THD *thd= current_thd;
+ thd->variables.time_zone->gmt_sec_to_TIME(now_time, time(NULL));
+ thd->time_zone_used= 1;
+}
+
+
+String *Item_func_sysdate_local::val_str(String *str)
+{
+ DBUG_ASSERT(fixed == 1);
+ store_now_in_TIME(&ltime);
+ buff_length= (uint) my_datetime_to_str(&ltime, buff);
+ str_value.set(buff, buff_length, &my_charset_bin);
+ return &str_value;
+}
+
+
+longlong Item_func_sysdate_local::val_int()
+{
+ DBUG_ASSERT(fixed == 1);
+ store_now_in_TIME(&ltime);
+ return (longlong) TIME_to_ulonglong_datetime(&ltime);
+}
+
+
+double Item_func_sysdate_local::val_real()
+{
+ DBUG_ASSERT(fixed == 1);
+ store_now_in_TIME(&ltime);
+ return (longlong) TIME_to_ulonglong_datetime(&ltime);
+}
+
+
+void Item_func_sysdate_local::fix_length_and_dec()
+{
+ decimals= 0;
+ collation.set(&my_charset_bin);
+ max_length= MAX_DATETIME_WIDTH*MY_CHARSET_BIN_MB_MAXLEN;
+}
+
+
+bool Item_func_sysdate_local::get_date(TIME *res,
+ uint fuzzy_date __attribute__((unused)))
+{
+ store_now_in_TIME(&ltime);
+ *res= ltime;
+ return 0;
+}
+
+
+int Item_func_sysdate_local::save_in_field(Field *to, bool no_conversions)
+{
+ store_now_in_TIME(&ltime);
+ to->set_notnull();
+ to->store_time(&ltime, MYSQL_TIMESTAMP_DATETIME);
+ return 0;
+}
+
+
String *Item_func_sec_to_time::val_str(String *str)
{
DBUG_ASSERT(fixed == 1);
@@ -1938,7 +2002,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
daynr= calc_daynr(ltime->year,ltime->month,1) + days;
/* Day number from year 0 to 9999-12-31 */
if ((ulonglong) daynr >= MAX_DAY_NUMBER)
- goto null_date;
+ goto invalid_date;
get_date_from_daynr((long) daynr, &ltime->year, &ltime->month,
&ltime->day);
break;
@@ -1949,13 +2013,13 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
sign * (long) interval.day);
/* Daynumber from year 0 to 9999-12-31 */
if ((ulong) period >= MAX_DAY_NUMBER)
- goto null_date;
+ goto invalid_date;
get_date_from_daynr((long) period,&ltime->year,&ltime->month,&ltime->day);
break;
case INTERVAL_YEAR:
ltime->year+= sign * (long) interval.year;
if ((ulong) ltime->year >= 10000L)
- goto null_date;
+ goto invalid_date;
if (ltime->month == 2 && ltime->day == 29 &&
calc_days_in_year(ltime->year) != 366)
ltime->day=28; // Was leap-year
@@ -1966,7 +2030,7 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
period= (ltime->year*12 + sign * (long) interval.year*12 +
ltime->month-1 + sign * (long) interval.month);
if ((ulong) period >= 120000L)
- goto null_date;
+ goto invalid_date;
ltime->year= (uint) (period / 12);
ltime->month= (uint) (period % 12L)+1;
/* Adjust day if the new month doesn't have enough days */
@@ -1982,6 +2046,11 @@ bool Item_date_add_interval::get_date(TIME *ltime, uint fuzzy_date)
}
return 0; // Ok
+invalid_date:
+ push_warning_printf(current_thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_DATETIME_FUNCTION_OVERFLOW,
+ ER(ER_DATETIME_FUNCTION_OVERFLOW),
+ "datetime");
null_date:
return (null_value=1);
}
diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h
index 107d12e6da2..4602088a5f5 100644
--- a/sql/item_timefunc.h
+++ b/sql/item_timefunc.h
@@ -446,6 +446,7 @@ public:
class Item_func_now :public Item_date_func
{
+protected:
longlong value;
char buff[20*2+32]; // +32 to make my_snprintf_{8bit|ucs2} happy
uint buff_length;
@@ -485,6 +486,32 @@ public:
};
+/*
+ This is like NOW(), but always uses the real current time, not the
+ query_start(). This matches the Oracle behavior.
+*/
+class Item_func_sysdate_local :public Item_func_now
+{
+public:
+ Item_func_sysdate_local() :Item_func_now() {}
+ Item_func_sysdate_local(Item *a) :Item_func_now(a) {}
+ bool const_item() const { return 0; }
+ const char *func_name() const { return "sysdate"; }
+ void store_now_in_TIME(TIME *now_time);
+ double val_real();
+ longlong val_int();
+ int save_in_field(Field *to, bool no_conversions);
+ String *val_str(String *str);
+ void fix_length_and_dec();
+ bool get_date(TIME *res, uint fuzzy_date);
+ void update_used_tables()
+ {
+ Item_func_now::update_used_tables();
+ used_tables_cache|= RAND_TABLE_BIT;
+ }
+};
+
+
class Item_func_from_days :public Item_date
{
public:
@@ -714,6 +741,12 @@ public:
{
return (new Field_date(maybe_null, name, t_arg, &my_charset_bin));
}
+ void fix_length_and_dec()
+ {
+ collation.set(&my_charset_bin);
+ max_length= 10;
+ maybe_null= 1;
+ }
};
@@ -854,7 +887,7 @@ class Item_func_timestamp_diff :public Item_int_func
public:
Item_func_timestamp_diff(Item *a,Item *b,interval_type type_arg)
:Item_int_func(a,b), int_type(type_arg) {}
- const char *func_name() const { return "timestamp_diff"; }
+ const char *func_name() const { return "timestampdiff"; }
longlong val_int();
void fix_length_and_dec()
{
diff --git a/sql/lex.h b/sql/lex.h
index 3160b9efdcb..f0a6c0047d2 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -748,6 +748,7 @@ static SYMBOL sql_functions[] = {
{ "SIN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sin)},
{ "SHA", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sha)},
{ "SHA1", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sha)},
+ { "SLEEP", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sleep)},
{ "SOUNDEX", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_soundex)},
{ "SPACE", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_space)},
{ "SQRT", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_sqrt)},
@@ -764,7 +765,7 @@ static SYMBOL sql_functions[] = {
{ "SUBSTRING_INDEX", SYM(SUBSTRING_INDEX)},
{ "SUBTIME", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_subtime)},
{ "SUM", SYM(SUM_SYM)},
- { "SYSDATE", SYM(NOW_SYM)},
+ { "SYSDATE", SYM(SYSDATE)},
{ "SYSTEM_USER", SYM(USER)},
{ "TAN", F_SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_tan)},
{ "TIME_FORMAT", F_SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_time_format)},
diff --git a/sql/lock.cc b/sql/lock.cc
index 29b6473d7d4..0aba7fddb51 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -848,10 +848,6 @@ static void print_lock_error(int error, const char *table)
So in this exceptional case the COMMIT should not be blocked by the FLUSH
TABLES WITH READ LOCK.
- TODO in MySQL 5.x: make_global_read_lock_block_commit() should be
- killable. Normally CPU does not spend a long time in this function (COMMITs
- are quite fast), but it would still be nice.
-
****************************************************************************/
volatile uint global_read_lock=0;
@@ -1002,7 +998,7 @@ bool make_global_read_lock_block_commit(THD *thd)
pthread_cond_wait(&COND_refresh, &LOCK_global_read_lock);
DBUG_EXECUTE_IF("make_global_read_lock_block_commit_loop",
protect_against_global_read_lock--;);
- if (error= thd->killed)
+ if ((error= test(thd->killed)))
global_read_lock_blocks_commit--; // undo what we did
else
thd->global_read_lock= MADE_GLOBAL_READ_LOCK_BLOCK_COMMIT;
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 29f1160466e..55006a7c9be 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -211,7 +211,7 @@ static inline int read_str(char **buf, char *buf_end, char **str,
/*
Transforms a string into "" or its expression in 0x... form.
*/
-static char *str_to_hex(char *to, char *from, uint len)
+char *str_to_hex(char *to, const char *from, uint len)
{
char *p= to;
if (len)
@@ -1329,10 +1329,20 @@ Query_log_event::Query_log_event(const char* buf, uint event_len,
}
}
+#if !defined(MYSQL_CLIENT) && defined(HAVE_QUERY_CACHE)
if (!(start= data_buf = (char*) my_malloc(catalog_len + 1 +
time_zone_len + 1 +
- data_len + 1, MYF(MY_WME))))
- DBUG_VOID_RETURN;
+ data_len + 1 +
+ QUERY_CACHE_FLAGS_SIZE +
+ db_len + 1,
+ MYF(MY_WME))))
+#else
+ if (!(start= data_buf = (char*) my_malloc(catalog_len + 1 +
+ time_zone_len + 1 +
+ data_len + 1,
+ MYF(MY_WME))))
+#endif
+ DBUG_VOID_RETURN;
if (catalog_len) // If catalog is given
{
if (likely(catalog_nz)) // true except if event comes from 5.0.0|1|2|3.
diff --git a/sql/log_event.h b/sql/log_event.h
index 9f4681ae2c5..1d8941e65ac 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -393,6 +393,10 @@ struct sql_ex_info
#define OPTIONS_WRITTEN_TO_BIN_LOG (OPTION_AUTO_IS_NULL | \
OPTION_NO_FOREIGN_KEY_CHECKS | OPTION_RELAXED_UNIQUE_CHECKS)
+#if OPTIONS_WRITTEN_TO_BIN_LOG != ((1L << 14) | (1L << 26) | (1L << 27))
+#error OPTIONS_WRITTEN_TO_BIN_LOG must NOT change their values!
+#endif
+
enum Log_event_type
{
/*
@@ -1578,5 +1582,5 @@ public:
bool is_valid() const { return 1; }
};
#endif
-
+char *str_to_hex(char *to, const char *from, uint len);
#endif /* _log_event_h */
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index 46a4a08a30c..b3d107365c9 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -284,6 +284,11 @@ extern CHARSET_INFO *national_charset_info, *table_alias_charset;
#define OPTION_SETUP_TABLES_DONE (1L << 30) // intern
/* If not set then the thread will ignore all warnings with level notes. */
#define OPTION_SQL_NOTES (1L << 31) // THD, user
+/*
+ Force the used temporary table to be a MyISAM table (because we will use
+ fulltext functions when reading from it.
+*/
+#define TMP_TABLE_FORCE_MYISAM (LL(1) << 32)
/*
Maximum length of time zone name that we support
@@ -494,8 +499,7 @@ typedef Comp_creator* (*chooser_compare_func_creator)(bool invert);
void free_items(Item *item);
void cleanup_items(Item *item);
class THD;
-void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0,
- TABLE *stopper= 0);
+void close_thread_tables(THD *thd, bool locked=0, bool skip_derived=0);
bool check_one_table_access(THD *thd, ulong privilege,
TABLE_LIST *tables);
bool check_routine_access(THD *thd,ulong want_access,char *db,char *name,
@@ -588,7 +592,7 @@ int quick_rm_table(enum db_type base,const char *db,
const char *table_name);
void close_cached_table(THD *thd, TABLE *table);
bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list);
-bool mysql_change_db(THD *thd,const char *name);
+bool mysql_change_db(THD *thd,const char *name,bool no_access_check);
void mysql_parse(THD *thd,char *inBuf,uint length);
bool mysql_test_parse_for_slave(THD *thd,char *inBuf,uint length);
bool is_update_query(enum enum_sql_command command);
@@ -743,7 +747,7 @@ int mysql_update(THD *thd,TABLE_LIST *tables,List<Item> &fields,
enum enum_duplicates handle_duplicates, bool ignore);
bool mysql_multi_update(THD *thd, TABLE_LIST *table_list,
List<Item> *fields, List<Item> *values,
- COND *conds, ulong options,
+ COND *conds, ulonglong options,
enum enum_duplicates handle_duplicates, bool ignore,
SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex);
bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, TABLE *table,
@@ -759,7 +763,7 @@ int check_that_all_fields_are_given_values(THD *thd, TABLE *entry,
TABLE_LIST *table_list);
bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
bool mysql_delete(THD *thd, TABLE_LIST *table, COND *conds, SQL_LIST *order,
- ha_rows rows, ulong options);
+ ha_rows rows, ulonglong options);
bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create);
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update);
@@ -784,23 +788,25 @@ extern Field *view_ref_found;
enum find_item_error_report_type {REPORT_ALL_ERRORS, REPORT_EXCEPT_NOT_FOUND,
IGNORE_ERRORS, REPORT_EXCEPT_NON_UNIQUE,
IGNORE_EXCEPT_NON_UNIQUE};
-Field *find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
- Item **ref,
- find_item_error_report_type report_error,
- bool check_privileges,
- bool register_tree_change);
Field *
-find_field_in_table(THD *thd, TABLE_LIST *table_list,
- const char *name, const char *item_name,
- uint length, Item **ref,
- bool check_grants_table, bool check_grants_view,
- bool allow_rowid,
- uint *cached_field_index_ptr,
- bool register_tree_change);
+find_field_in_tables(THD *thd, Item_ident *item,
+ TABLE_LIST *first_table, TABLE_LIST *last_table,
+ Item **ref, find_item_error_report_type report_error,
+ bool check_privileges, bool register_tree_change);
+Field *
+find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
+ const char *name, const char *item_name,
+ const char *table_name, const char *db_name,
+ uint length, Item **ref,
+ bool check_grants_table, bool check_grants_view,
+ bool allow_rowid,
+ uint *cached_field_index_ptr,
+ bool register_tree_change, TABLE_LIST **actual_table);
Field *
-find_field_in_real_table(THD *thd, TABLE *table, const char *name,
- uint length, bool check_grants, bool allow_rowid,
- uint *cached_field_index_ptr);
+find_field_in_table(THD *thd, TABLE *table, const char *name,
+ uint length, bool check_grants, bool allow_rowid,
+ uint *cached_field_index_ptr);
+
Field *
find_field_in_table_sef(TABLE *table, const char *name);
@@ -910,8 +916,10 @@ create_field * new_create_field(THD *thd, char *field_name, enum_field_types typ
uint uint_geom_type);
void store_position_for_column(const char *name);
bool add_to_list(THD *thd, SQL_LIST &list,Item *group,bool asc);
+Name_resolution_context *make_join_on_context(THD *thd, TABLE_LIST *left_op,
+ TABLE_LIST *right_op);
void add_join_on(TABLE_LIST *b,Item *expr);
-void add_join_natural(TABLE_LIST *a,TABLE_LIST *b);
+void add_join_natural(TABLE_LIST *a,TABLE_LIST *b,List<String> *using_fields);
bool add_proc_to_list(THD *thd, Item *item);
TABLE *unlink_open_table(THD *thd,TABLE *list,TABLE *find);
@@ -928,8 +936,8 @@ bool insert_fields(THD *thd, Name_resolution_context *context,
const char *db_name, const char *table_name,
List_iterator<Item> *it, bool any_privileges);
bool setup_tables(THD *thd, Name_resolution_context *context,
- TABLE_LIST *tables, Item **conds,
- TABLE_LIST **leaves, bool select_insert);
+ List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
+ Item **conds, TABLE_LIST **leaves, bool select_insert);
int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
List<Item> *sum_func_list, uint wild_num);
bool setup_fields(THD *thd, Item** ref_pointer_array,
@@ -952,10 +960,10 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
int setup_ftfuncs(SELECT_LEX* select);
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
void wait_for_refresh(THD *thd);
-int open_tables(THD *thd, TABLE_LIST **tables, uint *counter);
+int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
int simple_open_n_lock_tables(THD *thd,TABLE_LIST *tables);
bool open_and_lock_tables(THD *thd,TABLE_LIST *tables);
-bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables);
+bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
int lock_tables(THD *thd, TABLE_LIST *tables, uint counter);
TABLE *open_temporary_table(THD *thd, const char *path, const char *db,
const char *table_name, bool link_in_list);
@@ -1000,7 +1008,7 @@ bool fill_record_n_invoke_before_triggers(THD *thd, Field **field,
bool ignore_errors,
Table_triggers_list *triggers,
enum trg_event_type event);
-OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild);
+OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild);
inline TABLE_LIST *find_table_in_global_list(TABLE_LIST *table,
const char *db_name,
@@ -1128,6 +1136,7 @@ extern char log_error_file[FN_REFLEN], *opt_tc_log_file;
extern double log_10[32];
extern ulonglong log_10_int[20];
extern ulonglong keybuff_size;
+extern ulonglong thd_startup_options;
extern ulong refresh_version,flush_version, thread_id;
extern ulong binlog_cache_use, binlog_cache_disk_use;
extern ulong aborted_threads,aborted_connects;
@@ -1137,7 +1146,7 @@ extern ulong delayed_insert_threads, delayed_insert_writes;
extern ulong delayed_rows_in_use,delayed_insert_errors;
extern ulong slave_open_temp_tables;
extern ulong query_cache_size, query_cache_min_res_unit;
-extern ulong thd_startup_options, slow_launch_threads, slow_launch_time;
+extern ulong slow_launch_threads, slow_launch_time;
extern ulong table_cache_size;
extern ulong max_connections,max_connect_errors, connect_timeout;
extern ulong slave_net_timeout, slave_trans_retries;
@@ -1477,6 +1486,12 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
table->status= STATUS_NO_RECORD;
table->keys_in_use_for_query= table->s->keys_in_use;
table->maybe_null= table_list->outer_join;
+ TABLE_LIST *embedding= table_list->embedding;
+ while (!table->maybe_null && embedding)
+ {
+ table->maybe_null= embedding->outer_join;
+ embedding= embedding->embedding;
+ }
table->tablenr= tablenr;
table->map= (table_map) 1 << tablenr;
table->force_index= table_list->force_index;
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 5b8814818bf..7813e654433 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -394,8 +394,8 @@ uint delay_key_write_options, protocol_version;
uint lower_case_table_names;
uint tc_heuristic_recover= 0;
uint volatile thread_count, thread_running;
-ulong back_log, connect_timeout, concurrency;
-ulong server_id, thd_startup_options;
+ulonglong thd_startup_options;
+ulong back_log, connect_timeout, concurrency, server_id;
ulong table_cache_size, thread_stack, what_to_log;
ulong query_buff_size, slow_launch_time, slave_open_temp_tables;
ulong open_files_limit, max_binlog_size, max_relay_log_size;
@@ -720,7 +720,6 @@ static void close_connections(void)
}
#endif
end_thr_alarm(0); // Abort old alarms.
- end_slave();
/*
First signal all threads that it's time to die
@@ -736,6 +735,10 @@ static void close_connections(void)
{
DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
tmp->thread_id));
+ /* We skip slave threads on this first loop through. */
+ if (tmp->slave_thread)
+ continue;
+
tmp->killed= THD::KILL_CONNECTION;
if (tmp->mysys_var)
{
@@ -752,6 +755,8 @@ static void close_connections(void)
}
(void) pthread_mutex_unlock(&LOCK_thread_count); // For unlink from list
+ end_slave();
+
if (thread_count)
sleep(2); // Give threads time to die
@@ -1056,6 +1061,7 @@ void clean_up(bool print_message)
(void) ha_panic(HA_PANIC_CLOSE); /* close all tables and logs */
if (tc_log)
tc_log->close();
+ xid_cache_free();
delete_elements(&key_caches, (void (*)(const char*, gptr)) free_key_cache);
multi_keycache_free();
end_thr_alarm(1); /* Free allocated memory */
@@ -1911,7 +1917,8 @@ static void check_data_home(const char *path)
static void sig_reload(int signo)
{
// Flush everything
- reload_acl_and_cache((THD*) 0,REFRESH_LOG, (TABLE_LIST*) 0, NULL);
+ bool not_used;
+ reload_acl_and_cache((THD*) 0,REFRESH_LOG, (TABLE_LIST*) 0, &not_used);
signal(signo, SIG_ACK);
}
@@ -2270,12 +2277,13 @@ static void *signal_hand(void *arg __attribute__((unused)))
case SIGHUP:
if (!abort_loop)
{
+ bool not_used;
mysql_print_status(); // Print some debug info
reload_acl_and_cache((THD*) 0,
(REFRESH_LOG | REFRESH_TABLES | REFRESH_FAST |
REFRESH_GRANT |
REFRESH_THREADS | REFRESH_HOSTS),
- (TABLE_LIST*) 0, NULL); // Flush logs
+ (TABLE_LIST*) 0, &not_used); // Flush logs
}
break;
#ifdef USE_ONE_SIGNAL_HAND
@@ -2923,6 +2931,11 @@ server.");
using_update_log=1;
}
+ if (xid_cache_init())
+ {
+ sql_print_error("Out of memory");
+ unireg_abort(1);
+ }
if (ha_init())
{
sql_print_error("Can't init databases");
@@ -5337,6 +5350,10 @@ log and this option does nothing anymore.",
"Helps in performance tuning in heavily concurrent environments.",
(gptr*) &srv_thread_concurrency, (gptr*) &srv_thread_concurrency,
0, GET_LONG, REQUIRED_ARG, 20, 1, 1000, 0, 1, 0},
+ {"innodb_commit_concurrency", OPT_INNODB_THREAD_CONCURRENCY,
+ "Helps in performance tuning in heavily concurrent environments.",
+ (gptr*) &srv_commit_concurrency, (gptr*) &srv_commit_concurrency,
+ 0, GET_LONG, REQUIRED_ARG, 0, 0, 1000, 0, 1, 0},
{"innodb_thread_sleep_delay", OPT_INNODB_THREAD_SLEEP_DELAY,
"Time of innodb thread sleeping before joining InnoDB queue (usec). Value 0"
" disable a sleep",
diff --git a/sql/set_var.cc b/sql/set_var.cc
index d5c3496eae6..e92ea232759 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -413,6 +413,8 @@ sys_var_long_ptr sys_innodb_thread_sleep_delay("innodb_thread_sleep_delay",
&srv_thread_sleep_delay);
sys_var_long_ptr sys_innodb_thread_concurrency("innodb_thread_concurrency",
&srv_thread_concurrency);
+sys_var_long_ptr sys_innodb_commit_concurrency("innodb_commit_concurrency",
+ &srv_commit_concurrency);
#endif
/* Condition pushdown to storage engine */
@@ -711,6 +713,7 @@ sys_var *sys_variables[]=
&sys_innodb_concurrency_tickets,
&sys_innodb_thread_sleep_delay,
&sys_innodb_thread_concurrency,
+ &sys_innodb_commit_concurrency,
#endif
&sys_trust_routine_creators,
&sys_engine_condition_pushdown,
@@ -832,6 +835,7 @@ struct show_var_st init_vars[]= {
{sys_innodb_table_locks.name, (char*) &sys_innodb_table_locks, SHOW_SYS},
{sys_innodb_support_xa.name, (char*) &sys_innodb_support_xa, SHOW_SYS},
{sys_innodb_thread_concurrency.name, (char*) &sys_innodb_thread_concurrency, SHOW_SYS},
+ {sys_innodb_commit_concurrency.name, (char*) &sys_innodb_commit_concurrency, SHOW_SYS},
{sys_innodb_thread_sleep_delay.name, (char*) &sys_innodb_thread_sleep_delay, SHOW_SYS},
#endif
{sys_interactive_timeout.name,(char*) &sys_interactive_timeout, SHOW_SYS},
@@ -2298,7 +2302,12 @@ bool sys_var_key_buffer_size::update(THD *thd, set_var *var)
if (!tmp) // Zero size means delete
{
if (key_cache == dflt_key_cache)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_WARN_CANT_DROP_DEFAULT_KEYCACHE,
+ ER(ER_WARN_CANT_DROP_DEFAULT_KEYCACHE));
goto end; // Ignore default key cache
+ }
if (key_cache->key_cache_inited) // If initied
{
@@ -3100,7 +3109,18 @@ int set_var_password::check(THD *thd)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (!user->host.str)
- user->host.str= (char*) thd->host_or_ip;
+ {
+ if (thd->priv_host != 0)
+ {
+ user->host.str= (char *) thd->priv_host;
+ user->host.length= strlen(thd->priv_host);
+ }
+ else
+ {
+ user->host.str= (char *)"%";
+ user->host.length= 1;
+ }
+ }
/* Returns 1 as the function sends error to client */
return check_change_password(thd, user->host.str, user->user.str,
password, strlen(password)) ? 1 : 0;
diff --git a/sql/share/Makefile.am b/sql/share/Makefile.am
index b7ab8fead22..2e23027f1f5 100644
--- a/sql/share/Makefile.am
+++ b/sql/share/Makefile.am
@@ -33,7 +33,7 @@ all-local: english/errmsg.sys
# created. Normally these are created by extra/Makefile
english/errmsg.sys: errmsg.txt
- rm $(top_builddir)/include/mysqld_error.h
+ rm -f $(top_builddir)/include/mysqld_error.h
(cd $(top_builddir)/extra && $(MAKE))
install-data-local:
diff --git a/sql/share/errmsg.txt b/sql/share/errmsg.txt
index a22e32fb075..ba9005638e1 100644
--- a/sql/share/errmsg.txt
+++ b/sql/share/errmsg.txt
@@ -5137,8 +5137,8 @@ ER_SP_CANT_ALTER
eng "Failed to ALTER %s %s"
ER_SP_SUBSELECT_NYI 0A000
eng "Subselect value not supported"
-ER_SP_NO_USE 42000
- eng "USE is not allowed in a stored procedure"
+ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG 0A000
+ eng "%s is not allowed in stored function or trigger"
ER_SP_VARCOND_AFTER_CURSHNDLR 42000
eng "Variable or condition declaration after cursor or handler declaration"
ER_SP_CURSOR_AFTER_HANDLER 42000
@@ -5194,7 +5194,7 @@ ER_VIEW_SELECT_VARIABLE
rus "View SELECT ÓÏÄÅÒÖÉÔ ÐÅÒÅÍÅÎÎÕÀ ÉÌÉ ÐÁÒÁÍÅÔÒ"
ukr "View SELECT ÍÁ¤ ÚÍÉÎÎÕ ÁÂÏ ÐÁÒÁÍÅÔÅÒ"
ER_VIEW_SELECT_TMPTABLE
- eng "View's SELECT contains a temporary table '%-.64s'"
+ eng "View's SELECT refers to a temporary table '%-.64s'"
rus "View SELECT ÓÏÄÅÒÖÉÔ ÓÓÙÌËÕ ÎÁ ×ÒÅÍÅÎÎÕÀ ÔÁÂÌÉÃÕ '%-.64s'"
ukr "View SELECT ×ÉËÏÒÉÓÔÏ×Õ¤ ÔÉÍÞÁÓÏ×Õ ÔÁÂÌÉÃÀ '%-.64s'"
ER_VIEW_WRONG_LIST
@@ -5342,8 +5342,8 @@ ER_SP_DUP_HANDLER 42000
eng "Duplicate handler declared in the same block"
ER_SP_NOT_VAR_ARG 42000
eng "OUT or INOUT argument %d for routine %s is not a variable"
-ER_SP_NO_RETSET_IN_FUNC 0A000
- eng "Not allowed to return a result set from a function"
+ER_SP_NO_RETSET 0A000
+ eng "Not allowed to return a result set from a %s"
ER_CANT_CREATE_GEOMETRY_OBJECT 22003
eng "Cannot get geometry object from data you send to the GEOMETRY field"
ER_FAILED_ROUTINE_BREAK_BINLOG
@@ -5380,14 +5380,25 @@ ER_FOREIGN_DATA_STRING_INVALID_CANT_CREATE
eng "Can't create federated table. The data source connection string '%-.64s' is not in the correct format"
ER_FOREIGN_DATA_STRING_INVALID
eng "The data source connection string '%-.64s' is not in the correct format"
-ER_CANT_CREATE_FEDERATED_TABLE
- eng "Can't create federated table. Foreign data src error : '%-.64s'"
-ER_TRG_IN_WRONG_SCHEMA
- eng "Trigger in wrong schema"
+ER_CANT_CREATE_FEDERATED_TABLE
+ eng "Can't create federated table. Foreign data src error : '%-.64s'"
+ER_TRG_IN_WRONG_SCHEMA
+ eng "Trigger in wrong schema"
ER_STACK_OVERRUN_NEED_MORE
eng "Thread stack overrun: %ld bytes used of a %ld byte stack, and %ld bytes needed. Use 'mysqld -O thread_stack=#' to specify a bigger stack."
ER_TOO_LONG_BODY 42000 S1009
eng "Routine body for '%-.100s' is too long"
+ER_WARN_CANT_DROP_DEFAULT_KEYCACHE
+ eng "Cannot drop default keycache"
+ ger "Der Default-Keycache kann nicht gelöscht werden"
+ER_TOO_BIG_DISPLAYWIDTH 42000 S1009
+ eng "Display width out of range for column '%-.64s' (max = %d)"
+ER_XAER_DUPID XAE08
+ eng "XAER_DUPID: The XID already exists"
+ER_DATETIME_FUNCTION_OVERFLOW 22008
+ eng "Datetime function: %-.32s field overflow"
+ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG
+ eng "Can't update table '%-.64s' in stored function/trigger because it is already used by statement which invoked this stored function/trigger."
ER_PARTITION_REQUIRES_VALUES_ERROR
eng "%s PARTITIONING requires definition of VALUES %s for each partition"
swe "%s PARTITIONering kräver definition av VALUES %s för varje partition"
diff --git a/sql/slave.cc b/sql/slave.cc
index 2e751f9ddab..757d8bc212d 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -2018,8 +2018,8 @@ bool show_master_info(THD* thd, MASTER_INFO* mi)
&my_charset_bin);
protocol->store((ulonglong) mi->rli.group_relay_log_pos);
protocol->store(mi->rli.group_master_log_name, &my_charset_bin);
- protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT
- ? "Yes":"No", &my_charset_bin);
+ protocol->store(mi->slave_running == MYSQL_SLAVE_RUN_CONNECT ?
+ "Yes" : "No", &my_charset_bin);
protocol->store(mi->rli.slave_running ? "Yes":"No", &my_charset_bin);
protocol->store(rpl_filter->get_do_db());
protocol->store(rpl_filter->get_ignore_db());
diff --git a/sql/sp.cc b/sql/sp.cc
index e45a360e85b..4485019be4a 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -68,13 +68,16 @@ bool mysql_proc_table_exists= 1;
SYNOPSIS
close_proc_table()
- thd Thread context
+ thd Thread context
+ backup Pointer to Open_tables_state instance which holds
+ information about tables which were open before we
+ decided to access mysql.proc.
*/
-static void close_proc_table(THD *thd)
+void close_proc_table(THD *thd, Open_tables_state *backup)
{
close_thread_tables(thd);
- thd->pop_open_tables_state();
+ thd->restore_backup_open_tables_state(backup);
}
@@ -83,7 +86,10 @@ static void close_proc_table(THD *thd)
SYNOPSIS
open_proc_table_for_read()
- thd Thread context
+ thd Thread context
+ backup Pointer to Open_tables_state instance where information about
+ currently open tables will be saved, and from which will be
+ restored when we will end work with mysql.proc.
NOTES
Thanks to restrictions which we put on opening and locking of
@@ -97,11 +103,10 @@ static void close_proc_table(THD *thd)
# Pointer to TABLE object of mysql.proc
*/
-static TABLE *open_proc_table_for_read(THD *thd)
+TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup)
{
TABLE_LIST tables;
TABLE *table;
- bool old_open_tables= thd->open_tables != 0;
bool refresh;
DBUG_ENTER("open_proc_table");
@@ -112,8 +117,7 @@ static TABLE *open_proc_table_for_read(THD *thd)
if (!mysql_proc_table_exists)
DBUG_RETURN(0);
- if (thd->push_open_tables_state())
- DBUG_RETURN(0);
+ thd->reset_n_backup_open_tables_state(backup);
bzero((char*) &tables, sizeof(tables));
tables.db= (char*) "mysql";
@@ -121,7 +125,7 @@ static TABLE *open_proc_table_for_read(THD *thd)
if (!(table= open_table(thd, &tables, thd->mem_root, &refresh,
MYSQL_LOCK_IGNORE_FLUSH)))
{
- thd->pop_open_tables_state();
+ thd->restore_backup_open_tables_state(backup);
mysql_proc_table_exists= 0;
DBUG_RETURN(0);
}
@@ -130,15 +134,13 @@ static TABLE *open_proc_table_for_read(THD *thd)
table->reginfo.lock_type= TL_READ;
/*
- If we have other tables opened, we have to ensure we are not blocked
- by a flush tables or global read lock, as this could lead to a deadlock
+ We have to ensure we are not blocked by a flush tables, as this
+ could lead to a deadlock if we have other tables opened.
*/
if (!(thd->lock= mysql_lock_tables(thd, &table, 1,
- old_open_tables ?
- (MYSQL_LOCK_IGNORE_GLOBAL_READ_LOCK |
- MYSQL_LOCK_IGNORE_FLUSH) : 0)))
+ MYSQL_LOCK_IGNORE_FLUSH)))
{
- close_proc_table(thd);
+ close_proc_table(thd, backup);
DBUG_RETURN(0);
}
DBUG_RETURN(table);
@@ -271,12 +273,13 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
char buff[65];
String str(buff, sizeof(buff), &my_charset_bin);
ulong sql_mode;
+ Open_tables_state open_tables_state_backup;
DBUG_ENTER("db_find_routine");
DBUG_PRINT("enter", ("type: %d name: %*s",
type, name->m_name.length, name->m_name.str));
*sphp= 0; // In case of errors
- if (!(table= open_proc_table_for_read(thd)))
+ if (!(table= open_proc_table_for_read(thd, &open_tables_state_backup)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
if ((ret= db_find_routine_aux(thd, type, name, table)) != SP_OK)
@@ -371,7 +374,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
chistics.comment.str= ptr;
chistics.comment.length= length;
- close_proc_table(thd);
+ close_proc_table(thd, &open_tables_state_backup);
table= 0;
{
@@ -424,7 +427,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
LEX *newlex= thd->lex;
sp_head *sp= newlex->sphead;
- if (dbchanged && (ret= sp_change_db(thd, olddb, 1)))
+ if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
goto done;
if (sp)
{
@@ -435,7 +438,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
}
else
{
- if (dbchanged && (ret= sp_change_db(thd, olddb, 1)))
+ if (dbchanged && (ret= mysql_change_db(thd, olddb, 1)))
goto done;
*sphp= thd->lex->sphead;
(*sphp)->set_info((char *)definer, (uint)strlen(definer),
@@ -449,7 +452,7 @@ db_find_routine(THD *thd, int type, sp_name *name, sp_head **sphp)
done:
if (table)
- close_proc_table(thd);
+ close_proc_table(thd, &open_tables_state_backup);
DBUG_RETURN(ret);
}
@@ -591,7 +594,7 @@ db_create_routine(THD *thd, int type, sp_head *sp)
done:
close_thread_tables(thd);
if (dbchanged)
- (void)sp_change_db(thd, olddb, 1);
+ (void)mysql_change_db(thd, olddb, 1);
DBUG_RETURN(ret);
}
@@ -786,6 +789,7 @@ db_show_routine_status(THD *thd, int type, const char *wild)
*/
thd->lex->select_lex.context.resolve_in_table_list_only(&tables);
setup_tables(thd, &thd->lex->select_lex.context,
+ &thd->lex->select_lex.top_join_list,
&tables, 0, &leaves, FALSE);
for (used_field= &used_fields[0];
used_field->field_name;
@@ -795,7 +799,7 @@ db_show_routine_status(THD *thd, int type, const char *wild)
"mysql", "proc",
used_field->field_name);
if (!field ||
- !(used_field->field= find_field_in_tables(thd, field, &tables,
+ !(used_field->field= find_field_in_tables(thd, field, &tables, NULL,
0, REPORT_ALL_ERRORS, 1,
TRUE)))
{
@@ -986,13 +990,11 @@ int
sp_drop_procedure(THD *thd, sp_name *name)
{
int ret;
- bool found;
DBUG_ENTER("sp_drop_procedure");
DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
- found= sp_cache_remove(&thd->sp_proc_cache, name);
ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name);
- if (!found && !ret)
+ if (!ret)
sp_cache_invalidate();
DBUG_RETURN(ret);
}
@@ -1002,13 +1004,11 @@ int
sp_update_procedure(THD *thd, sp_name *name, st_sp_chistics *chistics)
{
int ret;
- bool found;
DBUG_ENTER("sp_update_procedure");
DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
- found= sp_cache_remove(&thd->sp_proc_cache, name);
ret= db_update_routine(thd, TYPE_ENUM_PROCEDURE, name, chistics);
- if (!found && !ret)
+ if (!ret)
sp_cache_invalidate();
DBUG_RETURN(ret);
}
@@ -1099,13 +1099,11 @@ int
sp_drop_function(THD *thd, sp_name *name)
{
int ret;
- bool found;
DBUG_ENTER("sp_drop_function");
DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
- found= sp_cache_remove(&thd->sp_func_cache, name);
ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name);
- if (!found && !ret)
+ if (!ret)
sp_cache_invalidate();
DBUG_RETURN(ret);
}
@@ -1115,13 +1113,11 @@ int
sp_update_function(THD *thd, sp_name *name, st_sp_chistics *chistics)
{
int ret;
- bool found;
DBUG_ENTER("sp_update_procedure");
DBUG_PRINT("enter", ("name: %*s", name->m_name.length, name->m_name.str));
- found= sp_cache_remove(&thd->sp_func_cache, name);
ret= db_update_routine(thd, TYPE_ENUM_FUNCTION, name, chistics);
- if (!found && !ret)
+ if (!ret)
sp_cache_invalidate();
DBUG_RETURN(ret);
}
@@ -1617,112 +1613,10 @@ sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddblen,
}
else
{
- int ret= sp_change_db(thd, newdb, no_access_check);
+ int ret= mysql_change_db(thd, newdb, no_access_check);
if (! ret)
*dbchangedp= TRUE;
DBUG_RETURN(ret);
}
}
-
-/*
- Change database.
-
- SYNOPSIS
- sp_change_db()
- thd Thread handler
- name Database name
- empty_is_ok True= it's ok with "" as name
- no_access_check True= don't do access check
-
- DESCRIPTION
- This is the same as mysql_change_db(), but with some extra
- arguments for Stored Procedure usage; doing implicit "use"
- when executing an SP in a different database.
- We also use different error routines, since this might be
- invoked from a function when executing a query or statement.
- Note: We would have prefered to reuse mysql_change_db(), but
- the error handling in particular made that too awkward, so
- we (reluctantly) have a "copy" here.
-
- RETURN VALUES
- 0 ok
- 1 error
-*/
-
-int
-sp_change_db(THD *thd, char *name, bool no_access_check)
-{
- int length, db_length;
- char *dbname=my_strdup((char*) name,MYF(MY_WME));
- char path[FN_REFLEN];
- HA_CREATE_INFO create;
- DBUG_ENTER("sp_change_db");
- DBUG_PRINT("enter", ("db: %s, no_access_check: %d", name, no_access_check));
-
- db_length= (!dbname ? 0 : strip_sp(dbname));
- if (dbname && db_length)
- {
- if ((db_length > NAME_LEN) || check_db_name(dbname))
- {
- my_error(ER_WRONG_DB_NAME, MYF(0), dbname);
- x_free(dbname);
- DBUG_RETURN(1);
- }
- }
-
- if (dbname && db_length)
- {
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (! no_access_check)
- {
- ulong db_access;
-
- if (test_all_bits(thd->master_access,DB_ACLS))
- db_access=DB_ACLS;
- else
- db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
- thd->master_access);
- if (!(db_access & DB_ACLS) &&
- (!grant_option || check_grant_db(thd,dbname)))
- {
- my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
- thd->priv_user,
- thd->priv_host,
- dbname);
- mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
- thd->priv_user,
- thd->priv_host,
- dbname);
- my_free(dbname,MYF(0));
- DBUG_RETURN(1);
- }
- }
-#endif
- (void) sprintf(path,"%s/%s",mysql_data_home,dbname);
- length=unpack_dirname(path,path); // Convert if not unix
- if (length && path[length-1] == FN_LIBCHAR)
- path[length-1]=0; // remove ending '\'
- if (access(path,F_OK))
- {
- my_error(ER_BAD_DB_ERROR, MYF(0), dbname);
- my_free(dbname,MYF(0));
- DBUG_RETURN(1);
- }
- }
-
- x_free(thd->db);
- thd->db=dbname; // THD::~THD will free this
- thd->db_length=db_length;
-
- if (dbname && db_length)
- {
- strmov(path+unpack_dirname(path,path), MY_DB_OPT_FILE);
- load_db_opt(thd, path, &create);
- thd->db_charset= create.default_table_charset ?
- create.default_table_charset :
- thd->variables.collation_server;
- thd->variables.collation_database= thd->db_charset;
- }
- DBUG_RETURN(0);
-}
diff --git a/sql/sp.h b/sql/sp.h
index 58244c34761..c278da863e0 100644
--- a/sql/sp.h
+++ b/sql/sp.h
@@ -94,6 +94,13 @@ void sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
extern "C" byte* sp_sroutine_key(const byte *ptr, uint *plen, my_bool first);
+/*
+ Routines which allow open/lock and close mysql.proc table even when
+ we already have some tables open and locked.
+*/
+TABLE *open_proc_table_for_read(THD *thd, Open_tables_state *backup);
+void close_proc_table(THD *thd, Open_tables_state *backup);
+
//
// Utilities...
//
@@ -105,8 +112,4 @@ int
sp_use_new_db(THD *thd, char *newdb, char *olddb, uint olddbmax,
bool no_access_check, bool *dbchangedp);
-// Like mysql_change_db() but handles empty db name and the send_ok() problem.
-int
-sp_change_db(THD *thd, char *db, bool no_access_check);
-
#endif /* _SP_H_ */
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
index c8f0ed6ba2d..495f969eeac 100644
--- a/sql/sp_cache.cc
+++ b/sql/sp_cache.cc
@@ -22,16 +22,80 @@
#include "sp_head.h"
static pthread_mutex_t Cversion_lock;
-static ulong Cversion = 0;
+static ulong volatile Cversion= 0;
-void
-sp_cache_init()
+
+/*
+ Cache of stored routines.
+*/
+
+class sp_cache
+{
+public:
+ ulong version;
+
+ sp_cache();
+ ~sp_cache();
+
+ inline void insert(sp_head *sp)
+ {
+ /* TODO: why don't we check return value? */
+ my_hash_insert(&m_hashtable, (const byte *)sp);
+ }
+
+ inline sp_head *lookup(char *name, uint namelen)
+ {
+ return (sp_head *)hash_search(&m_hashtable, (const byte *)name, namelen);
+ }
+
+#ifdef NOT_USED
+ inline bool remove(char *name, uint namelen)
+ {
+ sp_head *sp= lookup(name, namelen);
+ if (sp)
+ {
+ hash_delete(&m_hashtable, (byte *)sp);
+ return TRUE;
+ }
+ return FALSE;
+ }
+#endif
+
+ inline void remove_all()
+ {
+ cleanup();
+ init();
+ }
+
+private:
+ void init();
+ void cleanup();
+
+ /* All routines in this cache */
+ HASH m_hashtable;
+}; // class sp_cache
+
+
+/* Initialize the SP caching once at startup */
+
+void sp_cache_init()
{
pthread_mutex_init(&Cversion_lock, MY_MUTEX_INIT_FAST);
}
-void
-sp_cache_clear(sp_cache **cp)
+
+/*
+ Clear the cache *cp and set *cp to NULL.
+
+ SYNOPSIS
+ sp_cache_clear()
+ cp Pointer to cache to clear
+
+ NOTE
+ This function doesn't invalidate other caches.
+*/
+
+void sp_cache_clear(sp_cache **cp)
{
sp_cache *c= *cp;
@@ -42,114 +106,145 @@ sp_cache_clear(sp_cache **cp)
}
}
-void
-sp_cache_insert(sp_cache **cp, sp_head *sp)
-{
- sp_cache *c= *cp;
- if (! c)
- c= new sp_cache();
- if (c)
- {
- ulong v;
+/*
+ Insert a routine into the cache.
- pthread_mutex_lock(&Cversion_lock); // LOCK
- v= Cversion;
- pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+ SYNOPSIS
+ sp_cache_insert()
+ cp The cache to put routine into
+ sp Routine to insert.
+
+ TODO: Perhaps it will be more straightforward if in case we returned an
+ error from this function when we couldn't allocate sp_cache. (right
+ now failure to put routine into cache will cause a 'SP not found'
+ error to be reported at some later time)
+*/
- if (c->version < v)
- {
- if (*cp)
- c->remove_all();
- c->version= v;
- }
- c->insert(sp);
- if (*cp == NULL)
- *cp= c;
+void sp_cache_insert(sp_cache **cp, sp_head *sp)
+{
+ sp_cache *c;
+ ulong v;
+
+ if (!(c= *cp))
+ {
+ if (!(c= new sp_cache()))
+ return; // End of memory error
+ c->version= Cversion; // No need to lock when reading long variable
}
+ DBUG_PRINT("info",("sp_cache: inserting: %*s", sp->m_qname.length,
+ sp->m_qname.str));
+ c->insert(sp);
+ *cp= c; // Update *cp if it was NULL
}
-sp_head *
-sp_cache_lookup(sp_cache **cp, sp_name *name)
+
+/*
+ Look up a routine in the cache.
+ SYNOPSIS
+ sp_cache_lookup()
+ cp Cache to look into
+ name Name of rutine to find
+
+ NOTE
+ An obsolete (but not more obsolete then since last
+ sp_cache_flush_obsolete call) routine may be returned.
+
+ RETURN
+ The routine or
+ NULL if the routine not found.
+*/
+
+sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name)
{
- ulong v;
sp_cache *c= *cp;
-
if (! c)
return NULL;
+ return c->lookup(name->m_qname.str, name->m_qname.length);
+}
- pthread_mutex_lock(&Cversion_lock); // LOCK
- v= Cversion;
- pthread_mutex_unlock(&Cversion_lock); // UNLOCK
- if (c->version < v)
- {
- c->remove_all();
- c->version= v;
- return NULL;
- }
- return c->lookup(name->m_qname.str, name->m_qname.length);
+/*
+ Invalidate all routines in all caches.
+
+ SYNOPSIS
+ sp_cache_invalidate()
+
+ NOTE
+ This is called when a VIEW definition is modifed. We can't destroy sp_head
+ objects here as one may modify VIEW definitions from prelocking-free SPs.
+*/
+
+void sp_cache_invalidate()
+{
+ DBUG_PRINT("info",("sp_cache: invalidating"));
+ thread_safe_increment(Cversion, &Cversion_lock);
}
-bool
-sp_cache_remove(sp_cache **cp, sp_name *name)
+
+/*
+ Remove out-of-date SPs from the cache.
+
+ SYNOPSIS
+ sp_cache_flush_obsolete()
+ cp Cache to flush
+
+ NOTE
+ This invalidates pointers to sp_head objects this thread uses.
+ In practice that means 'dont call this function when inside SP'.
+*/
+
+void sp_cache_flush_obsolete(sp_cache **cp)
{
sp_cache *c= *cp;
- bool found= FALSE;
-
if (c)
{
ulong v;
-
- pthread_mutex_lock(&Cversion_lock); // LOCK
- v= Cversion++;
- pthread_mutex_unlock(&Cversion_lock); // UNLOCK
-
+ v= Cversion; // No need to lock when reading long variable
if (c->version < v)
+ {
+ DBUG_PRINT("info",("sp_cache: deleting all functions"));
+ /* We need to delete all elements. */
c->remove_all();
- else
- found= c->remove(name->m_qname.str, name->m_qname.length);
- c->version= v+1;
+ c->version= v;
+ }
}
- return found;
}
-void
-sp_cache_invalidate()
-{
- pthread_mutex_lock(&Cversion_lock); // LOCK
- Cversion++;
- pthread_mutex_unlock(&Cversion_lock); // UNLOCK
-}
-static byte *
-hash_get_key_for_sp_head(const byte *ptr, uint *plen,
+/*************************************************************************
+ Internal functions
+ *************************************************************************/
+
+static byte *hash_get_key_for_sp_head(const byte *ptr, uint *plen,
my_bool first)
{
sp_head *sp= (sp_head *)ptr;
-
*plen= sp->m_qname.length;
return (byte*) sp->m_qname.str;
}
+
static void
hash_free_sp_head(void *p)
{
sp_head *sp= (sp_head *)p;
-
delete sp;
}
+
sp_cache::sp_cache()
{
init();
}
+
sp_cache::~sp_cache()
{
hash_free(&m_hashtable);
}
+
void
sp_cache::init()
{
@@ -158,6 +253,7 @@ sp_cache::init()
version= 0;
}
+
void
sp_cache::cleanup()
{
diff --git a/sql/sp_cache.h b/sql/sp_cache.h
index 14b2db97f5f..1021d17b9e2 100644
--- a/sql/sp_cache.h
+++ b/sql/sp_cache.h
@@ -25,94 +25,39 @@
/*
Stored procedures/functions cache. This is used as follows:
* Each thread has its own cache.
- * Each sp_head object is put into its thread cache before it is used, and
+ * Each sp_head object is put into its thread cache before it is used, and
then remains in the cache until deleted.
*/
class sp_head;
class sp_cache;
-/* Initialize the SP caching once at startup */
-void sp_cache_init();
+/*
+ Cache usage scenarios:
+ 1. Application-wide init:
+ sp_cache_init();
+
+ 2. SP execution in thread:
+ 2.1 While holding sp_head* pointers:
+
+ // look up a routine in the cache (no checks if it is up to date or not)
+ sp_cache_lookup();
+
+ sp_cache_insert();
+ sp_cache_invalidate();
+
+ 2.2 When not holding any sp_head* pointers:
+ sp_cache_flush_obsolete();
+
+ 3. Before thread exit:
+ sp_cache_clear();
+*/
-/* Clear the cache *cp and set *cp to NULL */
+void sp_cache_init();
void sp_cache_clear(sp_cache **cp);
-
-/* Insert an SP into cache. If 'cp' points to NULL, it's set to a new cache */
void sp_cache_insert(sp_cache **cp, sp_head *sp);
-
-/* Lookup an SP in cache */
sp_head *sp_cache_lookup(sp_cache **cp, sp_name *name);
-
-/*
- Remove an SP from cache, and also bump the Cversion number so all other
- caches are invalidated.
- Returns true if something was removed.
-*/
-bool sp_cache_remove(sp_cache **cp, sp_name *name);
-
-/* Invalidate all existing SP caches by bumping Cversion number. */
void sp_cache_invalidate();
-
-
-/*
- *
- * The cache class. Don't use this directly, use the C API above
- *
- */
-
-class sp_cache
-{
-public:
-
- ulong version;
-
- sp_cache();
-
- ~sp_cache();
-
- void
- init();
-
- void
- cleanup();
-
- inline void
- insert(sp_head *sp)
- {
- my_hash_insert(&m_hashtable, (const byte *)sp);
- }
-
- inline sp_head *
- lookup(char *name, uint namelen)
- {
- return (sp_head *)hash_search(&m_hashtable, (const byte *)name, namelen);
- }
-
- inline bool
- remove(char *name, uint namelen)
- {
- sp_head *sp= lookup(name, namelen);
-
- if (sp)
- {
- hash_delete(&m_hashtable, (byte *)sp);
- return TRUE;
- }
- return FALSE;
- }
-
- inline void
- remove_all()
- {
- cleanup();
- init();
- }
-
-private:
-
- HASH m_hashtable;
-
-}; // class sp_cache
+void sp_cache_flush_obsolete(sp_cache **cp);
#endif /* _SP_CACHE_H_ */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 3a386356335..dfc91f5a3f4 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -126,16 +126,48 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
}
-/* Evaluate a (presumed) func item. Always returns an item, the parameter
-** if nothing else.
+/* Macro to switch arena in sp_eval_func_item */
+#define CREATE_ON_CALLERS_ARENA(new_command, condition, backup_arena) \
+ do \
+ { \
+ if (condition) \
+ thd->set_n_backup_item_arena(thd->spcont->callers_arena, \
+ backup_arena); \
+ new_command; \
+ if (condition) \
+ thd->restore_backup_item_arena(thd->spcont->callers_arena, \
+ &backup_current_arena); \
+ } while(0)
+
+/*
+ Evaluate an item and store it in the returned item
+
+ SYNOPSIS
+ sp_eval_func_item()
+ name - current thread object
+ it_addr - pointer to the item to evaluate
+ type - type of the item we evaluating
+ reuse - used if we would like to reuse existing item
+ instead of allocation of the new one
+ use_callers_arena - TRUE if we want to use caller's arena
+ rather then current one.
+ DESCRIPTION
+ We use this function to evaluate result for stored functions
+ and stored procedure parameters. It is also used to evaluate and
+ (re) allocate variables.
+
+ RETURN VALUES
+ Evaluated item is returned
*/
+
Item *
sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type,
- Item *reuse)
+ Item *reuse, bool use_callers_arena)
{
DBUG_ENTER("sp_eval_func_item");
Item *it= sp_prepare_func_item(thd, it_addr);
uint rsize;
+ Query_arena backup_current_arena;
DBUG_PRINT("info", ("type: %d", type));
if (!it)
@@ -143,93 +175,96 @@ sp_eval_func_item(THD *thd, Item **it_addr, enum enum_field_types type,
DBUG_RETURN(NULL);
}
- /* QQ How do we do this? Is there some better way? */
- if (type == MYSQL_TYPE_NULL)
- it= new(reuse, &rsize) Item_null();
- else
+ switch (sp_map_result_type(type)) {
+ case INT_RESULT:
{
- switch (sp_map_result_type(type)) {
- case INT_RESULT:
- {
- longlong i= it->val_int();
+ longlong i= it->val_int();
- if (it->null_value)
- {
- DBUG_PRINT("info", ("INT_RESULT: null"));
- it= new(reuse, &rsize) Item_null();
- }
- else
- {
- DBUG_PRINT("info", ("INT_RESULT: %d", i));
- it= new(reuse, &rsize) Item_int(i);
- }
- break;
- }
- case REAL_RESULT:
- {
- double d= it->val_real();
+ if (it->null_value)
+ {
+ DBUG_PRINT("info", ("INT_RESULT: null"));
+ goto return_null_item;
+ }
+ DBUG_PRINT("info", ("INT_RESULT: %d", i));
+ CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_int(i),
+ use_callers_arena, &backup_current_arena);
+ break;
+ }
+ case REAL_RESULT:
+ {
+ double d= it->val_real();
+ uint8 decimals;
+ uint32 max_length;
- if (it->null_value)
- {
- DBUG_PRINT("info", ("REAL_RESULT: null"));
- it= new(reuse, &rsize) Item_null();
- }
- else
- {
- /* There's some difference between Item::new_item() and the
- * constructor; the former crashes, the latter works... weird. */
- uint8 decimals= it->decimals;
- uint32 max_length= it->max_length;
- DBUG_PRINT("info", ("REAL_RESULT: %g", d));
- it= new(reuse, &rsize) Item_float(d);
- it->decimals= decimals;
- it->max_length= max_length;
- }
- break;
- }
- case DECIMAL_RESULT:
- {
- my_decimal value, *val= it->val_decimal(&value);
- if (it->null_value)
- it= new(reuse, &rsize) Item_null();
- else
- it= new(reuse, &rsize) Item_decimal(val);
+ if (it->null_value)
+ {
+ DBUG_PRINT("info", ("REAL_RESULT: null"));
+ goto return_null_item;
+ }
+
+ /*
+ There's some difference between Item::new_item() and the
+ constructor; the former crashes, the latter works... weird.
+ */
+ decimals= it->decimals;
+ max_length= it->max_length;
+ DBUG_PRINT("info", ("REAL_RESULT: %g", d));
+ CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_float(d),
+ use_callers_arena, &backup_current_arena);
+ it->decimals= decimals;
+ it->max_length= max_length;
+ break;
+ }
+ case DECIMAL_RESULT:
+ {
+ my_decimal value, *val= it->val_decimal(&value);
+ if (it->null_value)
+ goto return_null_item;
+ CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_decimal(val),
+ use_callers_arena, &backup_current_arena);
#ifndef DBUG_OFF
- char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
- DBUG_PRINT("info", ("DECIMAL_RESULT: %s", dbug_decimal_as_string(dbug_buff, val)));
+ {
+ char dbug_buff[DECIMAL_MAX_STR_LENGTH+1];
+ DBUG_PRINT("info", ("DECIMAL_RESULT: %s",
+ dbug_decimal_as_string(dbug_buff, val)));
+ }
#endif
- break;
- }
- case STRING_RESULT:
- {
- char buffer[MAX_FIELD_WIDTH];
- String tmp(buffer, sizeof(buffer), it->collation.collation);
- String *s= it->val_str(&tmp);
+ break;
+ }
+ case STRING_RESULT:
+ {
+ char buffer[MAX_FIELD_WIDTH];
+ String tmp(buffer, sizeof(buffer), it->collation.collation);
+ String *s= it->val_str(&tmp);
- if (it->null_value)
- {
- DBUG_PRINT("info", ("default result: null"));
- it= new(reuse, &rsize) Item_null();
- }
- else
- {
- DBUG_PRINT("info",("default result: %*s",
- s->length(), s->c_ptr_quick()));
- it= new(reuse, &rsize) Item_string(thd->strmake(s->ptr(),
- s->length()),
- s->length(),
- it->collation.collation);
- }
- break;
- }
- case ROW_RESULT:
- default:
- DBUG_ASSERT(0);
+ if (type == MYSQL_TYPE_NULL || it->null_value)
+ {
+ DBUG_PRINT("info", ("STRING_RESULT: null"));
+ goto return_null_item;
}
+ DBUG_PRINT("info",("STRING_RESULT: %*s",
+ s->length(), s->c_ptr_quick()));
+ CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize)
+ Item_string(thd->strmake(s->ptr(),
+ s->length()), s->length(),
+ it->collation.collation),
+ use_callers_arena, &backup_current_arena);
+ break;
+ }
+ case ROW_RESULT:
+ default:
+ DBUG_ASSERT(0);
}
it->rsize= rsize;
DBUG_RETURN(it);
+
+return_null_item:
+ CREATE_ON_CALLERS_ARENA(it= new(reuse, &rsize) Item_null(),
+ use_callers_arena, &backup_current_arena);
+ it->rsize= rsize;
+
+ DBUG_RETURN(it);
}
@@ -534,34 +569,22 @@ sp_head::destroy()
/*
- * This is only used for result fields from functions (both during
- * fix_length_and_dec() and evaluation).
- *
- * Since the current mem_root during a will be freed and the result
- * field will be used by the caller, we have to put it in the caller's
- * or main mem_root.
- */
+ This is only used for result fields from functions (both during
+ fix_length_and_dec() and evaluation).
+*/
+
Field *
sp_head::make_field(uint max_length, const char *name, TABLE *dummy)
{
Field *field;
- MEM_ROOT *tmp_mem_root;
- THD *thd;
DBUG_ENTER("sp_head::make_field");
- thd= current_thd;
- tmp_mem_root= thd->mem_root;
- if (thd->spcont && thd->spcont->callers_mem_root)
- thd->mem_root= thd->spcont->callers_mem_root;
- else
- thd->mem_root= &thd->main_mem_root;
field= ::make_field((char *)0,
!m_returns_len ? max_length : m_returns_len,
(uchar *)"", 0, m_returns_pack, m_returns, m_returns_cs,
(enum Field::geometry_type)0, Field::NONE,
m_returns_typelib,
name ? name : (const char *)m_name.str, dummy);
- thd->mem_root= tmp_mem_root;
DBUG_RETURN(field);
}
@@ -576,12 +599,20 @@ sp_head::execute(THD *thd)
uint ip= 0;
ulong save_sql_mode;
Query_arena *old_arena;
+ /* per-instruction arena */
+ MEM_ROOT execute_mem_root;
+ Query_arena execute_arena(&execute_mem_root, INITIALIZED_FOR_SP),
+ execute_backup_arena;
query_id_t old_query_id;
TABLE *old_derived_tables;
LEX *old_lex;
Item_change_list old_change_list;
String old_packet;
+ /* init per-instruction memroot */
+ init_alloc_root(&execute_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
+
+
/* Use some extra margin for possible SP recursion and functions */
if (check_stack_overrun(thd, 4*STACK_MIN_SIZE, olddb))
{
@@ -650,6 +681,18 @@ sp_head::execute(THD *thd)
*/
old_packet.swap(thd->packet);
+ /*
+ Switch to per-instruction arena here. We can do it since we cleanup
+ arena after every instruction.
+ */
+ thd->set_n_backup_item_arena(&execute_arena, &execute_backup_arena);
+
+ /*
+ Save callers arena in order to store instruction results and out
+ parameters in it later during sp_eval_func_item()
+ */
+ thd->spcont->callers_arena= &execute_backup_arena;
+
do
{
sp_instr *i;
@@ -659,7 +702,9 @@ sp_head::execute(THD *thd)
if (i == NULL)
break;
DBUG_PRINT("execute", ("Instruction %u", ip));
- thd->set_time(); // Make current_time() et al work
+ /* Don't change NOW() in FUNCTION or TRIGGER */
+ if (!thd->in_sub_stmt)
+ thd->set_time(); // Make current_time() et al work
/*
We have to set thd->current_arena before executing the instruction
to store in the instruction free_list all new items, created
@@ -668,6 +713,7 @@ sp_head::execute(THD *thd)
*/
thd->current_arena= i;
ret= i->execute(thd, &ip);
+
/*
If this SP instruction have sent eof, it has caused no_send_error to be
set. Clear it back to allow the next instruction to send error. (multi-
@@ -678,23 +724,30 @@ sp_head::execute(THD *thd)
cleanup_items(i->free_list);
i->state= Query_arena::EXECUTED;
- // Check if an exception has occurred and a handler has been found
- // Note: We havo to check even if ret==0, since warnings (and some
- // errors don't return a non-zero value.
- // We also have to check even if thd->killed != 0, since some
- // errors return with this even when a handler has been found
- // (e.g. "bad data").
+ /* we should cleanup free_list and memroot, used by instruction */
+ thd->free_items();
+ free_root(&execute_mem_root, MYF(0));
+
+ /*
+ Check if an exception has occurred and a handler has been found
+ Note: We havo to check even if ret==0, since warnings (and some
+ errors don't return a non-zero value.
+ We also have to check even if thd->killed != 0, since some
+ errors return with this even when a handler has been found
+ (e.g. "bad data").
+ */
if (ctx)
{
uint hf;
- switch (ctx->found_handler(&hip, &hf))
- {
+ switch (ctx->found_handler(&hip, &hf)) {
case SP_HANDLER_NONE:
break;
case SP_HANDLER_CONTINUE:
- ctx->save_variables(hf);
- ctx->push_hstack(ip);
+ thd->restore_backup_item_arena(&execute_arena, &execute_backup_arena);
+ ctx->save_variables(hf);
+ thd->set_n_backup_item_arena(&execute_arena, &execute_backup_arena);
+ ctx->push_hstack(ip);
// Fall through
default:
ip= hip;
@@ -708,6 +761,9 @@ sp_head::execute(THD *thd)
}
} while (ret == 0 && !thd->killed);
+ thd->restore_backup_item_arena(&execute_arena, &execute_backup_arena);
+
+
/* Restore all saved */
old_packet.swap(thd->packet);
DBUG_ASSERT(thd->change_list.is_empty());
@@ -734,7 +790,7 @@ sp_head::execute(THD *thd)
if (dbchanged)
{
if (! thd->killed)
- ret= sp_change_db(thd, olddb, 0);
+ ret= mysql_change_db(thd, olddb, 0);
}
m_is_invoked= FALSE;
DBUG_RETURN(ret);
@@ -753,68 +809,61 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
sp_rcontext *octx = thd->spcont;
sp_rcontext *nctx = NULL;
uint i;
- int ret;
- MEM_ROOT call_mem_root;
- Query_arena call_arena(&call_mem_root, INITIALIZED_FOR_SP), backup_arena;
+ Item_null *nit;
+ int ret= -1; // Assume error
if (argcount != params)
{
- // Need to use my_printf_error here, or it will not terminate the
- // invoking query properly.
+ /*
+ Need to use my_error here, or it will not terminate the
+ invoking query properly.
+ */
my_error(ER_SP_WRONG_NO_OF_ARGS, MYF(0),
"FUNCTION", m_qname.str, params, argcount);
- DBUG_RETURN(-1);
+ goto end;
}
- init_alloc_root(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
-
// QQ Should have some error checking here? (types, etc...)
- nctx= new sp_rcontext(csize, hmax, cmax);
- nctx->callers_mem_root= thd->mem_root;
+ if (!(nctx= new sp_rcontext(csize, hmax, cmax)))
+ goto end;
for (i= 0 ; i < argcount ; i++)
{
sp_pvar_t *pvar = m_pcont->find_pvar(i);
- Item *it= sp_eval_func_item(thd, argp++, pvar->type, NULL);
+ Item *it= sp_eval_func_item(thd, argp++, pvar->type, NULL, FALSE);
- if (it)
- nctx->push_item(it);
- else
- {
- DBUG_RETURN(-1);
- }
+ if (!it)
+ goto end; // EOM error
+ nctx->push_item(it);
}
- // The rest of the frame are local variables which are all IN.
- // Default all variables to null (those with default clauses will
- // be set by an set instruction).
+
+ /*
+ The rest of the frame are local variables which are all IN.
+ Default all variables to null (those with default clauses will
+ be set by an set instruction).
+ */
+
+ nit= NULL; // Re-use this, and only create if needed
+ for (; i < csize ; i++)
{
- Item_null *nit= NULL; // Re-use this, and only create if needed
- for (; i < csize ; i++)
+ if (! nit)
{
- if (! nit)
- nit= new Item_null();
- nctx->push_item(nit);
+ if (!(nit= new Item_null()))
+ DBUG_RETURN(-1);
}
+ nctx->push_item(nit);
}
thd->spcont= nctx;
- thd->set_n_backup_item_arena(&call_arena, &backup_arena);
- /* mem_root was moved to backup_arena */
- DBUG_ASSERT(nctx->callers_mem_root == backup_arena.mem_root);
ret= execute(thd);
- // Partially restore context now.
- // We still need the call mem root and free list for processing
- // of the result.
- thd->restore_backup_item_arena(&call_arena, &backup_arena);
-
if (m_type == TYPE_ENUM_FUNCTION && ret == 0)
{
/* We need result only in function but not in trigger */
Item *it= nctx->get_result();
if (it)
- *resp= sp_eval_func_item(thd, &it, m_returns, NULL);
+ *resp= sp_eval_func_item(thd, &it, m_returns, NULL, FALSE);
else
{
my_error(ER_SP_NORETURNEND, MYF(0), m_name.str);
@@ -823,17 +872,15 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
}
nctx->pop_all_cursors(); // To avoid memory leaks after an error
+ delete nctx; // Doesn't do anything
thd->spcont= octx;
- // Now get rid of the rest of the callee context
- call_arena.free_items();
- free_root(&call_mem_root, MYF(0));
-
+end:
DBUG_RETURN(ret);
}
-static Item_func_get_user_var *
-item_is_user_var(Item *it)
+
+static Item_func_get_user_var *item_is_user_var(Item *it)
{
if (it->type() == Item::FUNC_ITEM)
{
@@ -845,21 +892,18 @@ item_is_user_var(Item *it)
return NULL;
}
-int
-sp_head::execute_procedure(THD *thd, List<Item> *args)
+
+int sp_head::execute_procedure(THD *thd, List<Item> *args)
{
- DBUG_ENTER("sp_head::execute_procedure");
- DBUG_PRINT("info", ("procedure %s", m_name.str));
int ret= 0;
uint csize = m_pcont->max_pvars();
uint params = m_pcont->current_pvars();
uint hmax = m_pcont->max_handlers();
uint cmax = m_pcont->max_cursors();
- sp_rcontext *octx = thd->spcont;
+ sp_rcontext *save_spcont, *octx;
sp_rcontext *nctx = NULL;
- my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx
- MEM_ROOT call_mem_root;
- Query_arena call_arena(&call_mem_root, INITIALIZED_FOR_SP), backup_arena;
+ DBUG_ENTER("sp_head::execute_procedure");
+ DBUG_PRINT("info", ("procedure %s", m_name.str));
if (args->elements != params)
{
@@ -868,7 +912,22 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
DBUG_RETURN(-1);
}
- init_alloc_root(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0);
+ save_spcont= octx= thd->spcont;
+ if (! octx)
+ { // Create a temporary old context
+ if (!(octx= new sp_rcontext(csize, hmax, cmax)))
+ DBUG_RETURN(-1);
+ thd->spcont= octx;
+
+ /* set callers_arena to thd, for upper-level function to work */
+ thd->spcont->callers_arena= thd;
+ }
+
+ if (!(nctx= new sp_rcontext(csize, hmax, cmax)))
+ {
+ thd->spcont= save_spcont;
+ DBUG_RETURN(-1);
+ }
if (csize > 0 || hmax > 0 || cmax > 0)
{
@@ -877,13 +936,6 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
List_iterator<Item> li(*args);
Item *it;
- nctx= new sp_rcontext(csize, hmax, cmax);
- if (! octx)
- { // Create a temporary old context
- octx= new sp_rcontext(csize, hmax, cmax);
- tmp_octx= TRUE;
- }
-
/* Evaluate SP arguments (i.e. get the values passed as parameters) */
// QQ: Should do type checking?
DBUG_PRINT("info",(" %.*s: eval args", m_name.length, m_name.str));
@@ -905,20 +957,25 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (pvar->mode == sp_param_out)
{
if (! nit)
- nit= new Item_null();
+ {
+ if (!(nit= new Item_null()))
+ {
+ ret= -1;
+ break;
+ }
+ }
nctx->push_item(nit); // OUT
}
else
{
- Item *it2= sp_eval_func_item(thd, li.ref(), pvar->type, NULL);
+ Item *it2= sp_eval_func_item(thd, li.ref(), pvar->type, NULL, FALSE);
- if (it2)
- nctx->push_item(it2); // IN or INOUT
- else
+ if (!it2)
{
ret= -1; // Eval failed
break;
}
+ nctx->push_item(it2); // IN or INOUT
}
}
}
@@ -929,35 +986,50 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
we'll leave it here.
*/
if (!thd->in_sub_stmt)
- close_thread_tables(thd, 0, 0, 0);
+ close_thread_tables(thd, 0, 0);
DBUG_PRINT("info",(" %.*s: eval args done", m_name.length, m_name.str));
- // The rest of the frame are local variables which are all IN.
- // Default all variables to null (those with default clauses will
- // be set by an set instruction).
+ /*
+ The rest of the frame are local variables which are all IN.
+ Default all variables to null (those with default clauses will
+ be set by an set instruction).
+ */
for (; i < csize ; i++)
{
if (! nit)
- nit= new Item_null();
+ {
+ if (!(nit= new Item_null()))
+ {
+ ret= -1;
+ break;
+ }
+ }
nctx->push_item(nit);
}
- thd->spcont= nctx;
}
+ thd->spcont= nctx;
+
if (! ret)
- {
- thd->set_n_backup_item_arena(&call_arena, &backup_arena);
ret= execute(thd);
- thd->restore_backup_item_arena(&call_arena, &backup_arena);
- }
+
+ /*
+ In the case when we weren't able to employ reuse mechanism for
+ OUT/INOUT paranmeters, we should reallocate memory. This
+ allocation should be done on the arena which will live through
+ all execution of calling routine.
+ */
+ thd->spcont->callers_arena= octx->callers_arena;
if (!ret && csize > 0)
{
List_iterator<Item> li(*args);
Item *it;
- // Copy back all OUT or INOUT values to the previous frame, or
- // set global user variables
+ /*
+ Copy back all OUT or INOUT values to the previous frame, or
+ set global user variables
+ */
for (uint i = 0 ; (it= li++) && i < params ; i++)
{
sp_pvar_t *pvar= m_pcont->find_pvar(i);
@@ -972,12 +1044,20 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
Item *val= nctx->get_item(i);
Item *orig= octx->get_item(offset);
Item *o_item_next;
- Item *o_free_list= thd->free_list;
+ /* we'll use callers_arena in sp_eval_func_item */
+ Item *o_free_list= thd->spcont->callers_arena->free_list;
+
LINT_INIT(o_item_next);
if (orig)
o_item_next= orig->next;
- copy= sp_eval_func_item(thd, &val, pvar->type, orig); // Copy
+
+ /*
+ We might need to allocate new item if we weren't able to
+ employ reuse mechanism. Then we should do it on the callers arena.
+ */
+ copy= sp_eval_func_item(thd, &val, pvar->type, orig, TRUE); // Copy
+
if (!copy)
{
ret= -1;
@@ -987,9 +1067,11 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
octx->set_item(offset, copy);
if (orig && copy == orig)
{
- // A reused item slot, where the constructor put it in the
- // free_list, so we have to restore the list.
- thd->free_list= o_free_list;
+ /*
+ A reused item slot, where the constructor put it in the
+ free_list, so we have to restore the list.
+ */
+ thd->spcont->callers_arena->free_list= o_free_list;
copy->next= o_item_next;
}
}
@@ -1017,16 +1099,12 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
}
}
- if (tmp_octx)
- octx= NULL;
- if (nctx)
- nctx->pop_all_cursors(); // To avoid memory leaks after an error
- thd->spcont= octx;
+ if (!save_spcont)
+ delete octx; // Does nothing
- // Now get rid of the rest of the callee context
- call_arena.free_items();
- thd->lex->unit.cleanup();
- free_root(&call_mem_root, MYF(0));
+ nctx->pop_all_cursors(); // To avoid memory leaks after an error
+ delete nctx; // Does nothing
+ thd->spcont= save_spcont;
DBUG_RETURN(ret);
}
@@ -1420,8 +1498,6 @@ sp_head::opt_mark(uint ip)
ip= i->opt_mark(this);
}
-// ------------------------------------------------------------------
-
/*
Prepare LEX and thread for execution of instruction, if requested open
@@ -1513,6 +1589,7 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
thd->proc_info="closing tables";
close_thread_tables(thd);
+ thd->proc_info= 0;
if (m_lex->query_tables_own_last)
{
@@ -1549,9 +1626,10 @@ sp_lex_keeper::reset_lex_and_exec_core(THD *thd, uint *nextp,
}
-//
-// sp_instr
-//
+/*
+ sp_instr class functions
+*/
+
int sp_instr::exec_core(THD *thd, uint *nextp)
{
DBUG_ASSERT(0);
@@ -1559,9 +1637,10 @@ int sp_instr::exec_core(THD *thd, uint *nextp)
}
-//
-// sp_instr_stmt
-//
+/*
+ sp_instr_stmt class functions
+*/
+
int
sp_instr_stmt::execute(THD *thd, uint *nextp)
{
@@ -1606,9 +1685,11 @@ sp_instr_stmt::exec_core(THD *thd, uint *nextp)
return res;
}
-//
-// sp_instr_set
-//
+
+/*
+ sp_instr_set class functions
+*/
+
int
sp_instr_set::execute(THD *thd, uint *nextp)
{
@@ -1618,6 +1699,7 @@ sp_instr_set::execute(THD *thd, uint *nextp)
DBUG_RETURN(m_lex_keeper.reset_lex_and_exec_core(thd, nextp, TRUE, this));
}
+
int
sp_instr_set::exec_core(THD *thd, uint *nextp)
{
@@ -1638,9 +1720,10 @@ sp_instr_set::print(String *str)
}
-//
-// sp_instr_set_trigger_field
-//
+/*
+ sp_instr_set_trigger_field class functions
+*/
+
int
sp_instr_set_trigger_field::execute(THD *thd, uint *nextp)
{
@@ -1671,9 +1754,11 @@ sp_instr_set_trigger_field::print(String *str)
value->print(str);
}
-//
-// sp_instr_jump
-//
+
+/*
+ sp_instr_jump class functions
+*/
+
int
sp_instr_jump::execute(THD *thd, uint *nextp)
{
@@ -1732,9 +1817,10 @@ sp_instr_jump::opt_move(uint dst, List<sp_instr> *bp)
m_ip= dst;
}
-//
-// sp_instr_jump_if
-//
+
+/*
+ sp_instr_jump_if class functions
+*/
int
sp_instr_jump_if::execute(THD *thd, uint *nextp)
@@ -1790,9 +1876,11 @@ sp_instr_jump_if::opt_mark(sp_head *sp)
return m_ip+1;
}
-//
-// sp_instr_jump_if_not
-//
+
+/*
+ sp_instr_jump_if_not class functions
+*/
+
int
sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
{
@@ -1823,6 +1911,7 @@ sp_instr_jump_if_not::exec_core(THD *thd, uint *nextp)
return res;
}
+
void
sp_instr_jump_if_not::print(String *str)
{
@@ -1833,6 +1922,7 @@ sp_instr_jump_if_not::print(String *str)
m_expr->print(str);
}
+
uint
sp_instr_jump_if_not::opt_mark(sp_head *sp)
{
@@ -1848,9 +1938,10 @@ sp_instr_jump_if_not::opt_mark(sp_head *sp)
return m_ip+1;
}
-//
-// sp_instr_freturn
-//
+
+/*
+ sp_instr_freturn class functions
+*/
int
sp_instr_freturn::execute(THD *thd, uint *nextp)
@@ -1866,7 +1957,7 @@ sp_instr_freturn::exec_core(THD *thd, uint *nextp)
Item *it;
int res;
- it= sp_eval_func_item(thd, &m_value, m_type, NULL);
+ it= sp_eval_func_item(thd, &m_value, m_type, NULL, TRUE);
if (! it)
res= -1;
else
@@ -1889,9 +1980,10 @@ sp_instr_freturn::print(String *str)
m_value->print(str);
}
-//
-// sp_instr_hpush_jump
-//
+/*
+ sp_instr_hpush_jump class functions
+*/
+
int
sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
{
@@ -1900,7 +1992,7 @@ sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
sp_cond_type_t *p;
while ((p= li++))
- thd->spcont->push_handler(p, m_handler, m_type, m_frame);
+ thd->spcont->push_handler(p, m_ip+1, m_type, m_frame);
*nextp= m_dest;
DBUG_RETURN(0);
@@ -1917,7 +2009,7 @@ sp_instr_hpush_jump::print(String *str)
str->append(" f=");
str->qs_append(m_frame);
str->append(" h=");
- str->qs_append(m_handler);
+ str->qs_append(m_ip+1);
}
uint
@@ -1935,9 +2027,11 @@ sp_instr_hpush_jump::opt_mark(sp_head *sp)
return m_ip+1;
}
-//
-// sp_instr_hpop
-//
+
+/*
+ sp_instr_hpop class functions
+*/
+
int
sp_instr_hpop::execute(THD *thd, uint *nextp)
{
@@ -1962,9 +2056,10 @@ sp_instr_hpop::backpatch(uint dest, sp_pcontext *dst_ctx)
}
-//
-// sp_instr_hreturn
-//
+/*
+ sp_instr_hreturn class functions
+*/
+
int
sp_instr_hreturn::execute(THD *thd, uint *nextp)
{
@@ -1980,6 +2075,7 @@ sp_instr_hreturn::execute(THD *thd, uint *nextp)
DBUG_RETURN(0);
}
+
void
sp_instr_hreturn::print(String *str)
{
@@ -1990,6 +2086,7 @@ sp_instr_hreturn::print(String *str)
str->qs_append(m_dest);
}
+
uint
sp_instr_hreturn::opt_mark(sp_head *sp)
{
@@ -2003,27 +2100,45 @@ sp_instr_hreturn::opt_mark(sp_head *sp)
}
-//
-// sp_instr_cpush
-//
+/*
+ sp_instr_cpush class functions
+*/
+
int
sp_instr_cpush::execute(THD *thd, uint *nextp)
{
+ Query_arena backup_current_arena;
DBUG_ENTER("sp_instr_cpush::execute");
+
+ /*
+ We should create cursors in the callers arena, as
+ it could be (and usually is) used in several instructions.
+ */
+ thd->set_n_backup_item_arena(thd->spcont->callers_arena,
+ &backup_current_arena);
+
thd->spcont->push_cursor(&m_lex_keeper, this);
+
+ thd->restore_backup_item_arena(thd->spcont->callers_arena,
+ &backup_current_arena);
+
*nextp= m_ip+1;
+
DBUG_RETURN(0);
}
+
void
sp_instr_cpush::print(String *str)
{
str->append("cpush");
}
-//
-// sp_instr_cpop
-//
+
+/*
+ sp_instr_cpop class functions
+*/
+
int
sp_instr_cpop::execute(THD *thd, uint *nextp)
{
@@ -2033,6 +2148,7 @@ sp_instr_cpop::execute(THD *thd, uint *nextp)
DBUG_RETURN(0);
}
+
void
sp_instr_cpop::print(String *str)
{
@@ -2047,9 +2163,11 @@ sp_instr_cpop::backpatch(uint dest, sp_pcontext *dst_ctx)
m_count= m_ctx->diff_cursors(dst_ctx);
}
-//
-// sp_instr_copen
-//
+
+/*
+ sp_instr_copen class functions
+*/
+
int
sp_instr_copen::execute(THD *thd, uint *nextp)
{
@@ -2117,9 +2235,11 @@ sp_instr_copen::print(String *str)
str->qs_append(m_cursor);
}
-//
-// sp_instr_cclose
-//
+
+/*
+ sp_instr_cclose class functions
+*/
+
int
sp_instr_cclose::execute(THD *thd, uint *nextp)
{
@@ -2135,6 +2255,7 @@ sp_instr_cclose::execute(THD *thd, uint *nextp)
DBUG_RETURN(res);
}
+
void
sp_instr_cclose::print(String *str)
{
@@ -2143,24 +2264,35 @@ sp_instr_cclose::print(String *str)
str->qs_append(m_cursor);
}
-//
-// sp_instr_cfetch
-//
+
+/*
+ sp_instr_cfetch class functions
+*/
+
int
sp_instr_cfetch::execute(THD *thd, uint *nextp)
{
sp_cursor *c= thd->spcont->get_cursor(m_cursor);
int res;
+ Query_arena backup_current_arena;
DBUG_ENTER("sp_instr_cfetch::execute");
if (! c)
res= -1;
else
+ {
+ thd->set_n_backup_item_arena(thd->spcont->callers_arena,
+ &backup_current_arena);
res= c->fetch(thd, &m_varlist);
+ thd->restore_backup_item_arena(thd->spcont->callers_arena,
+ &backup_current_arena);
+ }
+
*nextp= m_ip+1;
DBUG_RETURN(res);
}
+
void
sp_instr_cfetch::print(String *str)
{
@@ -2178,9 +2310,11 @@ sp_instr_cfetch::print(String *str)
}
}
-//
-// sp_instr_error
-//
+
+/*
+ sp_instr_error class functions
+*/
+
int
sp_instr_error::execute(THD *thd, uint *nextp)
{
@@ -2191,6 +2325,7 @@ sp_instr_error::execute(THD *thd, uint *nextp)
DBUG_RETURN(-1);
}
+
void
sp_instr_error::print(String *str)
{
@@ -2199,12 +2334,12 @@ sp_instr_error::print(String *str)
str->qs_append(m_errcode);
}
-/* ------------------------------------------------------------------ */
+/* ------------------------------------------------------------------ */
-//
-// Security context swapping
-//
+/*
+ Security context swapping
+*/
#ifndef NO_EMBEDDED_ACCESS_CHECKS
void
@@ -2453,11 +2588,12 @@ sp_head::add_used_tables_to_table_list(THD *thd,
DBUG_RETURN(result);
}
+
/*
- * Simple function for adding an explicetly named (systems) table to
- * the global table list, e.g. "mysql", "proc".
- *
- */
+ Simple function for adding an explicetly named (systems) table to
+ the global table list, e.g. "mysql", "proc".
+*/
+
TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name,
diff --git a/sql/sp_head.h b/sql/sp_head.h
index e15b68be158..8ae7834eb2a 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -714,7 +714,6 @@ public:
sp_instr_hpush_jump(uint ip, sp_pcontext *ctx, int htype, uint fp)
: sp_instr_jump(ip, ctx), m_type(htype), m_frame(fp)
{
- m_handler= ip+1;
m_cond.empty();
}
@@ -743,7 +742,6 @@ private:
int m_type; // Handler type
uint m_frame;
- uint m_handler; // Location of handler
List<struct sp_cond_type> m_cond;
}; // class sp_instr_hpush_jump : public sp_instr_jump
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index d0817e43790..748c09f56c7 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -32,7 +32,6 @@ sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax)
: m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0),
m_hfound(-1), m_ccount(0)
{
- callers_mem_root= NULL;
in_handler= FALSE;
m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
@@ -47,17 +46,18 @@ sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr,
enum_field_types type)
{
extern Item *sp_eval_func_item(THD *thd, Item **it, enum_field_types type,
- Item *reuse);
+ Item *reuse, bool use_callers_arena);
Item *it;
Item *reuse_it;
Item *old_item_next;
- Item *old_free_list= thd->free_list;
+ /* sp_eval_func_item will use callers_arena */
+ Item *old_free_list= thd->spcont->callers_arena->free_list;
int res;
LINT_INIT(old_item_next);
if ((reuse_it= get_item(idx)))
old_item_next= reuse_it->next;
- it= sp_eval_func_item(thd, item_addr, type, reuse_it);
+ it= sp_eval_func_item(thd, item_addr, type, reuse_it, TRUE);
if (! it)
res= -1;
else
@@ -67,7 +67,7 @@ sp_rcontext::set_item_eval(THD *thd, uint idx, Item **item_addr,
{
// A reused item slot, where the constructor put it in the free_list,
// so we have to restore the list.
- thd->free_list= old_free_list;
+ thd->spcont->callers_arena->free_list= old_free_list;
it->next= old_item_next;
}
set_item(idx, it);
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index 856beb13f6d..dedbc7bdef1 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -48,8 +48,14 @@ class sp_rcontext : public Sql_alloc
public:
- MEM_ROOT *callers_mem_root; // Used to store result fields
bool in_handler;
+ /*
+ Arena used to (re) allocate items on . E.g. reallocate INOUT/OUT
+ SP parameters when they don't fit into prealloced items. This
+ is common situation with String items. It is used mainly in
+ sp_eval_func_item().
+ */
+ Query_arena *callers_arena;
sp_rcontext(uint fsize, uint hmax, uint cmax);
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 5d2f5d55a7b..41b9257059d 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -67,7 +67,8 @@ static ulong get_access(TABLE *form,uint fieldnr, uint *next_field=0);
static int acl_compare(ACL_ACCESS *a,ACL_ACCESS *b);
static ulong get_sort(uint count,...);
static void init_check_host(void);
-static ACL_USER *find_acl_user(const char *host, const char *user);
+static ACL_USER *find_acl_user(const char *host, const char *user,
+ my_bool exact);
static bool update_user_table(THD *thd, const char *host, const char *user,
const char *new_password, uint new_password_len);
static void update_hostname(acl_host_and_ip *host, const char *hostname);
@@ -1288,7 +1289,7 @@ bool check_change_password(THD *thd, const char *host, const char *user,
}
if (!thd->slave_thread &&
(strcmp(thd->user,user) ||
- my_strcasecmp(system_charset_info, host, thd->host_or_ip)))
+ my_strcasecmp(system_charset_info, host, thd->priv_host)))
{
if (check_access(thd, UPDATE_ACL, "mysql",0,1,0))
return(1);
@@ -1339,7 +1340,7 @@ bool change_password(THD *thd, const char *host, const char *user,
VOID(pthread_mutex_lock(&acl_cache->lock));
ACL_USER *acl_user;
- if (!(acl_user= find_acl_user(host, user)))
+ if (!(acl_user= find_acl_user(host, user, TRUE)))
{
VOID(pthread_mutex_unlock(&acl_cache->lock));
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
@@ -1379,7 +1380,7 @@ bool change_password(THD *thd, const char *host, const char *user,
*/
static ACL_USER *
-find_acl_user(const char *host, const char *user)
+find_acl_user(const char *host, const char *user, my_bool exact)
{
DBUG_ENTER("find_acl_user");
DBUG_PRINT("enter",("host: '%s' user: '%s'",host,user));
@@ -1395,7 +1396,9 @@ find_acl_user(const char *host, const char *user)
if (!acl_user->user && !user[0] ||
acl_user->user && !strcmp(user,acl_user->user))
{
- if (compare_hostname(&acl_user->host,host,host))
+ if (exact ? !my_strcasecmp(&my_charset_latin1, host,
+ acl_user->host.hostname) :
+ compare_hostname(&acl_user->host,host,host))
{
DBUG_RETURN(acl_user);
}
@@ -1821,7 +1824,7 @@ static int replace_db_table(TABLE *table, const char *db,
}
/* Check if there is such a user in user table in memory? */
- if (!find_acl_user(combo.host.str,combo.user.str))
+ if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
{
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH), MYF(0));
DBUG_RETURN(-1);
@@ -2368,7 +2371,7 @@ static int replace_table_table(THD *thd, GRANT_TABLE *grant_table,
The following should always succeed as new users are created before
this function is called!
*/
- if (!find_acl_user(combo.host.str,combo.user.str))
+ if (!find_acl_user(combo.host.str,combo.user.str, FALSE))
{
my_message(ER_PASSWORD_NO_MATCH, ER(ER_PASSWORD_NO_MATCH),
MYF(0)); /* purecov: deadcode */
@@ -2490,7 +2493,7 @@ static int replace_routine_table(THD *thd, GRANT_NAME *grant_name,
The following should always succeed as new users are created before
this function is called!
*/
- if (!find_acl_user(combo.host.str,combo.user.str))
+ if (!find_acl_user(combo.host.str, combo.user.str, FALSE))
{
my_error(ER_PASSWORD_NO_MATCH,MYF(0));
DBUG_RETURN(-1);
@@ -2637,10 +2640,11 @@ bool mysql_table_grant(THD *thd, TABLE_LIST *table_list,
while ((column = column_iter++))
{
uint unused_field_idx= NO_CACHED_FIELD_INDEX;
- Field *f=find_field_in_table(thd, table_list, column->column.ptr(),
- column->column.ptr(),
- column->column.length(), 0, 1, 1, 0,
- &unused_field_idx, FALSE);
+ TABLE_LIST *dummy;
+ Field *f=find_field_in_table_ref(thd, table_list, column->column.ptr(),
+ column->column.ptr(), NULL, NULL,
+ column->column.length(), 0, 1, 1, 0,
+ &unused_field_idx, FALSE, &dummy);
if (f == (Field*)0)
{
my_error(ER_BAD_FIELD_ERROR, MYF(0),
@@ -3695,6 +3699,24 @@ ulong get_table_grant(THD *thd, TABLE_LIST *table)
}
+/*
+ Determine the access priviliges for a field.
+
+ SYNOPSIS
+ get_column_grant()
+ thd thread handler
+ grant grants table descriptor
+ db_name name of database that the field belongs to
+ table_name name of table that the field belongs to
+ field_name name of field
+
+ DESCRIPTION
+ The procedure may also modify: grant->grant_table and grant->version.
+
+ RETURN
+ The access priviliges for the field db_name.table_name.field_name
+*/
+
ulong get_column_grant(THD *thd, GRANT_INFO *grant,
const char *db_name, const char *table_name,
const char *field_name)
@@ -4244,7 +4266,7 @@ void get_privilege_desc(char *to, uint max_length, ulong access)
void get_mqh(const char *user, const char *host, USER_CONN *uc)
{
ACL_USER *acl_user;
- if (initialized && (acl_user= find_acl_user(host,user)))
+ if (initialized && (acl_user= find_acl_user(host,user, FALSE)))
uc->user_resources= acl_user->user_resource;
else
bzero((char*) &uc->user_resources, sizeof(uc->user_resources));
@@ -4589,11 +4611,12 @@ static int handle_grant_struct(uint struct_no, bool drop,
ACL_DB *acl_db;
GRANT_NAME *grant_name;
DBUG_ENTER("handle_grant_struct");
+ DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
+ struct_no, user_from->user.str, user_from->host.str));
+
LINT_INIT(acl_user);
LINT_INIT(acl_db);
LINT_INIT(grant_name);
- DBUG_PRINT("info",("scan struct: %u search: '%s'@'%s'",
- struct_no, user_from->user.str, user_from->host.str));
/* Get the number of elements in the in-memory structure. */
switch (struct_no) {
@@ -5289,10 +5312,12 @@ bool sp_grant_privileges(THD *thd, const char *sp_db, const char *sp_name,
combo->user.str= thd->user;
- if (!find_acl_user(combo->host.str=(char*)thd->host_or_ip, combo->user.str) &&
- !find_acl_user(combo->host.str=(char*)thd->host, combo->user.str) &&
- !find_acl_user(combo->host.str=(char*)thd->ip, combo->user.str) &&
- !find_acl_user(combo->host.str=(char*)"%", combo->user.str))
+ if (!find_acl_user(combo->host.str=(char*)thd->host_or_ip, combo->user.str,
+ FALSE) &&
+ !find_acl_user(combo->host.str=(char*)thd->host, combo->user.str,
+ FALSE) &&
+ !find_acl_user(combo->host.str=(char*)thd->ip, combo->user.str, FALSE) &&
+ !find_acl_user(combo->host.str=(char*)"%", combo->user.str, FALSE))
DBUG_RETURN(TRUE);
bzero((char*)tables, sizeof(TABLE_LIST));
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index dfed0e36b70..b8b96f14205 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -129,12 +129,11 @@ static void check_unused(void)
# Pointer to list of names of open tables.
*/
-OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild)
+OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *db, const char *wild)
{
int result = 0;
OPEN_TABLE_LIST **start_list, *open_list;
TABLE_LIST table_list;
- char name[NAME_LEN*2];
DBUG_ENTER("list_open_tables");
VOID(pthread_mutex_lock(&LOCK_open));
@@ -151,12 +150,10 @@ OPEN_TABLE_LIST *list_open_tables(THD *thd, const char *wild)
DBUG_ASSERT(share->table_name != 0);
if ((!share->table_name)) // To be removed
continue; // Shouldn't happen
- if (wild)
- {
- strxmov(name,share->table_cache_key,".",share->table_name,NullS);
- if (wild_compare(name,wild,0))
- continue;
- }
+ if (db && my_strcasecmp(system_charset_info, db, share->db))
+ continue;
+ if (wild && wild_compare(share->table_name,wild,0))
+ continue;
/* Check if user has SELECT privilege for any column in the table */
table_list.db= (char*) share->db;
@@ -404,8 +401,7 @@ static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
upper level) and will leave prelocked mode if needed.
*/
-void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
- TABLE *stopper)
+void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived)
{
bool found_old_table;
prelocked_mode_type prelocked_mode= thd->prelocked_mode;
@@ -502,7 +498,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
*/
bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
if (!thd->active_transaction())
- thd->transaction.xid.null();
+ thd->transaction.xid_state.xid.null();
/* VOID(pthread_sigmask(SIG_SETMASK,&thd->block_signals,NULL)); */
if (!lock_in_use)
@@ -512,7 +508,7 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
DBUG_PRINT("info", ("thd->open_tables: %p", thd->open_tables));
found_old_table= 0;
- while (thd->open_tables != stopper)
+ while (thd->open_tables)
found_old_table|=close_thread_table(thd, &thd->open_tables);
thd->some_tables_deleted=0;
@@ -569,7 +565,7 @@ bool close_thread_table(THD *thd, TABLE **table_ptr)
else
{
// Free memory and reset for next loop
- table->file->ha_reset();
+ table->file->reset();
}
table->in_use=0;
if (unused_tables)
@@ -1047,35 +1043,61 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
{ // Using table locks
TABLE *best_table= 0;
int best_distance= INT_MIN;
+ bool check_if_used= thd->prelocked_mode &&
+ ((int) table_list->lock_type >=
+ (int) TL_WRITE_ALLOW_WRITE);
for (table=thd->open_tables; table ; table=table->next)
{
if (table->s->key_length == key_length &&
- !memcmp(table->s->table_cache_key, key, key_length) &&
- !my_strcasecmp(system_charset_info, table->alias, alias) &&
- table->query_id != thd->query_id && /* skip tables already used */
- !(thd->prelocked_mode && table->query_id))
+ !memcmp(table->s->table_cache_key, key, key_length))
{
- int distance= ((int) table->reginfo.lock_type -
- (int) table_list->lock_type);
- /*
- Find a table that either has the exact lock type requested,
- or has the best suitable lock. In case there is no locked
- table that has an equal or higher lock than requested,
- we us the closest matching lock to be able to produce an error
- message about wrong lock mode on the table. The best_table is changed
- if bd < 0 <= d or bd < d < 0 or 0 <= d < bd.
-
- distance < 0 - No suitable lock found
- distance > 0 - we have lock mode higher then we require
- distance == 0 - we have lock mode exactly which we need
- */
- if (best_distance < 0 && distance > best_distance ||
- distance >= 0 && distance < best_distance)
+ if (check_if_used && table->query_id &&
+ table->query_id != thd->query_id)
{
- best_distance= distance;
- best_table= table;
- if (best_distance == 0) // Found perfect lock
- break;
+ /*
+ If we are in stored function or trigger we should ensure that
+ we won't change table that is already used by calling statement.
+ So if we are opening table for writing, we should check that it
+ is not already open by some calling stamement.
+ */
+ my_error(ER_CANT_UPDATE_USED_TABLE_IN_SF_OR_TRG, MYF(0),
+ table->s->table_name);
+ DBUG_RETURN(0);
+ }
+ if (!my_strcasecmp(system_charset_info, table->alias, alias) &&
+ table->query_id != thd->query_id && /* skip tables already used */
+ !(thd->prelocked_mode && table->query_id))
+ {
+ int distance= ((int) table->reginfo.lock_type -
+ (int) table_list->lock_type);
+ /*
+ Find a table that either has the exact lock type requested,
+ or has the best suitable lock. In case there is no locked
+ table that has an equal or higher lock than requested,
+ we us the closest matching lock to be able to produce an error
+ message about wrong lock mode on the table. The best_table
+ is changed if bd < 0 <= d or bd < d < 0 or 0 <= d < bd.
+
+ distance < 0 - No suitable lock found
+ distance > 0 - we have lock mode higher then we require
+ distance == 0 - we have lock mode exactly which we need
+ */
+ if (best_distance < 0 && distance > best_distance ||
+ distance >= 0 && distance < best_distance)
+ {
+ best_distance= distance;
+ best_table= table;
+ if (best_distance == 0 && !check_if_used)
+ {
+ /*
+ If we have found perfect match and we don't need to check that
+ table is not used by one of calling statements (assuming that
+ we are inside of function or trigger) we can finish iterating
+ through open tables list.
+ */
+ break;
+ }
+ }
}
}
}
@@ -1669,7 +1691,7 @@ static int open_unireg_entry(THD *thd, TABLE *entry, const char *db,
{
/* Give right error message */
thd->clear_error();
- DBUG_PRINT("error", ("Dicovery of %s/%s failed", db, name));
+ DBUG_PRINT("error", ("Discovery of %s/%s failed", db, name));
my_printf_error(ER_UNKNOWN_ERROR,
"Failed to open '%-.64s', error while "
"unpacking from engine",
@@ -1804,6 +1826,9 @@ err:
thd - thread handler
start - list of tables in/out
counter - number of opened tables will be return using this parameter
+ flags - bitmap of flags to modify how the tables will be open:
+ MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
+ done a flush or namelock on it.
NOTE
Unless we are already in prelocked mode, this function will also precache
@@ -1821,7 +1846,7 @@ err:
-1 - error
*/
-int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
+int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
{
TABLE_LIST *tables;
bool refresh;
@@ -1900,7 +1925,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
(*counter)++;
if (!tables->table &&
- !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, 0)))
+ !(tables->table= open_table(thd, tables, &new_frm_mem, &refresh, flags)))
{
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
@@ -1919,8 +1944,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
has added its base tables after itself, adjust the boundary pointer
accordingly.
*/
- if (query_tables_last_own &&
- query_tables_last_own == &(tables->next_global) &&
+ if (query_tables_last_own == &(tables->next_global) &&
tables->view->query_tables)
query_tables_last_own= tables->view->query_tables_last;
@@ -2143,7 +2167,8 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
{
DBUG_ENTER("simple_open_n_lock_tables");
uint counter;
- if (open_tables(thd, &tables, &counter) || lock_tables(thd, tables, counter))
+ if (open_tables(thd, &tables, &counter, 0) ||
+ lock_tables(thd, tables, counter))
DBUG_RETURN(-1); /* purecov: inspected */
DBUG_RETURN(0);
}
@@ -2170,7 +2195,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
{
uint counter;
DBUG_ENTER("open_and_lock_tables");
- if (open_tables(thd, &tables, &counter) ||
+ if (open_tables(thd, &tables, &counter, 0) ||
lock_tables(thd, tables, counter) ||
mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
(thd->fill_derived_tables() &&
@@ -2187,6 +2212,9 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
open_normal_and_derived_tables
thd - thread handler
tables - list of tables for open
+ flags - bitmap of flags to modify how the tables will be open:
+ MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
+ done a flush or namelock on it.
RETURN
FALSE - ok
@@ -2197,12 +2225,12 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
data from the tables.
*/
-bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables)
+bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags)
{
uint counter;
DBUG_ENTER("open_normal_and_derived_tables");
DBUG_ASSERT(!thd->fill_derived_tables());
- if (open_tables(thd, &tables, &counter) ||
+ if (open_tables(thd, &tables, &counter, flags) ||
mysql_handle_derived(thd->lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE); /* purecov: inspected */
DBUG_RETURN(0);
@@ -2466,190 +2494,243 @@ bool rm_temporary_table(enum db_type base, char *path)
/*****************************************************************************
-** find field in list or tables. if field is unqualifed and unique,
-** return unique field
+* The following find_field_in_XXX procedures implement the core of the
+* name resolution functionality. The entry point to resolve a column name in a
+* list of tables is 'find_field_in_tables'. It calls 'find_field_in_table_ref'
+* for each table reference. In turn, depending on the type of table reference,
+* 'find_field_in_table_ref' calls one of the 'find_field_in_XXX' procedures
+* below specific for the type of table reference.
******************************************************************************/
-/* Special Field pointers for find_field_in_tables returning */
+/* Special Field pointers as return values of find_field_in_XXX functions. */
Field *not_found_field= (Field*) 0x1;
Field *view_ref_found= (Field*) 0x2;
#define WRONG_GRANT (Field*) -1
+static void update_field_dependencies(THD *thd, Field *field, TABLE *table)
+{
+ if (thd->set_query_id)
+ {
+ if (field->query_id != thd->query_id)
+ {
+ field->query_id= thd->query_id;
+ table->used_fields++;
+ table->used_keys.intersect(field->part_of_key);
+ }
+ else
+ thd->dupp_field= field;
+ }
+}
+
/*
- Find field in table or view
+ Find a field by name in a view that uses merge algorithm.
SYNOPSIS
- find_field_in_table()
+ find_field_in_view()
thd thread handler
- table_list table where to find
+ table_list view to search for 'name'
name name of field
item_name name of item if it will be created (VIEW)
length length of name
- ref [in/out] expression substituted in VIEW should be
- passed using this reference (return
- view_ref_found)
- (*ref != NULL) only if *ref contains
- the item that we need to replace.
- check_grants_table do check columns grants for table?
- check_grants_view do check columns grants for view?
- allow_rowid do allow finding of "_rowid" field?
- cached_field_index_ptr cached position in field list (used to
- speedup prepared tables field finding)
+ ref expression substituted in VIEW should be passed
+ using this reference (return view_ref_found)
+ check_grants do check columns grants for view?
register_tree_change TRUE if ref is not stack variable and we
- need register changes in item tree
+ need register changes in item tree
RETURN
0 field is not found
view_ref_found found value in VIEW (real result is in *ref)
- # pointer to field
+ # pointer to field - only for schema table fields
*/
-Field *
-find_field_in_table(THD *thd, TABLE_LIST *table_list,
- const char *name, const char *item_name,
- uint length, Item **ref,
- bool check_grants_table, bool check_grants_view,
- bool allow_rowid,
- uint *cached_field_index_ptr,
- bool register_tree_change)
+static Field *
+find_field_in_view(THD *thd, TABLE_LIST *table_list,
+ const char *name, const char *item_name,
+ uint length, Item **ref, bool check_grants,
+ bool register_tree_change)
{
- Field *fld;
- DBUG_ENTER("find_field_in_table");
- DBUG_PRINT("enter", ("table: '%s' name: '%s' item name: '%s' ref 0x%lx",
- table_list->alias, name, item_name, (ulong) ref));
- if (table_list->field_translation)
+ DBUG_ENTER("find_field_in_view");
+ DBUG_PRINT("enter",
+ ("view: '%s', field name: '%s', item name: '%s', ref 0x%lx",
+ table_list->alias, name, item_name, (ulong) ref));
+ Field_iterator_view field_it;
+ field_it.set(table_list);
+ DBUG_ASSERT(table_list->schema_table_reformed ||
+ (ref != 0 && table_list->view != 0));
+ for (; !field_it.end_of_fields(); field_it.next())
{
- Field_iterator_view field_it;
- field_it.set(table_list);
- DBUG_ASSERT(table_list->schema_table_reformed ||
- (ref != 0 && table_list->view != 0));
- for (; !field_it.end_of_fields(); field_it.next())
+ if (!my_strcasecmp(system_charset_info, field_it.name(), name))
{
- if (!my_strcasecmp(system_charset_info, field_it.name(), name))
- {
- if (table_list->schema_table_reformed)
- {
- /*
- Translation table items are always Item_fields
- and fixed already('mysql_schema_table' function).
- So we can return ->field. It is used only for
- 'show & where' commands.
- */
- DBUG_RETURN(((Item_field*) (field_it.item()))->field);
- }
+ if (table_list->schema_table_reformed)
+ /*
+ Translation table items are always Item_fields and fixed already
+ ('mysql_schema_table' function). So we can return ->field. It is
+ used only for 'show & where' commands.
+ */
+ DBUG_RETURN(((Item_field*) (field_it.item()))->field);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (check_grants_view &&
- check_grant_column(thd, &table_list->grant,
- table_list->view_db.str,
- table_list->view_name.str,
- name, length))
- DBUG_RETURN(WRONG_GRANT);
+ if (check_grants &&
+ check_grant_column(thd, &table_list->grant,
+ table_list->view_db.str,
+ table_list->view_name.str,
+ name, length))
+ DBUG_RETURN(WRONG_GRANT);
#endif
- Item *item= field_it.create_item(thd);
- if (!item)
- {
- DBUG_RETURN(0);
- }
- /*
- *ref != NULL means that *ref contains the item that we need to
- replace. If the item was aliased by the user, set the alias to
- the replacing item.
- */
- if (*ref && !(*ref)->is_autogenerated_name)
- item->set_name((*ref)->name, (*ref)->name_length,
- system_charset_info);
- if (register_tree_change)
- thd->change_item_tree(ref, item);
- else
- *ref= item;
- DBUG_RETURN((Field*) view_ref_found);
- }
+ Item *item= field_it.create_item(thd);
+ if (!item)
+ DBUG_RETURN(0);
+ /*
+ *ref != NULL means that *ref contains the item that we need to
+ replace. If the item was aliased by the user, set the alias to
+ the replacing item.
+ */
+ if (*ref && !(*ref)->is_autogenerated_name)
+ item->set_name((*ref)->name, (*ref)->name_length,
+ system_charset_info);
+ if (register_tree_change)
+ thd->change_item_tree(ref, item);
+ else
+ *ref= item;
+ DBUG_RETURN((Field*) view_ref_found);
}
- DBUG_RETURN(0);
}
- fld= find_field_in_real_table(thd, table_list->table, name, length,
- check_grants_table, allow_rowid,
- cached_field_index_ptr);
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /* check for views with temporary table algorithm */
- if (check_grants_view && table_list->view &&
- fld && fld != WRONG_GRANT &&
- check_grant_column(thd, &table_list->grant,
- table_list->view_db.str,
- table_list->view_name.str,
- name, length))
- {
- DBUG_RETURN(WRONG_GRANT);
- }
-#endif
- DBUG_RETURN(fld);
+ DBUG_RETURN(0);
}
/*
- Find field in table, no side effects, only purpose is to check for field
- in table object and get reference to the field if found.
+ Find field by name in a NATURAL/USING join table reference.
SYNOPSIS
- find_field_in_table_sef()
+ find_field_in_natural_join()
+ thd [in] thread handler
+ table_ref [in] table reference to search
+ name [in] name of field
+ length [in] length of name
+ ref [in/out] if 'name' is resolved to a view field, ref is
+ set to point to the found view field
+ check_grants [in] do check columns grants?
+ register_tree_change [in] TRUE if ref is not stack variable and we
+ need register changes in item tree
+ actual_table [out] the original table reference where the field
+ belongs - differs from 'table_list' only for
+ NATURAL/USING joins
- table table where to find
- name Name of field searched for
+ DESCRIPTION
+ Search for a field among the result fields of a NATURAL/USING join.
+ Notice that this procedure is called only for non-qualified field
+ names. In the case of qualified fields, we search directly the base
+ tables of a natural join.
RETURN
- 0 field is not found
- # pointer to field
+ NULL if the field was not found
+ WRONG_GRANT if no access rights to the found field
+ # Pointer to the found Field
*/
-Field *find_field_in_table_sef(TABLE *table, const char *name)
+static Field *
+find_field_in_natural_join(THD *thd, TABLE_LIST *table_ref, const char *name,
+ uint length, Item **ref, bool check_grants,
+ bool register_tree_change,
+ TABLE_LIST **actual_table)
{
- Field **field_ptr;
- if (table->s->name_hash.records)
- field_ptr= (Field**)hash_search(&table->s->name_hash,(byte*) name,
- strlen(name));
- else
+ List_iterator_fast<Natural_join_column>
+ field_it(*(table_ref->join_columns));
+ Natural_join_column *nj_col;
+ Field *found_field;
+ DBUG_ENTER("find_field_in_natural_join");
+ DBUG_PRINT("enter", ("field name: '%s', ref 0x%lx",
+ name, (ulong) ref));
+ DBUG_ASSERT(table_ref->is_natural_join && table_ref->join_columns);
+ DBUG_ASSERT(*actual_table == NULL);
+
+ LINT_INIT(found_field);
+
+ for (;;)
{
- if (!(field_ptr= table->field))
- return (Field *)0;
- for (; *field_ptr; ++field_ptr)
- if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name))
- break;
+ if (!(nj_col= field_it++))
+ DBUG_RETURN(NULL);
+
+ if (!my_strcasecmp(system_charset_info, nj_col->name(), name))
+ break;
+ }
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ if (check_grants && nj_col->check_grants(thd, name, length))
+ DBUG_RETURN(WRONG_GRANT);
+#endif
+
+ if (nj_col->view_field)
+ {
+ /*
+ The found field is a view field, we do as in find_field_in_view()
+ and return a pointer to pointer to the Item of that field.
+ */
+ Item *item= nj_col->create_item(thd);
+ if (!item)
+ DBUG_RETURN(NULL);
+ DBUG_ASSERT(nj_col->table_field == NULL);
+ if (nj_col->table_ref->schema_table_reformed)
+ {
+ /*
+ Translation table items are always Item_fields and fixed
+ already('mysql_schema_table' function). So we can return
+ ->field. It is used only for 'show & where' commands.
+ */
+ DBUG_RETURN(((Item_field*) (nj_col->view_field->item))->field);
+ }
+ if (register_tree_change)
+ thd->change_item_tree(ref, item);
+ else
+ *ref= item;
+ found_field= (Field*) view_ref_found;
}
- if (field_ptr)
- return *field_ptr;
else
- return (Field *)0;
+ {
+ /* This is a base table. */
+ DBUG_ASSERT(nj_col->view_field == NULL);
+ DBUG_ASSERT(nj_col->table_ref->table == nj_col->table_field->table);
+ found_field= nj_col->table_field;
+ update_field_dependencies(thd, found_field, nj_col->table_ref->table);
+ }
+
+ *actual_table= nj_col->table_ref;
+
+ DBUG_RETURN(found_field);
}
/*
- Find field in table
+ Find field by name in a base table or a view with temp table algorithm.
SYNOPSIS
- find_field_in_real_table()
+ find_field_in_table()
thd thread handler
- table_list table where to find
+ table table where to search for the field
name name of field
length length of name
check_grants do check columns grants?
allow_rowid do allow finding of "_rowid" field?
- cached_field_index_ptr cached position in field list (used to
- speedup prepared tables field finding)
+ cached_field_index_ptr cached position in field list (used to speedup
+ lookup for fields in prepared tables)
RETURN
- 0 field is not found
- # pointer to field
+ 0 field is not found
+ # pointer to field
*/
-Field *find_field_in_real_table(THD *thd, TABLE *table,
- const char *name, uint length,
- bool check_grants, bool allow_rowid,
- uint *cached_field_index_ptr)
+Field *
+find_field_in_table(THD *thd, TABLE *table, const char *name, uint length,
+ bool check_grants, bool allow_rowid,
+ uint *cached_field_index_ptr)
{
Field **field_ptr, *field;
uint cached_field_index= *cached_field_index_ptr;
+ DBUG_ENTER("find_field_in_table");
+ DBUG_PRINT("enter", ("table: '%s', field name: '%s'", table->alias, name));
/* We assume here that table->field < NO_CACHED_FIELD_INDEX = UINT_MAX */
if (cached_field_index < table->s->fields &&
@@ -2662,7 +2743,7 @@ Field *find_field_in_real_table(THD *thd, TABLE *table,
else
{
if (!(field_ptr= table->field))
- return (Field *)0;
+ DBUG_RETURN((Field *)0);
for (; *field_ptr; ++field_ptr)
if (!my_strcasecmp(system_charset_info, (*field_ptr)->field_name, name))
break;
@@ -2678,32 +2759,141 @@ Field *find_field_in_real_table(THD *thd, TABLE *table,
if (!allow_rowid ||
my_strcasecmp(system_charset_info, name, "_rowid") ||
!(field=table->rowid_field))
- return (Field*) 0;
+ DBUG_RETURN((Field*) 0);
}
- if (thd->set_query_id)
- {
- table->file->ha_set_bit_in_rw_set(field->fieldnr,
- (bool)(thd->set_query_id-1));
- if (field->query_id != thd->query_id)
- {
- if (table->get_fields_in_item_tree)
- field->flags|= GET_FIXED_FIELDS_FLAG;
- field->query_id=thd->query_id;
- table->used_fields++;
- table->used_keys.intersect(field->part_of_key);
- }
- else
- thd->dupp_field=field;
- } else if (table->get_fields_in_item_tree)
- field->flags|= GET_FIXED_FIELDS_FLAG;
+ update_field_dependencies(thd, field, table);
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (check_grants && check_grant_column(thd, &table->grant,
table->s->db,
table->s->table_name, name, length))
- return WRONG_GRANT;
+ field= WRONG_GRANT;
#endif
- return field;
+ DBUG_RETURN(field);
+}
+
+
+/*
+ Find field in a table reference.
+
+ SYNOPSIS
+ find_field_in_table_ref()
+ thd [in] thread handler
+ table_list [in] table reference to search
+ name [in] name of field
+ item_name [in] name of item if it will be created (VIEW)
+ table_name [in] optional table name that qualifies the field
+ db_name [in] optional database name that qualifies the
+ length [in] field length of name
+ ref [in/out] if 'name' is resolved to a view field, ref
+ is set to point to the found view field
+ check_grants_table [in] do check columns grants for table?
+ check_grants_view [in] do check columns grants for view?
+ allow_rowid [in] do allow finding of "_rowid" field?
+ cached_field_index_ptr [in] cached position in field list (used to
+ speedup lookup for fields in prepared tables)
+ register_tree_change [in] TRUE if ref is not stack variable and we
+ need register changes in item tree
+ actual_table [out] the original table reference where the field
+ belongs - differs from 'table_list' only for
+ NATURAL_USING joins.
+
+ RETURN
+ 0 field is not found
+ view_ref_found found value in VIEW (real result is in *ref)
+ # pointer to field
+*/
+
+Field *
+find_field_in_table_ref(THD *thd, TABLE_LIST *table_list,
+ const char *name, const char *item_name,
+ const char *table_name, const char *db_name,
+ uint length, Item **ref,
+ bool check_grants_table, bool check_grants_view,
+ bool allow_rowid, uint *cached_field_index_ptr,
+ bool register_tree_change, TABLE_LIST **actual_table)
+{
+ Field *fld;
+ DBUG_ENTER("find_field_in_table_ref");
+ DBUG_PRINT("enter",
+ ("table: '%s' field name: '%s' item name: '%s' ref 0x%lx",
+ table_list->alias, name, item_name, (ulong) ref));
+
+ /*
+ Check that the table and database that qualify the current field name
+ are the same as the table we are going to search for the field.
+ This is done differently for NATURAL/USING joins because there we can't
+ simply compare the qualifying table and database names with the ones of
+ 'table_list' because each field in such a join may originate from a
+ different table.
+ TODO: Ensure that table_name, db_name and tables->db always points to
+ something !
+ */
+ if (!table_list->is_natural_join &&
+ table_name && table_name[0] &&
+ (my_strcasecmp(table_alias_charset, table_list->alias, table_name) ||
+ (db_name && db_name[0] && table_list->db && table_list->db[0] &&
+ strcmp(db_name, table_list->db))))
+ DBUG_RETURN(0);
+
+ *actual_table= NULL;
+ if (table_list->field_translation)
+ {
+ if ((fld= find_field_in_view(thd, table_list, name, item_name, length,
+ ref, check_grants_view,
+ register_tree_change)))
+ *actual_table= table_list;
+ }
+ else if (table_list->is_natural_join)
+ {
+ if (table_name && table_name[0])
+ {
+ /*
+ Qualified field; Search for it in the tables used by the natural join.
+ */
+ List_iterator<TABLE_LIST> it(table_list->nested_join->join_list);
+ TABLE_LIST *table;
+ while ((table= it++))
+ {
+ if ((fld= find_field_in_table_ref(thd, table, name, item_name,
+ table_name, db_name, length, ref,
+ check_grants_table,
+ check_grants_view,
+ allow_rowid, cached_field_index_ptr,
+ register_tree_change, actual_table)))
+ DBUG_RETURN(fld);
+ }
+ DBUG_RETURN(0);
+ }
+ /*
+ Non-qualified field, search directly in the result columns of the
+ natural join.
+ */
+ fld= find_field_in_natural_join(thd, table_list, name, length, ref,
+ /* TIMOUR_TODO: check this with Sanja */
+ check_grants_table || check_grants_view,
+ register_tree_change, actual_table);
+ }
+ else
+ {
+ if ((fld= find_field_in_table(thd, table_list->table, name, length,
+ check_grants_table, allow_rowid,
+ cached_field_index_ptr)))
+ *actual_table= table_list;
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ /* check for views with temporary table algorithm */
+ if (check_grants_view && table_list->view &&
+ fld && fld != WRONG_GRANT &&
+ check_grant_column(thd, &table_list->grant,
+ table_list->view_db.str,
+ table_list->view_name.str,
+ name, length))
+ fld= WRONG_GRANT;
+#endif
+ }
+
+ DBUG_RETURN(fld);
}
@@ -2712,21 +2902,23 @@ Field *find_field_in_real_table(THD *thd, TABLE *table,
SYNOPSIS
find_field_in_tables()
- thd Pointer to current thread structure
- item Field item that should be found
- tables Tables to be searched for item
- ref If 'item' is resolved to a view field, ref is set to
+ thd pointer to current thread structure
+ item field item that should be found
+ first_table list of tables to be searched for item
+ last_table end of the list of tables to search for item. If NULL
+ then search to the end of the list 'first_table'.
+ ref if 'item' is resolved to a view field, ref is set to
point to the found view field
- report_error Degree of error reporting:
+ report_error Degree of error reporting:
- IGNORE_ERRORS then do not report any error
- IGNORE_EXCEPT_NON_UNIQUE report only non-unique
- fields, suppress all other errors
+ fields, suppress all other errors
- REPORT_EXCEPT_NON_UNIQUE report all other errors
except when non-unique fields were found
- REPORT_ALL_ERRORS
check_privileges need to check privileges
- register_tree_change TRUE if ref is not stack variable and we
- need register changes in item tree
+ register_tree_change TRUE if ref is not a stack variable and we
+ to need register changes in item tree
RETURN VALUES
0 If error: the found field is not unique, or there are
@@ -2740,63 +2932,74 @@ Field *find_field_in_real_table(THD *thd, TABLE *table,
*/
Field *
-find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
+find_field_in_tables(THD *thd, Item_ident *item,
+ TABLE_LIST *first_table, TABLE_LIST *last_table,
Item **ref, find_item_error_report_type report_error,
bool check_privileges, bool register_tree_change)
{
Field *found=0;
- const char *db=item->db_name;
- const char *table_name=item->table_name;
- const char *name=item->field_name;
+ const char *db= item->db_name;
+ const char *table_name= item->table_name;
+ const char *name= item->field_name;
uint length=(uint) strlen(name);
char name_buff[NAME_LEN+1];
+ TABLE_LIST *cur_table= first_table;
+ TABLE_LIST *actual_table;
bool allow_rowid;
+ if (!table_name || !table_name[0])
+ {
+ table_name= 0; // For easier test
+ db= 0;
+ }
+
+ allow_rowid= table_name || (cur_table && !cur_table->next_local);
+
if (item->cached_table)
{
/*
- This shortcut is used by prepared statements. We assuming that
- TABLE_LIST *tables is not changed during query execution (which
+ This shortcut is used by prepared statements. We assume that
+ TABLE_LIST *first_table is not changed during query execution (which
is true for all queries except RENAME but luckily RENAME doesn't
use fields...) so we can rely on reusing pointer to its member.
With this optimization we also miss case when addition of one more
field makes some prepared query ambiguous and so erroneous, but we
accept this trade off.
*/
- if (item->cached_table->table && !item->cached_table->view)
- {
- found= find_field_in_real_table(thd, item->cached_table->table,
- name, length,
- test(item->cached_table->
- table->grant.want_privilege) &&
- check_privileges,
- 1, &(item->cached_field_index));
-
- }
+ TABLE_LIST *table_ref= item->cached_table;
+ /*
+ The condition (table_ref->view == NULL) ensures that we will call
+ find_field_in_table even in the case of information schema tables
+ when table_ref->field_translation != NULL.
+ */
+ if (table_ref->table && !table_ref->view)
+ found= find_field_in_table(thd, table_ref->table, name, length,
+ test(table_ref->table->
+ grant.want_privilege) &&
+ check_privileges,
+ 1, &(item->cached_field_index));
else
- {
- TABLE_LIST *table= item->cached_table;
- found= find_field_in_table(thd, table, name, item->name, length,
- ref,
- (table->table &&
- test(table->table->grant.
- want_privilege) &&
- check_privileges),
- (test(table->grant.want_privilege) &&
- check_privileges),
- 1, &(item->cached_field_index),
- register_tree_change);
- }
+ found= find_field_in_table_ref(thd, table_ref, name, item->name,
+ NULL, NULL, length, ref,
+ (table_ref->table &&
+ test(table_ref->table->grant.
+ want_privilege) &&
+ check_privileges),
+ (test(table_ref->grant.want_privilege) &&
+ check_privileges),
+ 1, &(item->cached_field_index),
+ register_tree_change,
+ &actual_table);
if (found)
{
if (found == WRONG_GRANT)
return (Field*) 0;
{
SELECT_LEX *current_sel= thd->lex->current_select;
- SELECT_LEX *last_select= item->cached_table->select_lex;
+ SELECT_LEX *last_select= table_ref->select_lex;
/*
If the field was an outer referencee, mark all selects using this
- sub query as dependent of the outer query
+ sub query as dependent on the outer query
*/
if (current_sel != last_select)
mark_select_range_as_dependent(thd, last_select, current_sel,
@@ -2818,117 +3021,89 @@ find_field_in_tables(THD *thd, Item_ident *item, TABLE_LIST *tables,
db= name_buff;
}
- if (table_name && table_name[0])
- { /* Qualified field */
- bool found_table= 0;
- for (; tables; tables= tables->next_local)
- {
- /* TODO; Ensure that db and tables->db always points to something ! */
- if (!my_strcasecmp(table_alias_charset, tables->alias, table_name) &&
- (!db || !db[0] || !tables->db || !tables->db[0] ||
- !strcmp(db,tables->db)))
- {
- found_table=1;
- Field *find= find_field_in_table(thd, tables, name, item->name,
- length, ref,
- (tables->table &&
- test(tables->table->grant.
- want_privilege) &&
- check_privileges),
- (test(tables->grant.want_privilege) &&
- check_privileges),
- 1, &(item->cached_field_index),
- register_tree_change);
- if (find)
- {
- item->cached_table= tables;
- if (!tables->cacheable_table)
- item->cached_table= 0;
- if (find == WRONG_GRANT)
- return (Field*) 0;
- if (db || !thd->where)
- return find;
- if (found)
- {
- if (report_error == REPORT_ALL_ERRORS ||
- report_error == IGNORE_EXCEPT_NON_UNIQUE)
- my_error(ER_NON_UNIQ_ERROR, MYF(0),
- item->full_name(),thd->where);
- return (Field*) 0;
- }
- found=find;
- }
- }
- }
- if (found)
- return found;
- if (!found_table && (report_error == REPORT_ALL_ERRORS ||
- report_error == REPORT_EXCEPT_NON_UNIQUE))
- {
- char buff[NAME_LEN*2+1];
- if (db && db[0])
- {
- strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS);
- table_name=buff;
- }
- my_error(ER_UNKNOWN_TABLE, MYF(0), table_name, thd->where);
- }
- else
- if (report_error == REPORT_ALL_ERRORS ||
- report_error == REPORT_EXCEPT_NON_UNIQUE)
- my_error(ER_BAD_FIELD_ERROR, MYF(0), item->full_name(),thd->where);
- else
- return (Field*) not_found_field;
- return (Field*) 0;
- }
- allow_rowid= tables && !tables->next_local; // Only one table
- for (; tables ; tables= tables->next_local)
- {
- Field *field;
- if (!tables->table && !tables->ancestor)
- {
- if (report_error == REPORT_ALL_ERRORS ||
- report_error == REPORT_EXCEPT_NON_UNIQUE)
- my_error(ER_BAD_FIELD_ERROR, MYF(0), item->full_name(),thd->where);
- return (Field*) not_found_field;
- }
+ if (last_table)
+ last_table= last_table->next_name_resolution_table;
- field= find_field_in_table(thd, tables, name, item->name,
- length, ref,
- (tables->table &&
- test(tables->table->grant.
- want_privilege) &&
- check_privileges),
- (test(tables->grant.want_privilege) &&
- check_privileges),
- allow_rowid,
- &(item->cached_field_index),
- register_tree_change);
- if (field)
+ for (; cur_table != last_table ;
+ cur_table= cur_table->next_name_resolution_table)
+ {
+ Field *cur_field= find_field_in_table_ref(thd, cur_table, name, item->name,
+ table_name, db,
+ length, ref,
+ (cur_table->table &&
+ test(cur_table->table->grant.
+ want_privilege) &&
+ check_privileges),
+ (test(cur_table->grant.
+ want_privilege)
+ && check_privileges),
+ allow_rowid,
+ &(item->cached_field_index),
+ register_tree_change,
+ &actual_table);
+ if (cur_field)
{
- if (field == WRONG_GRANT)
+ if (cur_field == WRONG_GRANT)
return (Field*) 0;
- item->cached_table= (!tables->cacheable_table || found) ? 0 : tables;
+
+ /*
+ Store the original table of the field, which may be different from
+ cur_table in the case of NATURAL/USING join.
+ */
+ item->cached_table= (!actual_table->cacheable_table || found) ?
+ 0 : actual_table;
+
+ DBUG_ASSERT(thd->where);
+ /*
+ If we found a fully qualified field we return it directly as it can't
+ have duplicates.
+ */
+ if (db)
+ return cur_field;
+
if (found)
{
- if (!thd->where) // Returns first found
- break;
if (report_error == REPORT_ALL_ERRORS ||
report_error == IGNORE_EXCEPT_NON_UNIQUE)
- my_error(ER_NON_UNIQ_ERROR, MYF(0), name, thd->where);
+ my_error(ER_NON_UNIQ_ERROR, MYF(0),
+ table_name ? item->full_name() : name, thd->where);
return (Field*) 0;
}
- found= field;
+ found= cur_field;
}
}
+
if (found)
return found;
- if (report_error == REPORT_ALL_ERRORS ||
- report_error == REPORT_EXCEPT_NON_UNIQUE)
- my_error(ER_BAD_FIELD_ERROR, MYF(0), item->full_name(), thd->where);
+
+ /*
+ If the field was qualified and there were no tables to search, issue
+ an error that an unknown table was given. The situation is detected
+ as follows: if there were no tables we wouldn't go through the loop
+ and cur_table wouldn't be updated by the loop increment part, so it
+ will be equal to the first table.
+ */
+ if (table_name && (cur_table == first_table) &&
+ (report_error == REPORT_ALL_ERRORS ||
+ report_error == REPORT_EXCEPT_NON_UNIQUE))
+ {
+ char buff[NAME_LEN*2+1];
+ if (db && db[0])
+ {
+ strxnmov(buff,sizeof(buff)-1,db,".",table_name,NullS);
+ table_name=buff;
+ }
+ my_error(ER_UNKNOWN_TABLE, MYF(0), table_name, thd->where);
+ }
else
- return (Field*) not_found_field;
- return (Field*) 0;
+ {
+ if (report_error == REPORT_ALL_ERRORS ||
+ report_error == REPORT_EXCEPT_NON_UNIQUE)
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), item->full_name(), thd->where);
+ else
+ found= not_found_field;
+ }
+ return found;
}
@@ -3132,6 +3307,664 @@ find_item_in_list(Item *find, List<Item> &items, uint *counter,
return (Item **) not_found_item;
}
+
+/*
+ Test if a string is a member of a list of strings.
+
+ SYNOPSIS
+ test_if_string_in_list()
+ find the string to look for
+ str_list a list of strings to be searched
+
+ DESCRIPTION
+ Sequentially search a list of strings for a string, and test whether
+ the list contains the same string.
+
+ RETURN
+ TRUE if find is in str_list
+ FALSE otherwise
+*/
+
+static bool
+test_if_string_in_list(const char *find, List<String> *str_list)
+{
+ List_iterator<String> str_list_it(*str_list);
+ String *curr_str;
+ size_t find_length= strlen(find);
+ while ((curr_str= str_list_it++))
+ {
+ if (find_length != curr_str->length())
+ continue;
+ if (!strncmp(find, curr_str->ptr(), find_length))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/*
+ Create a new name resolution context for an item so that it is
+ being resolved in a specific table reference.
+
+ SYNOPSIS
+ set_new_item_local_context()
+ thd pointer to current thread
+ item item for which new context is created and set
+ table_ref table ref where an item showld be resolved
+
+ DESCRIPTION
+ Create a new name resolution context for an item, so that the item
+ is resolved only the supplied 'table_ref'.
+
+ RETURN
+ FALSE if all OK
+ TRUE otherwise
+*/
+
+static bool
+set_new_item_local_context(THD *thd, Item_ident *item, TABLE_LIST *table_ref)
+{
+ Name_resolution_context *context;
+ if (!(context= new (thd->mem_root) Name_resolution_context))
+ return TRUE;
+ context->init();
+ context->first_name_resolution_table=
+ context->last_name_resolution_table= table_ref;
+ item->context= context;
+ return FALSE;
+}
+
+
+/*
+ Find and mark the common columns of two table references.
+
+ SYNOPSIS
+ mark_common_columns()
+ thd [in] current thread
+ table_ref_1 [in] the first (left) join operand
+ table_ref_2 [in] the second (right) join operand
+ using_fields [in] if the join is JOIN...USING - the join columns,
+ if NATURAL join, then NULL
+ found_using_fields [out] number of fields from the USING clause that were
+ found among the common fields
+
+ DESCRIPTION
+ The procedure finds the common columns of two relations (either
+ tables or intermediate join results), and adds an equi-join condition
+ to the ON clause of 'table_ref_2' for each pair of matching columns.
+ If some of table_ref_XXX represents a base table or view, then we
+ create new 'Natural_join_column' instances for each column
+ reference and store them in the 'join_columns' of the table
+ reference.
+
+ IMPLEMENTATION
+ The procedure assumes that store_natural_using_join_columns() was
+ called for the previous level of NATURAL/USING joins.
+
+ RETURN
+ TRUE error when some common column is non-unique, or out of memory
+ FALSE OK
+*/
+
+static bool
+mark_common_columns(THD *thd, TABLE_LIST *table_ref_1, TABLE_LIST *table_ref_2,
+ List<String> *using_fields, uint *found_using_fields)
+{
+ Field_iterator_table_ref it_1, it_2;
+ Natural_join_column *nj_col_1, *nj_col_2;
+ const char *field_name_1;
+ Query_arena *arena, backup;
+ bool add_columns= TRUE;
+ bool result= TRUE;
+
+ DBUG_ENTER("mark_common_columns");
+ DBUG_PRINT("info", ("operand_1: %s operand_2: %s",
+ table_ref_1->alias, table_ref_2->alias));
+
+ *found_using_fields= 0;
+ arena= thd->change_arena_if_needed(&backup);
+
+ /*
+ TABLE_LIST::join_columns could be allocated by the previous call to
+ store_natural_using_join_columns() for the lower level of nested tables.
+ */
+ if (!table_ref_1->join_columns)
+ {
+ if (!(table_ref_1->join_columns= new List<Natural_join_column>))
+ goto err;
+ table_ref_1->is_join_columns_complete= FALSE;
+ }
+ if (!table_ref_2->join_columns)
+ {
+ if (!(table_ref_2->join_columns= new List<Natural_join_column>))
+ goto err;
+ table_ref_2->is_join_columns_complete= FALSE;
+ }
+
+ for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next())
+ {
+ bool is_created_1;
+ bool found= FALSE;
+ if (!(nj_col_1= it_1.get_or_create_column_ref(thd, &is_created_1)))
+ goto err;
+ field_name_1= nj_col_1->name();
+
+ /* If nj_col_1 was just created add it to the list of join columns. */
+ if (is_created_1)
+ table_ref_1->join_columns->push_back(nj_col_1);
+
+ /*
+ Find a field with the same name in table_ref_2.
+
+ Note that for the second loop, it_2.set() will iterate over
+ table_ref_2->join_columns and not generate any new elements or
+ lists.
+ */
+ nj_col_2= NULL;
+ for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next())
+ {
+ bool is_created_2;
+ Natural_join_column *cur_nj_col_2;
+ const char *cur_field_name_2;
+ if (!(cur_nj_col_2= it_2.get_or_create_column_ref(thd, &is_created_2)))
+ goto err;
+ cur_field_name_2= cur_nj_col_2->name();
+
+ /* If nj_col_1 was just created add it to the list of join columns. */
+ if (add_columns && is_created_2)
+ table_ref_2->join_columns->push_back(cur_nj_col_2);
+
+ /* Compare the two columns and check for duplicate common fields. */
+ if (!my_strcasecmp(system_charset_info, field_name_1, cur_field_name_2))
+ {
+ if (found)
+ {
+ my_error(ER_NON_UNIQ_ERROR, MYF(0), field_name_1, thd->where);
+ goto err;
+ }
+ nj_col_2= cur_nj_col_2;
+ found= TRUE;
+ }
+ }
+ /* Force it_2.set() to use table_ref_2->join_columns. */
+ table_ref_2->is_join_columns_complete= TRUE;
+ add_columns= FALSE;
+ if (!found)
+ continue; // No matching field
+
+ /*
+ field_1 and field_2 have the same names. Check if they are in the USING
+ clause (if present), mark them as common fields, and add a new
+ equi-join condition to the ON clause.
+ */
+ if (nj_col_2 &&
+ (!using_fields ||
+ test_if_string_in_list(field_name_1, using_fields)))
+ {
+ Item *item_1= nj_col_1->create_item(thd);
+ Item *item_2= nj_col_2->create_item(thd);
+ Field *field_1= nj_col_1->field();
+ Field *field_2= nj_col_2->field();
+ Item_ident *item_ident_1, *item_ident_2;
+ Item_func_eq *eq_cond;
+
+ if (!item_1 || !item_2)
+ goto err; // out of memory
+
+ /*
+ The following assert checks that the two created items are of
+ type Item_ident.
+ */
+ DBUG_ASSERT(!thd->lex->current_select->no_wrap_view_item);
+ /*
+ In the case of no_wrap_view_item == 0, the created items must be
+ of sub-classes of Item_ident.
+ */
+ DBUG_ASSERT(item_1->type() == Item::FIELD_ITEM ||
+ item_1->type() == Item::REF_ITEM);
+ DBUG_ASSERT(item_2->type() == Item::FIELD_ITEM ||
+ item_2->type() == Item::REF_ITEM);
+
+ /*
+ We need to cast item_1,2 to Item_ident, because we need to hook name
+ resolution contexts specific to each item.
+ */
+ item_ident_1= (Item_ident*) item_1;
+ item_ident_2= (Item_ident*) item_2;
+ /*
+ Create and hook special name resolution contexts to each item in the
+ new join condition . We need this to both speed-up subsequent name
+ resolution of these items, and to enable proper name resolution of
+ the items during the execute phase of PS.
+ */
+ if (set_new_item_local_context(thd, item_ident_1, nj_col_1->table_ref) ||
+ set_new_item_local_context(thd, item_ident_2, nj_col_2->table_ref))
+ goto err;
+
+ if (!(eq_cond= new Item_func_eq(item_ident_1, item_ident_2)))
+ goto err; /* Out of memory. */
+
+ /*
+ Add the new equi-join condition to the ON clause. Notice that
+ fix_fields() is applied to all ON conditions in setup_conds()
+ so we don't do it here.
+ */
+ add_join_on((table_ref_1->outer_join & JOIN_TYPE_RIGHT ?
+ table_ref_1 : table_ref_2),
+ eq_cond);
+
+ nj_col_1->is_common= nj_col_2->is_common= TRUE;
+
+ if (field_1)
+ {
+ /* Mark field_1 used for table cache. */
+ field_1->query_id= thd->query_id;
+ nj_col_1->table_ref->table->used_keys.intersect(field_1->part_of_key);
+ }
+ if (field_2)
+ {
+ /* Mark field_2 used for table cache. */
+ field_2->query_id= thd->query_id;
+ nj_col_2->table_ref->table->used_keys.intersect(field_2->part_of_key);
+ }
+
+ if (using_fields != NULL)
+ ++(*found_using_fields);
+ }
+ }
+ table_ref_1->is_join_columns_complete= TRUE;
+
+ /*
+ Everything is OK.
+ Notice that at this point there may be some column names in the USING
+ clause that are not among the common columns. This is an SQL error and
+ we check for this error in store_natural_using_join_columns() when
+ (found_using_fields < length(join_using_fields)).
+ */
+ result= FALSE;
+
+err:
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ DBUG_RETURN(result);
+}
+
+
+
+/*
+ Materialize and store the row type of NATURAL/USING join.
+
+ SYNOPSIS
+ store_natural_using_join_columns()
+ thd current thread
+ natural_using_join the table reference of the NATURAL/USING join
+ table_ref_1 the first (left) operand (of a NATURAL/USING join).
+ table_ref_2 the second (right) operand (of a NATURAL/USING join).
+ using_fields if the join is JOIN...USING - the join columns,
+ if NATURAL join, then NULL
+ found_using_fields number of fields from the USING clause that were
+ found among the common fields
+
+ DESCRIPTION
+ Iterate over the columns of both join operands and sort and store
+ all columns into the 'join_columns' list of natural_using_join
+ where the list is formed by three parts:
+ part1: The coalesced columns of table_ref_1 and table_ref_2,
+ sorted according to the column order of the first table.
+ part2: The other columns of the first table, in the order in
+ which they were defined in CREATE TABLE.
+ part3: The other columns of the second table, in the order in
+ which they were defined in CREATE TABLE.
+ Time complexity - O(N1+N2), where Ni = length(table_ref_i).
+
+ IMPLEMENTATION
+ The procedure assumes that mark_common_columns() has been called
+ for the join that is being processed.
+
+ RETURN
+ TRUE error: Some common column is ambiguous
+ FALSE OK
+*/
+
+static bool
+store_natural_using_join_columns(THD *thd, TABLE_LIST *natural_using_join,
+ TABLE_LIST *table_ref_1,
+ TABLE_LIST *table_ref_2,
+ List<String> *using_fields,
+ uint found_using_fields)
+{
+ Field_iterator_table_ref it_1, it_2;
+ Natural_join_column *nj_col_1, *nj_col_2;
+ bool is_created;
+ Query_arena *arena, backup;
+ bool result= TRUE;
+ List<Natural_join_column> *non_join_columns;
+ DBUG_ENTER("store_natural_using_join_columns");
+
+ DBUG_ASSERT(!natural_using_join->join_columns);
+
+ arena= thd->change_arena_if_needed(&backup);
+
+ if (!(non_join_columns= new List<Natural_join_column>) ||
+ !(natural_using_join->join_columns= new List<Natural_join_column>))
+ goto err;
+
+ /* Append the columns of the first join operand. */
+ for (it_1.set(table_ref_1); !it_1.end_of_fields(); it_1.next())
+ {
+ if (!(nj_col_1= it_1.get_or_create_column_ref(thd, &is_created)))
+ goto err;
+ /*
+ The following assert checks that mark_common_columns() was run and
+ we created the list table_ref_1->join_columns.
+ */
+ DBUG_ASSERT(!is_created);
+ if (nj_col_1->is_common)
+ {
+ natural_using_join->join_columns->push_back(nj_col_1);
+ /* Reset the common columns for the next call to mark_common_columns. */
+ nj_col_1->is_common= FALSE;
+ }
+ else
+ non_join_columns->push_back(nj_col_1);
+ }
+
+ /*
+ Check that all columns in the USING clause are among the common
+ columns. If this is not the case, report the first one that was
+ not found in an error.
+ */
+ if (using_fields && found_using_fields < using_fields->elements)
+ {
+ String *using_field_name;
+ List_iterator_fast<String> using_fields_it(*using_fields);
+ while ((using_field_name= using_fields_it++))
+ {
+ const char *using_field_name_ptr= using_field_name->c_ptr();
+ List_iterator_fast<Natural_join_column>
+ it(*(natural_using_join->join_columns));
+ Natural_join_column *common_field;
+
+ for (;;)
+ {
+ /* If reached the end of fields, and none was found, report error. */
+ if (!(common_field= it++))
+ {
+ my_error(ER_BAD_FIELD_ERROR, MYF(0), using_field_name_ptr,
+ current_thd->where);
+ goto err;
+ }
+ if (!my_strcasecmp(system_charset_info,
+ common_field->name(), using_field_name_ptr))
+ break; // Found match
+ }
+ }
+ }
+
+ /* Append the non-equi-join columns of the second join operand. */
+ for (it_2.set(table_ref_2); !it_2.end_of_fields(); it_2.next())
+ {
+ if (!(nj_col_2= it_2.get_or_create_column_ref(thd, &is_created)))
+ goto err;
+ /*
+ The following assert checks that mark_common_columns() was run and
+ we created the list table_ref_2->join_columns.
+ */
+ DBUG_ASSERT(!is_created);
+ if (!nj_col_2->is_common)
+ non_join_columns->push_back(nj_col_2);
+ else
+ {
+ /* Reset the common columns for the next call to mark_common_columns. */
+ nj_col_2->is_common= FALSE;
+ }
+ }
+
+ if (non_join_columns->elements > 0)
+ natural_using_join->join_columns->concat(non_join_columns);
+ natural_using_join->is_join_columns_complete= TRUE;
+
+ result= FALSE;
+
+err:
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Precompute and store the row types of the top-most NATURAL/USING joins.
+
+ SYNOPSIS
+ store_top_level_join_columns()
+ thd current thread
+ table_ref nested join or table in a FROM clause
+ left_neighbor neighbor table reference to the left of table_ref at the
+ same level in the join tree
+ right_neighbor neighbor table reference to the right of table_ref at the
+ same level in the join tree
+
+ DESCRIPTION
+ The procedure performs a post-order traversal of a nested join tree
+ and materializes the row types of NATURAL/USING joins in a
+ bottom-up manner until it reaches the TABLE_LIST elements that
+ represent the top-most NATURAL/USING joins. The procedure should be
+ applied to each element of SELECT_LEX::top_join_list (i.e. to each
+ top-level element of the FROM clause).
+
+ IMPLEMENTATION
+ Notice that the table references in the list nested_join->join_list
+ are in reverse order, thus when we iterate over it, we are moving
+ from the right to the left in the FROM clause.
+
+ RETURN
+ TRUE Error
+ FALSE OK
+*/
+
+static bool
+store_top_level_join_columns(THD *thd, TABLE_LIST *table_ref,
+ TABLE_LIST *left_neighbor,
+ TABLE_LIST *right_neighbor)
+{
+ Query_arena *arena, backup;
+ bool result= TRUE;
+
+ DBUG_ENTER("store_top_level_join_columns");
+
+ arena= thd->change_arena_if_needed(&backup);
+
+ /* Call the procedure recursively for each nested table reference. */
+ if (table_ref->nested_join)
+ {
+ List_iterator_fast<TABLE_LIST> nested_it(table_ref->nested_join->join_list);
+ TABLE_LIST *cur_left_neighbor= nested_it++;
+ TABLE_LIST *cur_right_neighbor= NULL;
+
+ while (cur_left_neighbor)
+ {
+ TABLE_LIST *cur_table_ref= cur_left_neighbor;
+ cur_left_neighbor= nested_it++;
+ /*
+ The order of RIGHT JOIN operands is reversed in 'join list' to
+ transform it into a LEFT JOIN. However, in this procedure we need
+ the join operands in their lexical order, so below we reverse the
+ join operands. Notice that this happens only in the first loop, and
+ not in the second one, as in the second loop cur_left_neighbor == NULL.
+ This is the correct behavior, because the second loop
+ sets cur_table_ref reference correctly after the join operands are
+ swapped in the first loop.
+ */
+ if (cur_left_neighbor &&
+ cur_table_ref->outer_join & JOIN_TYPE_RIGHT)
+ {
+ /* This can happen only for JOIN ... ON. */
+ DBUG_ASSERT(table_ref->nested_join->join_list.elements == 2);
+ swap_variables(TABLE_LIST*, cur_left_neighbor, cur_table_ref);
+ }
+
+ if (cur_table_ref->nested_join &&
+ store_top_level_join_columns(thd, cur_table_ref,
+ cur_left_neighbor, cur_right_neighbor))
+ goto err;
+ cur_right_neighbor= cur_table_ref;
+ }
+ }
+
+ /*
+ If this is a NATURAL/USING join, materialize its result columns and
+ convert to a JOIN ... ON.
+ */
+ if (table_ref->is_natural_join)
+ {
+ DBUG_ASSERT(table_ref->nested_join &&
+ table_ref->nested_join->join_list.elements == 2);
+ List_iterator_fast<TABLE_LIST> operand_it(table_ref->nested_join->join_list);
+ /*
+ Notice that the order of join operands depends on whether table_ref
+ represents a LEFT or a RIGHT join. In a RIGHT join, the operands are
+ in inverted order.
+ */
+ TABLE_LIST *table_ref_2= operand_it++; /* Second NATURAL join operand.*/
+ TABLE_LIST *table_ref_1= operand_it++; /* First NATURAL join operand. */
+ List<String> *using_fields= table_ref->join_using_fields;
+ uint found_using_fields;
+
+ /*
+ The two join operands were interchanged in the parser, change the order
+ back for 'mark_common_columns'.
+ */
+ if (table_ref_2->outer_join & JOIN_TYPE_RIGHT)
+ swap_variables(TABLE_LIST*, table_ref_1, table_ref_2);
+ if (mark_common_columns(thd, table_ref_1, table_ref_2,
+ using_fields, &found_using_fields))
+ goto err;
+
+ /*
+ Swap the join operands back, so that we pick the columns of the second
+ one as the coalesced columns. In this way the coalesced columns are the
+ same as of an equivalent LEFT JOIN.
+ */
+ if (table_ref_1->outer_join & JOIN_TYPE_RIGHT)
+ swap_variables(TABLE_LIST*, table_ref_1, table_ref_2);
+ if (store_natural_using_join_columns(thd, table_ref, table_ref_1,
+ table_ref_2, using_fields,
+ found_using_fields))
+ goto err;
+
+ /*
+ Change NATURAL JOIN to JOIN ... ON. We do this for both operands
+ because either one of them or the other is the one with the
+ natural join flag because RIGHT joins are transformed into LEFT,
+ and the two tables may be reordered.
+ */
+ table_ref_1->natural_join= table_ref_2->natural_join= NULL;
+
+ /* Add a TRUE condition to outer joins that have no common columns. */
+ if (table_ref_2->outer_join &&
+ !table_ref_1->on_expr && !table_ref_2->on_expr)
+ table_ref_2->on_expr= new Item_int((longlong) 1,1); /* Always true. */
+
+ /* Change this table reference to become a leaf for name resolution. */
+ if (left_neighbor)
+ {
+ TABLE_LIST *last_leaf_on_the_left;
+ last_leaf_on_the_left= left_neighbor->last_leaf_for_name_resolution();
+ last_leaf_on_the_left->next_name_resolution_table= table_ref;
+ }
+ if (right_neighbor)
+ {
+ TABLE_LIST *first_leaf_on_the_right;
+ first_leaf_on_the_right= right_neighbor->first_leaf_for_name_resolution();
+ table_ref->next_name_resolution_table= first_leaf_on_the_right;
+ }
+ else
+ table_ref->next_name_resolution_table= NULL;
+ }
+ result= FALSE; /* All is OK. */
+
+err:
+ if (arena)
+ thd->restore_backup_item_arena(arena, &backup);
+ DBUG_RETURN(result);
+}
+
+
+/*
+ Compute and store the row types of the top-most NATURAL/USING joins
+ in a FROM clause.
+
+ SYNOPSIS
+ setup_natural_join_row_types()
+ thd current thread
+ from_clause list of top-level table references in a FROM clause
+
+ DESCRIPTION
+ Apply the procedure 'store_top_level_join_columns' to each of the
+ top-level table referencs of the FROM clause. Adjust the list of tables
+ for name resolution - context->first_name_resolution_table to the
+ top-most, lef-most NATURAL/USING join.
+
+ IMPLEMENTATION
+ Notice that the table references in 'from_clause' are in reverse
+ order, thus when we iterate over it, we are moving from the right
+ to the left in the FROM clause.
+
+ RETURN
+ TRUE Error
+ FALSE OK
+*/
+static bool setup_natural_join_row_types(THD *thd,
+ List<TABLE_LIST> *from_clause,
+ Name_resolution_context *context)
+{
+ thd->where= "from clause";
+ if (from_clause->elements == 0)
+ return FALSE; /* We come here in the case of UNIONs. */
+
+ /* For stored procedures do not redo work if already done. */
+ if (!context->select_lex->first_execution)
+ return FALSE;
+
+ List_iterator_fast<TABLE_LIST> table_ref_it(*from_clause);
+ TABLE_LIST *table_ref; /* Current table reference. */
+ /* Table reference to the left of the current. */
+ TABLE_LIST *left_neighbor;
+ /* Table reference to the right of the current. */
+ TABLE_LIST *right_neighbor= NULL;
+
+ /* Note that tables in the list are in reversed order */
+ for (left_neighbor= table_ref_it++; left_neighbor ; )
+ {
+ table_ref= left_neighbor;
+ left_neighbor= table_ref_it++;
+ if (store_top_level_join_columns(thd, table_ref,
+ left_neighbor, right_neighbor))
+ return TRUE;
+ if (left_neighbor)
+ {
+ TABLE_LIST *first_leaf_on_the_right;
+ first_leaf_on_the_right= table_ref->first_leaf_for_name_resolution();
+ left_neighbor->next_name_resolution_table= first_leaf_on_the_right;
+ }
+ right_neighbor= table_ref;
+ }
+
+ /*
+ Store the top-most, left-most NATURAL/USING join, so that we start
+ the search from that one instead of context->table_list. At this point
+ right_neighbor points to the left-most top-level table reference in the
+ FROM clause.
+ */
+ DBUG_ASSERT(right_neighbor);
+ context->first_name_resolution_table=
+ right_neighbor->first_leaf_for_name_resolution();
+
+ return FALSE;
+}
+
+
/****************************************************************************
** Expand all '*' in given fields
****************************************************************************/
@@ -3212,10 +4045,11 @@ int setup_wild(THD *thd, TABLE_LIST *tables, List<Item> &fields,
****************************************************************************/
bool setup_fields(THD *thd, Item **ref_pointer_array,
- List<Item> &fields, ulong set_query_id,
+ List<Item> &fields, bool set_query_id,
List<Item> *sum_func_list, bool allow_sum_func)
{
reg2 Item *item;
+ bool save_set_query_id= thd->set_query_id;
List_iterator<Item> it(fields);
DBUG_ENTER("setup_fields");
@@ -3243,6 +4077,7 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
if (!item->fixed && item->fix_fields(thd, it.ref()) ||
(item= *(it.ref()))->check_cols(1))
{
+ thd->set_query_id= save_set_query_id;
DBUG_RETURN(TRUE); /* purecov: inspected */
}
if (ref)
@@ -3250,8 +4085,9 @@ bool setup_fields(THD *thd, Item **ref_pointer_array,
if (item->with_sum_func && item->type() != Item::SUM_FUNC_ITEM &&
sum_func_list)
item->split_sum_func(thd, ref_pointer_array, *sum_func_list);
- thd->used_tables|=item->used_tables();
+ thd->used_tables|= item->used_tables();
}
+ thd->set_query_id= save_set_query_id;
DBUG_RETURN(test(thd->net.report_error));
}
@@ -3272,9 +4108,7 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables)
for (TABLE_LIST *table= tables; table; table= table->next_local)
{
if (table->view && table->effective_algorithm == VIEW_ALGORITHM_MERGE)
- {
list= make_leaves_list(list, table->ancestor);
- }
else
{
*list= table;
@@ -3291,33 +4125,36 @@ TABLE_LIST **make_leaves_list(TABLE_LIST **list, TABLE_LIST *tables)
setup_tables()
thd Thread handler
context name resolution contest to setup table list there
- tables Table list
+ from_clause Top-level list of table references in the FROM clause
+ tables Table list (select_lex->table_list)
conds Condition of current SELECT (can be changed by VIEW)
- leaves List of join table leaves list
+ leaves List of join table leaves list (select_lex->leaf_tables)
refresh It is onle refresh for subquery
select_insert It is SELECT ... INSERT command
NOTE
Check also that the 'used keys' and 'ignored keys' exists and set up the
- table structure accordingly
- Create leaf tables list
+ table structure accordingly.
+ Create a list of leaf tables. For queries with NATURAL/USING JOINs,
+ compute the row types of the top most natural/using join table references
+ and link these into a list of table references for name resolution.
This has to be called for all tables that are used by items, as otherwise
table->map is not set and all Item_field will be regarded as const items.
RETURN
- FALSE ok; In this case *map will includes the choosed index
+ FALSE ok; In this case *map will includes the chosen index
TRUE error
*/
bool setup_tables(THD *thd, Name_resolution_context *context,
- TABLE_LIST *tables, Item **conds,
- TABLE_LIST **leaves, bool select_insert)
+ List<TABLE_LIST> *from_clause, TABLE_LIST *tables,
+ Item **conds, TABLE_LIST **leaves, bool select_insert)
{
uint tablenr= 0;
DBUG_ENTER("setup_tables");
- context->table_list= tables;
+ context->table_list= context->first_name_resolution_table= tables;
/*
this is used for INSERT ... SELECT.
@@ -3389,6 +4226,11 @@ bool setup_tables(THD *thd, Name_resolution_context *context,
DBUG_RETURN(1);
}
}
+
+ /* Precompute and store the row types of NATURAL/USING joins. */
+ if (setup_natural_join_row_types(thd, from_clause, context))
+ DBUG_RETURN(1);
+
DBUG_RETURN(0);
}
@@ -3447,8 +4289,7 @@ bool get_key_map_from_key_list(key_map *map, TABLE *table,
for all columns
1 If any privilege is ok
RETURN
- 0 ok
- 'it' is updated to point at last inserted
+ 0 ok 'it' is updated to point at last inserted
1 error. Error message is generated but not sent to client
*/
@@ -3457,12 +4298,11 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
const char *table_name, List_iterator<Item> *it,
bool any_privileges)
{
- /* allocate variables on stack to avoid pool alloaction */
- Field_iterator_table table_iter;
- Field_iterator_view view_iter;
- uint found;
+ Field_iterator_table_ref field_iterator;
+ bool found;
char name_buff[NAME_LEN+1];
DBUG_ENTER("insert_fields");
+ DBUG_PRINT("arena", ("current arena: 0x%lx", (ulong)thd->current_arena));
if (db_name && lower_case_table_names)
{
@@ -3476,200 +4316,192 @@ insert_fields(THD *thd, Name_resolution_context *context, const char *db_name,
db_name= name_buff;
}
- found= 0;
- for (TABLE_LIST *tables= context->table_list;
+ found= FALSE;
+
+ /*
+ If table names are qualified, then loop over all tables used in the query,
+ else treat natural joins as leaves and do not iterate over their underlying
+ tables.
+ */
+ for (TABLE_LIST *tables= (table_name ? context->table_list :
+ context->first_name_resolution_table);
tables;
- tables= tables->next_local)
+ tables= (table_name ? tables->next_local :
+ tables->next_name_resolution_table)
+ )
{
- Field_iterator *iterator;
- TABLE_LIST *natural_join_table;
Field *field;
- TABLE_LIST *embedded;
- TABLE_LIST *last;
- TABLE_LIST *embedding;
TABLE *table= tables->table;
- if (!table_name || (!my_strcasecmp(table_alias_charset, table_name,
- tables->alias) &&
- (!db_name || !strcmp(tables->db,db_name))))
- {
- bool view;
+ DBUG_ASSERT(tables->is_leaf_for_name_resolution());
+
+ if (table_name && my_strcasecmp(table_alias_charset, table_name,
+ tables->alias) ||
+ (db_name && strcmp(tables->db,db_name)))
+ continue;
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- /* Ensure that we have access right to all columns */
- if (!((table && (table->grant.privilege & SELECT_ACL) ||
- tables->view && (tables->grant.privilege & SELECT_ACL))) &&
- !any_privileges)
- {
- if (tables->view)
- {
- view_iter.set(tables);
- if (check_grant_all_columns(thd, SELECT_ACL, &tables->grant,
- tables->view_db.str,
- tables->view_name.str,
- &view_iter))
- goto err;
- }
- else if (!tables->schema_table)
- {
- DBUG_ASSERT(table != 0);
- table_iter.set(tables);
- if (check_grant_all_columns(thd, SELECT_ACL, &table->grant,
- table->s->db,
- table->s->table_name,
- &table_iter))
- goto err;
- }
- }
+ /* Ensure that we have access rights to all fields to be inserted. */
+ if (!((table && (table->grant.privilege & SELECT_ACL) ||
+ tables->view && (tables->grant.privilege & SELECT_ACL))) &&
+ !any_privileges)
+ {
+ field_iterator.set(tables);
+ if (check_grant_all_columns(thd, SELECT_ACL, field_iterator.grant(),
+ field_iterator.db_name(),
+ field_iterator.table_name(),
+ &field_iterator))
+ DBUG_RETURN(TRUE);
+ }
#endif
- natural_join_table= 0;
- last= embedded= tables;
- while ((embedding= embedded->embedding) &&
- embedding->join_list->elements != 1)
- {
- TABLE_LIST *next;
- List_iterator_fast<TABLE_LIST> it(embedding->nested_join->join_list);
- last= it++;
- while ((next= it++))
- last= next;
- if (last != tables)
- break;
- embedded= embedding;
- }
- if (tables == last &&
- !embedded->outer_join &&
- embedded->natural_join &&
- !embedded->natural_join->outer_join)
- {
- embedding= embedded->natural_join;
- while (embedding->nested_join)
- embedding= embedding->nested_join->join_list.head();
- natural_join_table= embedding;
- }
- if (tables->field_translation)
+ /*
+ Update the tables used in the query based on the referenced fields. For
+ views and natural joins this update is performed inside the loop below.
+ */
+ if (table)
+ thd->used_tables|= table->map;
+
+ /*
+ Initialize a generic field iterator for the current table reference.
+ Notice that it is guaranteed that this iterator will iterate over the
+ fields of a single table reference, because 'tables' is a leaf (for
+ name resolution purposes).
+ */
+ field_iterator.set(tables);
+
+ for (; !field_iterator.end_of_fields(); field_iterator.next())
+ {
+ Item *item;
+
+ if (!(item= field_iterator.create_item(thd)))
+ DBUG_RETURN(TRUE);
+
+ if (!found)
{
- iterator= &view_iter;
- view= 1;
+ found= TRUE;
+ it->replace(item); /* Replace '*' with the first found item. */
}
else
- {
- iterator= &table_iter;
- view= 0;
- }
- iterator->set(tables);
-
- /* for view used tables will be collected in following loop */
- if (table)
- thd->used_tables|= table->map;
+ it->after(item); /* Add 'item' to the SELECT list. */
- for (; !iterator->end_of_fields(); iterator->next())
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ /*
+ Set privilege information for the fields of newly created views.
+ We have that (any_priviliges == TRUE) if and only if we are creating
+ a view. In the time of view creation we can't use the MERGE algorithm,
+ therefore if 'tables' is itself a view, it is represented by a
+ temporary table. Thus in this case we can be sure that 'item' is an
+ Item_field.
+ */
+ if (any_privileges)
{
- Item *not_used_item;
- uint not_used_field_index= NO_CACHED_FIELD_INDEX;
- const char *field_name= iterator->name();
- /* Skip duplicate field names if NATURAL JOIN is used */
- if (!natural_join_table ||
- !find_field_in_table(thd, natural_join_table, field_name,
- field_name,
- strlen(field_name), &not_used_item, 0, 0, 0,
- &not_used_field_index, TRUE))
+ DBUG_ASSERT(tables->field_translation == NULL && table ||
+ tables->is_natural_join);
+ DBUG_ASSERT(item->type() == Item::FIELD_ITEM);
+ Item_field *fld= (Item_field*) item;
+ const char *field_table_name= field_iterator.table_name();
+
+ if (!tables->schema_table &&
+ !(fld->have_privileges=
+ (get_column_grant(thd, field_iterator.grant(),
+ field_iterator.db_name(),
+ field_table_name, fld->field_name) &
+ VIEW_ANY_ACL)))
{
- Item *item= iterator->create_item(thd);
- if (!item)
- goto err;
- thd->used_tables|= item->used_tables();
- if (!found++)
- (void) it->replace(item); // Replace '*'
- else
- it->after(item);
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (any_privileges)
- {
- /*
- In time of view creation MEGRGE algorithm for underlying
- VIEWs can't be used => it should be Item_field
- */
- DBUG_ASSERT(item->type() == Item::FIELD_ITEM);
- Item_field *fld= (Item_field*)item;
- char *db, *tab;
- if (tables->view)
- {
- db= tables->view_db.str;
- tab= tables->view_name.str;
- }
- else
- {
- db= tables->db;
- tab= tables->table_name;
- }
- if (!tables->schema_table &&
- !(fld->have_privileges= (get_column_grant(thd,
- &table->grant,
- db,
- tab,
- fld->field_name) &
- VIEW_ANY_ACL)))
- {
- my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0),
- "ANY",
- thd->priv_user,
- thd->host_or_ip,
- fld->field_name,
- tab);
- goto err;
- }
- }
-#endif
+ my_error(ER_COLUMNACCESS_DENIED_ERROR, MYF(0), "ANY",
+ thd->priv_user, thd->host_or_ip,
+ fld->field_name, field_table_name);
+ DBUG_RETURN(TRUE);
}
- if ((field= iterator->field()))
+ }
+#endif
+
+ if ((field= field_iterator.field()))
+ {
+ /*
+ Mark if field used before in this select.
+ Used by 'insert' to verify if a field name is used twice.
+ */
+ if (field->query_id == thd->query_id)
+ thd->dupp_field= field;
+ field->query_id= thd->query_id;
+
+ if (table)
+ table->used_keys.intersect(field->part_of_key);
+
+ if (tables->is_natural_join)
{
+ bool is_created;
+ TABLE *field_table;
/*
- Mark if field used before in this select.
- Used by 'insert' to verify if a field name is used twice
+ In this case we are sure that the column ref will not be created
+ because it was already created and stored with the natural join.
*/
- if (field->query_id == thd->query_id)
- thd->dupp_field=field;
- field->query_id=thd->query_id;
- table->used_keys.intersect(field->part_of_key);
- }
- else
- {
- Item *item= ((Field_iterator_view *) iterator)->item();
- item->walk(&Item::reset_query_id_processor,
- (byte *)(&thd->query_id));
+ Natural_join_column *nj_col;
+ if (!(nj_col= field_iterator.get_or_create_column_ref(thd,
+ &is_created)))
+ DBUG_RETURN(TRUE);
+ DBUG_ASSERT(nj_col->table_field && !is_created);
+ field_table= nj_col->table_ref->table;
+ if (field_table)
+ {
+ thd->used_tables|= field_table->map;
+ field_table->used_keys.intersect(field->part_of_key);
+ field_table->used_fields++;
+ }
}
}
- /*
- All fields are used in case if usual tables (in case of view used
- fields marked in setup_tables during fix_fields of view columns
- */
- if (table)
+ else
{
- table->used_fields= table->s->fields;
- table->file->ha_set_all_bits_in_read_set();
+ thd->used_tables|= item->used_tables();
+ item->walk(&Item::reset_query_id_processor,
+ (byte *)(&thd->query_id));
}
}
+ /*
+ In case of stored tables, all fields are considered as used,
+ while in the case of views, the fields considered as used are the
+ ones marked in setup_tables during fix_fields of view columns.
+ For NATURAL joins, used_tables is updated in the IF above.
+ */
+ if (table)
+ table->used_fields= table->s->fields;
}
if (found)
- DBUG_RETURN(0);
+ DBUG_RETURN(FALSE);
+ /*
+ TODO: in the case when we skipped all columns because there was a
+ qualified '*', and all columns were coalesced, we have to give a more
+ meaningful message than ER_BAD_TABLE_ERROR.
+ */
if (!table_name)
my_message(ER_NO_TABLES_USED, ER(ER_NO_TABLES_USED), MYF(0));
else
my_error(ER_BAD_TABLE_ERROR, MYF(0), table_name);
-err:
- DBUG_RETURN(1);
+
+ DBUG_RETURN(TRUE);
}
/*
- Fix all conditions and outer join expressions
+ Fix all conditions and outer join expressions.
SYNOPSIS
setup_conds()
thd thread handler
- leaves list of leaves of join table tree
+ tables list of tables for name resolving (select_lex->table_list)
+ leaves list of leaves of join table tree (select_lex->leaf_tables)
+ conds WHERE clause
+
+ DESCRIPTION
+ TODO
+
+ RETURN
+ TRUE if some error occured (e.g. out of memory)
+ FALSE if all is OK
*/
int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
@@ -3695,6 +4527,7 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
arena= 0; // For easier test
thd->set_query_id=1;
+ select_lex->cond_count= 0;
for (table= tables; table; table= table->next_local)
{
@@ -3702,7 +4535,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
goto err_no_arena;
}
- select_lex->cond_count= 0;
if (*conds)
{
thd->where="where clause";
@@ -3711,11 +4543,14 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
goto err_no_arena;
}
- /* Check if we are using outer joins */
+ /*
+ Apply fix_fields() to all ON clauses at all levels of nesting,
+ including the ones inside view definitions.
+ */
for (table= leaves; table; table= table->next_leaf)
{
- TABLE_LIST *embedded;
- TABLE_LIST *embedding= table;
+ TABLE_LIST *embedded; /* The table at the current level of nesting. */
+ TABLE_LIST *embedding= table; /* The parent nested table reference. */
do
{
embedded= embedding;
@@ -3729,146 +4564,6 @@ int setup_conds(THD *thd, TABLE_LIST *tables, TABLE_LIST *leaves,
goto err_no_arena;
select_lex->cond_count++;
}
-
- if (embedded->natural_join)
- {
- /* Make a join of all fields wich have the same name */
- TABLE_LIST *tab1= embedded;
- TABLE_LIST *tab2= embedded->natural_join;
- if (!(embedded->outer_join & JOIN_TYPE_RIGHT))
- {
- while (tab1->nested_join)
- {
- TABLE_LIST *next;
- List_iterator_fast<TABLE_LIST> it(tab1->nested_join->join_list);
- tab1= it++;
- while ((next= it++))
- tab1= next;
- }
- }
- else
- {
- while (tab1->nested_join)
- tab1= tab1->nested_join->join_list.head();
- }
- if (embedded->outer_join & JOIN_TYPE_RIGHT)
- {
- while (tab2->nested_join)
- {
- TABLE_LIST *next;
- List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list);
- tab2= it++;
- while ((next= it++))
- tab2= next;
- }
- }
- else
- {
- while (tab2->nested_join)
- tab2= tab2->nested_join->join_list.head();
- }
-
- if (arena)
- arena= thd->change_arena_if_needed(&backup);
-
- TABLE *t1=tab1->table;
- TABLE *t2=tab2->table;
- Field_iterator_table table_iter;
- Field_iterator_view view_iter;
- Field_iterator *iterator;
- Field *t1_field, *t2_field;
- Item *item_t2= 0;
- Item_cond_and *cond_and= new Item_cond_and();
-
- if (!cond_and) // If not out of memory
- goto err_no_arena;
- cond_and->top_level_item();
-
- if (table->field_translation)
- {
- iterator= &view_iter;
- view_iter.set(tab1);
- }
- else
- {
- iterator= &table_iter;
- table_iter.set(tab1);
- }
-
- for (; !iterator->end_of_fields(); iterator->next())
- {
- const char *t1_field_name= iterator->name();
- uint not_used_field_index= NO_CACHED_FIELD_INDEX;
-
- if ((t2_field= find_field_in_table(thd, tab2, t1_field_name,
- t1_field_name,
- strlen(t1_field_name), &item_t2,
- 0, 0, 0,
- &not_used_field_index,
- FALSE)))
- {
- if (t2_field != view_ref_found)
- {
- if (!(item_t2= new Item_field(thd, &select_lex->context,
- t2_field)))
- goto err;
- /* Mark field used for table cache */
- t2_field->query_id= thd->query_id;
- t2->file->ha_set_bit_in_read_set(t2_field->fieldnr);
- t2->used_keys.intersect(t2_field->part_of_key);
- }
- if ((t1_field= iterator->field()))
- {
- /* Mark field used for table cache */
- t1_field->query_id= thd->query_id;
- t1->file->ha_set_bit_in_read_set(t1_field->fieldnr);
- t1->used_keys.intersect(t1_field->part_of_key);
- }
- Item_func_eq *tmp= new Item_func_eq(iterator->create_item(thd),
- item_t2);
- if (!tmp)
- goto err;
- cond_and->list.push_back(tmp);
- }
- }
- select_lex->cond_count+= cond_and->list.elements;
-
- // to prevent natural join processing during PS re-execution
- embedding->natural_join= 0;
-
- if (cond_and->list.elements)
- {
- COND *on_expr= cond_and;
- if (!on_expr->fixed)
- on_expr->fix_fields(thd, &on_expr);
- if (!embedded->outer_join) // Not left join
- {
- *conds= and_conds(*conds, cond_and);
- // fix_fields() should be made with temporary memory pool
- if (arena)
- thd->restore_backup_item_arena(arena, &backup);
- if (*conds && !(*conds)->fixed)
- {
- if ((*conds)->fix_fields(thd, conds))
- goto err_no_arena;
- }
- }
- else
- {
- embedded->on_expr= and_conds(embedded->on_expr, cond_and);
- // fix_fields() should be made with temporary memory pool
- if (arena)
- thd->restore_backup_item_arena(arena, &backup);
- if (embedded->on_expr && !embedded->on_expr->fixed)
- {
- if (embedded->on_expr->fix_fields(thd, &embedded->on_expr))
- goto err_no_arena;
- }
- }
- }
- else if (arena)
- thd->restore_backup_item_arena(arena, &backup);
- }
embedding= embedded->embedding;
}
while (embedding &&
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index d03d4c381fa..8cfeb4be024 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -762,7 +762,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
TABLE_COUNTER_TYPE local_tables;
ulong tot_length;
DBUG_ENTER("Query_cache::store_query");
- if (query_cache_size == 0)
+ if (query_cache_size == 0 || thd->locked_tables)
DBUG_VOID_RETURN;
uint8 tables_type= 0;
@@ -923,6 +923,10 @@ end:
0 The query was cached and user was sent the result.
-1 The query was cached but we didn't have rights to use it.
No error is sent to the client yet.
+
+ NOTE
+ This method requires that sql points to allocated memory of size:
+ tot_length= query_length + thd->db_length + 1 + QUERY_CACHE_FLAGS_SIZE;
*/
int
@@ -936,8 +940,8 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
Query_cache_query_flags flags;
DBUG_ENTER("Query_cache::send_result_to_client");
- if (query_cache_size == 0 || thd->variables.query_cache_type == 0)
-
+ if (query_cache_size == 0 || thd->locked_tables ||
+ thd->variables.query_cache_type == 0)
goto err;
/* Check that we haven't forgot to reset the query cache variables */
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 56d3194765b..d4f05456cad 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -157,8 +157,8 @@ bool foreign_key_prefix(Key *a, Key *b)
** Thread specific functions
****************************************************************************/
-Open_tables_state::Open_tables_state()
- :version(refresh_version)
+Open_tables_state::Open_tables_state(ulong version_arg)
+ :version(version_arg)
{
reset_open_tables_state();
}
@@ -172,9 +172,9 @@ Open_tables_state::Open_tables_state()
THD::THD()
:Statement(CONVENTIONAL_EXECUTION, 0, ALLOC_ROOT_MIN_BLOCK_SIZE, 0),
- Open_tables_state(),
+ Open_tables_state(refresh_version),
lock_id(&main_lock_id),
- user_time(0), in_sub_stmt(FALSE), global_read_lock(0), is_fatal_error(0),
+ user_time(0), in_sub_stmt(0), global_read_lock(0), is_fatal_error(0),
rand_used(0), time_zone_used(0),
last_insert_id_used(0), insert_id_used(0), clear_next_insert_id(0),
in_lock_tables(0), bootstrap(0), derived_tables_processing(FALSE),
@@ -323,7 +323,8 @@ void THD::init_for_queries()
variables.trans_alloc_block_size,
variables.trans_prealloc_size);
#endif
- transaction.xid.null();
+ transaction.xid_state.xid.null();
+ transaction.xid_state.in_thd=1;
}
@@ -358,9 +359,15 @@ void THD::cleanup(void)
{
DBUG_ENTER("THD::cleanup");
#ifdef ENABLE_WHEN_BINLOG_WILL_BE_ABLE_TO_PREPARE
- if (transaction.xa_state != XA_PREPARED)
+ if (transaction.xid_state.xa_state == XA_PREPARED)
+ {
+#error xid_state in the cache should be replaced by the allocated value
+ }
#endif
+ {
ha_rollback(this);
+ xid_cache_delete(&transaction.xid_state);
+ }
if (locked_tables)
{
lock=locked_tables; locked_tables=0;
@@ -1813,31 +1820,195 @@ void THD::set_status_var_init()
access to mysql.proc table to find definitions of stored routines.
****************************************************************************/
-bool THD::push_open_tables_state()
+void THD::reset_n_backup_open_tables_state(Open_tables_state *backup)
{
- Open_tables_state *state;
- DBUG_ENTER("push_open_table_state");
- /* Currently we only push things one level */
- DBUG_ASSERT(open_state_list.elements == 0);
-
- if (!(state= (Open_tables_state*) alloc(sizeof(*state))))
- DBUG_RETURN(1); // Fatal error is set
- /* Store state for currently open tables */
- state->set_open_tables_state(this);
- if (open_state_list.push_back(state, mem_root))
- DBUG_RETURN(1); // Fatal error is set
+ DBUG_ENTER("reset_n_backup_open_tables_state");
+ backup->set_open_tables_state(this);
reset_open_tables_state();
- DBUG_RETURN(0);
+ DBUG_VOID_RETURN;
}
-void THD::pop_open_tables_state()
-{
- Open_tables_state *state;
- DBUG_ENTER("pop_open_table_state");
- /* Currently we only push things one level */
- DBUG_ASSERT(open_state_list.elements == 1);
- state= open_state_list.pop();
- set_open_tables_state(state);
+void THD::restore_backup_open_tables_state(Open_tables_state *backup)
+{
+ DBUG_ENTER("restore_backup_open_tables_state");
+ /*
+ Before we will throw away current open tables state we want
+ to be sure that it was properly cleaned up.
+ */
+ DBUG_ASSERT(open_tables == 0 && temporary_tables == 0 &&
+ handler_tables == 0 && derived_tables == 0 &&
+ lock == 0 && locked_tables == 0 &&
+ prelocked_mode == NON_PRELOCKED);
+ set_open_tables_state(backup);
DBUG_VOID_RETURN;
}
+
+
+
+/****************************************************************************
+ Handling of statement states in functions and triggers.
+
+ This is used to ensure that the function/trigger gets a clean state
+ to work with and does not cause any side effects of the calling statement.
+
+ It also allows most stored functions and triggers to replicate even
+ if they are used items that would normally be stored in the binary
+ replication (like last_insert_id() etc...)
+
+ The following things is done
+ - Disable binary logging for the duration of the statement
+ - Disable multi-result-sets for the duration of the statement
+ - Value of last_insert_id() is reset and restored
+ - Value set by 'SET INSERT_ID=#' is reset and restored
+ - Value for found_rows() is reset and restored
+ - examined_row_count is added to the total
+ - cuted_fields is added to the total
+
+ NOTES:
+ Seed for random() is saved for the first! usage of RAND()
+ We reset examined_row_count and cuted_fields and add these to the
+ result to ensure that if we have a bug that would reset these within
+ a function, we are not loosing any rows from the main statement.
+****************************************************************************/
+
+void THD::reset_sub_statement_state(Sub_statement_state *backup,
+ uint new_state)
+{
+ backup->options= options;
+ backup->in_sub_stmt= in_sub_stmt;
+ backup->no_send_ok= net.no_send_ok;
+ backup->enable_slow_log= enable_slow_log;
+ backup->last_insert_id= last_insert_id;
+ backup->next_insert_id= next_insert_id;
+ backup->insert_id_used= insert_id_used;
+ backup->limit_found_rows= limit_found_rows;
+ backup->examined_row_count= examined_row_count;
+ backup->sent_row_count= sent_row_count;
+ backup->cuted_fields= cuted_fields;
+ backup->client_capabilities= client_capabilities;
+
+ options&= ~OPTION_BIN_LOG;
+ /* Disable result sets */
+ client_capabilities &= ~CLIENT_MULTI_RESULTS;
+ in_sub_stmt|= new_state;
+ last_insert_id= 0;
+ next_insert_id= 0;
+ insert_id_used= 0;
+ examined_row_count= 0;
+ sent_row_count= 0;
+ cuted_fields= 0;
+
+#ifndef EMBEDDED_LIBRARY
+ /* Surpress OK packets in case if we will execute statements */
+ net.no_send_ok= TRUE;
+#endif
+}
+
+
+void THD::restore_sub_statement_state(Sub_statement_state *backup)
+{
+ options= backup->options;
+ in_sub_stmt= backup->in_sub_stmt;
+ net.no_send_ok= backup->no_send_ok;
+ enable_slow_log= backup->enable_slow_log;
+ last_insert_id= backup->last_insert_id;
+ next_insert_id= backup->next_insert_id;
+ insert_id_used= backup->insert_id_used;
+ limit_found_rows= backup->limit_found_rows;
+ sent_row_count= backup->sent_row_count;
+ client_capabilities= backup->client_capabilities;
+
+ /*
+ The following is added to the old values as we are interested in the
+ total complexity of the query
+ */
+ examined_row_count+= backup->examined_row_count;
+ cuted_fields+= backup->cuted_fields;
+}
+
+
+/***************************************************************************
+ Handling of XA id cacheing
+***************************************************************************/
+
+pthread_mutex_t LOCK_xid_cache;
+HASH xid_cache;
+
+static byte *xid_get_hash_key(const byte *ptr,uint *length,
+ my_bool not_used __attribute__((unused)))
+{
+ *length=((XID_STATE*)ptr)->xid.length();
+ return (byte *)&((XID_STATE*)ptr)->xid;
+}
+
+static void xid_free_hash (void *ptr)
+{
+ if (!((XID_STATE*)ptr)->in_thd)
+ my_free((byte *)ptr, MYF(0));
+}
+
+bool xid_cache_init()
+{
+ pthread_mutex_init(&LOCK_xid_cache, MY_MUTEX_INIT_FAST);
+ return hash_init(&xid_cache, &my_charset_bin, 100, 0, 0,
+ xid_get_hash_key, xid_free_hash, 0) != 0;
+}
+
+void xid_cache_free()
+{
+ if (hash_inited(&xid_cache))
+ {
+ hash_free(&xid_cache);
+ pthread_mutex_destroy(&LOCK_xid_cache);
+ }
+}
+
+XID_STATE *xid_cache_search(XID *xid)
+{
+ pthread_mutex_lock(&LOCK_xid_cache);
+ XID_STATE *res=(XID_STATE *)hash_search(&xid_cache, (byte *)xid, xid->length());
+ pthread_mutex_unlock(&LOCK_xid_cache);
+ return res;
+}
+
+
+bool xid_cache_insert(XID *xid, enum xa_states xa_state)
+{
+ XID_STATE *xs;
+ my_bool res;
+ pthread_mutex_lock(&LOCK_xid_cache);
+ if (hash_search(&xid_cache, (byte *)xid, xid->length()))
+ res=0;
+ else if (!(xs=(XID_STATE *)my_malloc(sizeof(*xs), MYF(MY_WME))))
+ res=1;
+ else
+ {
+ xs->xa_state=xa_state;
+ xs->xid.set(xid);
+ xs->in_thd=0;
+ res=my_hash_insert(&xid_cache, (byte*)xs);
+ }
+ pthread_mutex_unlock(&LOCK_xid_cache);
+ return res;
+}
+
+
+bool xid_cache_insert(XID_STATE *xid_state)
+{
+ pthread_mutex_lock(&LOCK_xid_cache);
+ DBUG_ASSERT(hash_search(&xid_cache, (byte *)&xid_state->xid,
+ xid_state->xid.length())==0);
+ my_bool res=my_hash_insert(&xid_cache, (byte*)xid_state);
+ pthread_mutex_unlock(&LOCK_xid_cache);
+ return res;
+}
+
+
+void xid_cache_delete(XID_STATE *xid_state)
+{
+ pthread_mutex_lock(&LOCK_xid_cache);
+ hash_delete(&xid_cache, (byte *)xid_state);
+ pthread_mutex_unlock(&LOCK_xid_cache);
+}
+
diff --git a/sql/sql_class.h b/sql/sql_class.h
index d2aea640245..a1de3391ab4 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -351,8 +351,6 @@ public:
inline uint32 get_open_count() { return open_count; }
};
-/* character conversion tables */
-
typedef struct st_copy_info {
ha_rows records;
@@ -566,11 +564,11 @@ struct system_variables
#endif /* HAVE_NDBCLUSTER_DB */
my_bool old_alter_table;
my_bool old_passwords;
-
+
/* Only charset part of these variables is sensible */
- CHARSET_INFO *character_set_client;
+ CHARSET_INFO *character_set_client;
CHARSET_INFO *character_set_results;
-
+
/* Both charset and collation parts of these variables are important */
CHARSET_INFO *collation_server;
CHARSET_INFO *collation_database;
@@ -633,7 +631,7 @@ typedef struct system_status_var
ulong filesort_range_count;
ulong filesort_rows;
ulong filesort_scan_count;
- /* Ppepared statements and binary protocol */
+ /* Prepared statements and binary protocol */
ulong com_stmt_prepare;
ulong com_stmt_execute;
ulong com_stmt_send_long_data;
@@ -658,8 +656,8 @@ void free_tmp_table(THD *thd, TABLE *entry);
/* The following macro is to make init of Query_arena simpler */
#ifndef DBUG_OFF
#define INIT_ARENA_DBUG_INFO is_backup_arena= 0
-#else
-#define INIT_ARENA_DBUG_INFO
+#else
+#define INIT_ARENA_DBUG_INFO
#endif
@@ -934,6 +932,22 @@ struct st_savepoint {
enum xa_states {XA_NOTR=0, XA_ACTIVE, XA_IDLE, XA_PREPARED};
extern const char *xa_state_names[];
+typedef struct st_xid_state {
+ /* For now, this is only used to catch duplicated external xids */
+ XID xid; // transaction identifier
+ enum xa_states xa_state; // used by external XA only
+ bool in_thd;
+} XID_STATE;
+
+extern pthread_mutex_t LOCK_xid_cache;
+extern HASH xid_cache;
+bool xid_cache_init(void);
+void xid_cache_free(void);
+XID_STATE *xid_cache_search(XID *xid);
+bool xid_cache_insert(XID *xid, enum xa_states xa_state);
+bool xid_cache_insert(XID_STATE *xid_state);
+void xid_cache_delete(XID_STATE *xid_state);
+
/*
A registry for item tree transformations performed during
query optimization. We register only those changes which require
@@ -955,7 +969,7 @@ enum prelocked_mode_type {NON_PRELOCKED= 0, PRELOCKED= 1,
/*
- Class that holds information about tables which were open and locked
+ Class that holds information about tables which were opened and locked
by the thread. It is also used to save/restore this information in
push_open_tables_state()/pop_open_tables_state().
*/
@@ -1018,7 +1032,13 @@ public:
ulong version;
uint current_tablenr;
- Open_tables_state();
+ /*
+ This constructor serves for creation of Open_tables_state instances
+ which are used as backup storage.
+ */
+ Open_tables_state() {};
+
+ Open_tables_state(ulong version_arg);
void set_open_tables_state(Open_tables_state *state)
{
@@ -1034,6 +1054,27 @@ public:
};
+/* class to save context when executing a function or trigger */
+
+/* Defines used for Sub_statement_state::in_sub_stmt */
+
+#define SUB_STMT_TRIGGER 1
+#define SUB_STMT_FUNCTION 2
+
+class Sub_statement_state
+{
+public:
+ ulonglong options;
+ ulonglong last_insert_id, next_insert_id;
+ ulonglong limit_found_rows;
+ ha_rows cuted_fields, sent_row_count, examined_row_count;
+ ulong client_capabilities;
+ uint in_sub_stmt;
+ bool enable_slow_log, insert_id_used;
+ my_bool no_send_ok;
+};
+
+
/*
For each client connection we create a separate thread with THD serving as
a thread/connection descriptor
@@ -1071,7 +1112,7 @@ public:
// the lock_id of a cursor.
pthread_mutex_t LOCK_delete; // Locked before thd is deleted
/* all prepared statements and cursors of this connection */
- Statement_map stmt_map;
+ Statement_map stmt_map;
/*
A pointer to the stack frame of handle_one_connection(),
which is called first in the thread for handling a client
@@ -1140,11 +1181,10 @@ public:
time_t connect_time,thr_create_time; // track down slow pthread_create
thr_lock_type update_lock_default;
delayed_insert *di;
- my_bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
-
- /* TRUE if we are inside of trigger or stored function. */
- bool in_sub_stmt;
-
+
+ /* <> 0 if we are inside of trigger or stored function. */
+ uint in_sub_stmt;
+
/* container for handler's private per-connection data */
void *ha_data[MAX_HA];
struct st_transactions {
@@ -1152,8 +1192,7 @@ public:
THD_TRANS all; // Trans since BEGIN WORK
THD_TRANS stmt; // Trans for current statement
bool on; // see ha_enable_transaction()
- XID xid; // transaction identifier
- enum xa_states xa_state; // used by external XA only
+ XID_STATE xid_state;
/*
Tables changed in transaction (that must be invalidated in query cache).
List contain only transactional tables, that not invalidated in query
@@ -1173,7 +1212,7 @@ public:
st_transactions()
{
bzero((char*)this, sizeof(*this));
- xid.null();
+ xid_state.xid.null();
init_sql_alloc(&mem_root, ALLOC_ROOT_MIN_BLOCK_SIZE, 0);
}
#endif
@@ -1226,8 +1265,16 @@ public:
*/
ulonglong current_insert_id;
ulonglong limit_found_rows;
+ ulonglong options; /* Bitmap of states */
+ longlong row_count_func; /* For the ROW_COUNT() function */
ha_rows cuted_fields,
sent_row_count, examined_row_count;
+ /*
+ The set of those tables whose fields are referenced in all subqueries
+ of the query.
+ TODO: possibly this it is incorrect to have used tables in THD because
+ with more than one subquery, it is not clear what does the field mean.
+ */
table_map used_tables;
USER_CONN *user_connect;
CHARSET_INFO *db_charset;
@@ -1240,7 +1287,6 @@ public:
List <MYSQL_ERROR> warn_list;
uint warn_count[(uint) MYSQL_ERROR::WARN_LEVEL_END];
uint total_warn_count;
- List <Open_tables_state> open_state_list;
/*
Id of current query. Statement can be reused to execute several queries
query_id is global in context of the whole MySQL server.
@@ -1250,7 +1296,7 @@ public:
update auto-updatable fields (like auto_increment and timestamp).
*/
query_id_t query_id, warn_id;
- ulong options, thread_id, col_access;
+ ulong thread_id, col_access;
/* Statement id is thread-wide. This counter is used to generate ids */
ulong statement_id_counter;
@@ -1290,7 +1336,8 @@ public:
bool no_warnings_for_error; /* no warnings on call to my_error() */
/* set during loop of derived table processing */
bool derived_tables_processing;
- longlong row_count_func; /* For the ROW_COUNT() function */
+ my_bool tablespace_op; /* This is TRUE in DISCARD/IMPORT TABLESPACE */
+
sp_rcontext *spcont; // SP runtime context
sp_cache *sp_proc_cache;
sp_cache *sp_func_cache;
@@ -1496,13 +1543,15 @@ public:
void set_status_var_init();
bool is_context_analysis_only()
{ return current_arena->is_stmt_prepare() || lex->view_prepare_mode; }
- bool push_open_tables_state();
- void pop_open_tables_state();
+ void reset_n_backup_open_tables_state(Open_tables_state *backup);
+ void restore_backup_open_tables_state(Open_tables_state *backup);
+ void reset_sub_statement_state(Sub_statement_state *backup, uint new_state);
+ void restore_sub_statement_state(Sub_statement_state *backup);
};
#define tmp_disable_binlog(A) \
- {ulong tmp_disable_binlog__save_options= (A)->options; \
+ {ulonglong tmp_disable_binlog__save_options= (A)->options; \
(A)->options&= ~OPTION_BIN_LOG
#define reenable_binlog(A) (A)->options= tmp_disable_binlog__save_options;}
@@ -1569,6 +1618,7 @@ public:
statement/stored procedure.
*/
virtual void cleanup();
+ void set_thd(THD *thd_arg) { thd= thd_arg; }
};
@@ -1924,14 +1974,13 @@ class multi_delete :public select_result_interceptor
{
TABLE_LIST *delete_tables, *table_being_deleted;
Unique **tempfiles;
- THD *thd;
ha_rows deleted, found;
uint num_of_tables;
int error;
bool do_delete, transactional_tables, normal_tables, delete_while_scanning;
public:
- multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables);
+ multi_delete(TABLE_LIST *dt, uint num_of_tables);
~multi_delete();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_data(List<Item> &items);
@@ -1947,7 +1996,6 @@ class multi_update :public select_result_interceptor
TABLE_LIST *all_tables; /* query/update command tables */
TABLE_LIST *leaves; /* list of leves of join table tree */
TABLE_LIST *update_tables, *table_being_updated;
- THD *thd;
TABLE **tmp_tables, *main_table, *table_to_update;
TMP_TABLE_PARAM *tmp_table_param;
ha_rows updated, found;
@@ -1959,7 +2007,7 @@ class multi_update :public select_result_interceptor
bool do_update, trans_safe, transactional_tables, ignore;
public:
- multi_update(THD *thd_arg, TABLE_LIST *ut, TABLE_LIST *leaves_list,
+ multi_update(TABLE_LIST *ut, TABLE_LIST *leaves_list,
List<Item> *fields, List<Item> *values,
enum_duplicates handle_duplicates, bool ignore);
~multi_update();
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index d110ff6f778..5ca3f07f0bd 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -26,13 +26,16 @@
#include <direct.h>
#endif
+#define MAX_DROP_TABLE_Q_LEN 1024
+
const char *del_exts[]= {".frm", ".BAK", ".TMD",".opt", NullS};
static TYPELIB deletable_extentions=
{array_elements(del_exts)-1,"del_exts", del_exts, NULL};
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp,
- const char *db, const char *path,
- uint level);
+ const char *db, const char *path, uint level,
+ TABLE_LIST **dropped_tables);
+
static long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
static my_bool rm_dir_w_symlink(const char *org_path, my_bool send_error);
/* Database options hash */
@@ -52,6 +55,7 @@ typedef struct my_dbopt_st
/*
Function we use in the creation of our hash to get key.
*/
+
static byte* dboptions_get_key(my_dbopt_t *opt, uint *length,
my_bool not_used __attribute__((unused)))
{
@@ -61,6 +65,21 @@ static byte* dboptions_get_key(my_dbopt_t *opt, uint *length,
/*
+ Helper function to write a query to binlog used by mysql_rm_db()
+*/
+
+static inline void write_to_binlog(THD *thd, char *query, uint q_len,
+ char *db, uint db_len)
+{
+ Query_log_event qinfo(thd, query, q_len, 0, 0);
+ qinfo.error_code= 0;
+ qinfo.db= db;
+ qinfo.db_len= db_len;
+ mysql_bin_log.write(&qinfo);
+}
+
+
+/*
Function to free dboptions hash element
*/
@@ -593,6 +612,7 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
char path[FN_REFLEN+16];
MY_DIR *dirp;
uint length;
+ TABLE_LIST* dropped_tables= 0;
DBUG_ENTER("mysql_rm_db");
VOID(pthread_mutex_lock(&LOCK_mysql_create_db));
@@ -629,8 +649,10 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
remove_db_from_cache(db);
pthread_mutex_unlock(&LOCK_open);
+
error= -1;
- if ((deleted= mysql_rm_known_files(thd, dirp, db, path, 0)) >= 0)
+ if ((deleted= mysql_rm_known_files(thd, dirp, db, path, 0,
+ &dropped_tables)) >= 0)
{
ha_drop_database(path);
query_cache_invalidate1(db);
@@ -672,6 +694,43 @@ bool mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
send_ok(thd, (ulong) deleted);
thd->server_status&= ~SERVER_STATUS_DB_DROPPED;
}
+ else if (mysql_bin_log.is_open())
+ {
+ char *query, *query_pos, *query_end, *query_data_start;
+ TABLE_LIST *tbl;
+ uint db_len;
+
+ if (!(query= thd->alloc(MAX_DROP_TABLE_Q_LEN)))
+ goto exit; /* not much else we can do */
+ query_pos= query_data_start= strmov(query,"drop table ");
+ query_end= query + MAX_DROP_TABLE_Q_LEN;
+ db_len= strlen(db);
+
+ for (tbl= dropped_tables; tbl; tbl= tbl->next_local)
+ {
+ uint tbl_name_len;
+ if (!tbl->was_dropped)
+ continue;
+
+ /* 3 for the quotes and the comma*/
+ tbl_name_len= strlen(tbl->table_name) + 3;
+ if (query_pos + tbl_name_len + 1 >= query_end)
+ {
+ write_to_binlog(thd, query, query_pos -1 - query, db, db_len);
+ query_pos= query_data_start;
+ }
+
+ *query_pos++ = '`';
+ query_pos= strmov(query_pos,tbl->table_name);
+ *query_pos++ = '`';
+ *query_pos++ = ',';
+ }
+
+ if (query_pos != query_data_start)
+ {
+ write_to_binlog(thd, query, query_pos -1 - query, db, db_len);
+ }
+ }
exit:
(void)sp_drop_db_routines(thd, db); /* QQ Ignore errors for now */
@@ -717,7 +776,8 @@ exit2:
*/
static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
- const char *org_path, uint level)
+ const char *org_path, uint level,
+ TABLE_LIST **dropped_tables)
{
long deleted=0;
ulong found_other_files=0;
@@ -759,7 +819,7 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
if ((new_dirp = my_dir(newpath,MYF(MY_DONT_SORT))))
{
DBUG_PRINT("my",("New subdir found: %s", newpath));
- if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1)) < 0)
+ if ((mysql_rm_known_files(thd, new_dirp, NullS, newpath,1,0)) < 0)
goto err;
if (!(copy_of_path= thd->memdup(newpath, length+1)) ||
!(dir= new (thd->mem_root) String(copy_of_path, length,
@@ -837,6 +897,9 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
}
my_dirend(dirp);
+ if (dropped_tables)
+ *dropped_tables= tot_list;
+
/*
If the directory is a symbolic link, remove the link first, then
remove the directory the symbolic link pointed at
@@ -996,8 +1059,9 @@ err:
SYNOPSIS
mysql_change_db()
- thd Thread handler
- name Databasename
+ thd Thread handler
+ name Databasename
+ no_access_check True don't do access check. In this case name may be ""
DESCRIPTION
Becasue the database name may have been given directly from the
@@ -1009,28 +1073,37 @@ err:
replication slave SQL thread (for that thread, setting of thd->db is done
in ::exec_event() methods of log_event.cc).
- This function does not send the error message to the client, if that
- should be sent to the client, call net_send_error after this function
+ This function does not send anything, including error messages to the
+ client, if that should be sent to the client, call net_send_error after
+ this function.
RETURN VALUES
0 ok
1 error
*/
-bool mysql_change_db(THD *thd, const char *name)
+bool mysql_change_db(THD *thd, const char *name, bool no_access_check)
{
int length, db_length;
char *dbname=my_strdup((char*) name,MYF(MY_WME));
char path[FN_REFLEN];
HA_CREATE_INFO create;
- bool schema_db= 0;
+ bool system_db= 0;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
ulong db_access;
#endif
DBUG_ENTER("mysql_change_db");
+ DBUG_PRINT("enter",("name: '%s'",name));
+ /* dbname can only be NULL if malloc failed */
if (!dbname || !(db_length= strlen(dbname)))
{
+ if (no_access_check && dbname)
+ {
+ /* Called from SP when orignal database was not set */
+ system_db= 1;
+ goto end;
+ }
x_free(dbname); /* purecov: inspected */
my_message(ER_NO_DB_ERROR, ER(ER_NO_DB_ERROR),
MYF(0)); /* purecov: inspected */
@@ -1045,7 +1118,7 @@ bool mysql_change_db(THD *thd, const char *name)
DBUG_PRINT("info",("Use database: %s", dbname));
if (!my_strcasecmp(system_charset_info, dbname, information_schema_name.str))
{
- schema_db= 1;
+ system_db= 1;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
db_access= SELECT_ACL;
#endif
@@ -1053,23 +1126,27 @@ bool mysql_change_db(THD *thd, const char *name)
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (test_all_bits(thd->master_access,DB_ACLS))
- db_access=DB_ACLS;
- else
- db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
- thd->master_access);
- if (!(db_access & DB_ACLS) && (!grant_option || check_grant_db(thd,dbname)))
+ if (!no_access_check)
{
- my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
- thd->priv_user,
- thd->priv_host,
- dbname);
- mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
- thd->priv_user,
- thd->priv_host,
- dbname);
- my_free(dbname,MYF(0));
- DBUG_RETURN(1);
+ if (test_all_bits(thd->master_access,DB_ACLS))
+ db_access=DB_ACLS;
+ else
+ db_access= (acl_get(thd->host,thd->ip, thd->priv_user,dbname,0) |
+ thd->master_access);
+ if (!(db_access & DB_ACLS) && (!grant_option ||
+ check_grant_db(thd,dbname)))
+ {
+ my_error(ER_DBACCESS_DENIED_ERROR, MYF(0),
+ thd->priv_user,
+ thd->priv_host,
+ dbname);
+ mysql_log.write(thd,COM_INIT_DB,ER(ER_DBACCESS_DENIED_ERROR),
+ thd->priv_user,
+ thd->priv_host,
+ dbname);
+ my_free(dbname,MYF(0));
+ DBUG_RETURN(1);
+ }
}
#endif
(void) sprintf(path,"%s/%s",mysql_data_home,dbname);
@@ -1083,14 +1160,14 @@ bool mysql_change_db(THD *thd, const char *name)
DBUG_RETURN(1);
}
end:
- send_ok(thd);
x_free(thd->db);
thd->db=dbname; // THD::~THD will free this
thd->db_length=db_length;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- thd->db_access=db_access;
+ if (!no_access_check)
+ thd->db_access=db_access;
#endif
- if (schema_db)
+ if (system_db)
{
thd->db_charset= system_charset_info;
thd->variables.collation_database= system_charset_info;
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 2967e2a8a20..5c339d7d64d 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -30,7 +30,7 @@
#include "sql_trigger.h"
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- SQL_LIST *order, ha_rows limit, ulong options)
+ SQL_LIST *order, ha_rows limit, ulonglong options)
{
bool will_batch;
int error, loc_error;
@@ -309,6 +309,7 @@ bool mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds)
DBUG_ENTER("mysql_prepare_delete");
if (setup_tables(thd, &thd->lex->select_lex.context,
+ &thd->lex->select_lex.top_join_list,
table_list, conds, &select_lex->leaf_tables,
FALSE) ||
setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
@@ -367,6 +368,7 @@ bool mysql_multi_delete_prepare(THD *thd)
lex->query_tables also point on local list of DELETE SELECT_LEX
*/
if (setup_tables(thd, &thd->lex->select_lex.context,
+ &thd->lex->select_lex.top_join_list,
lex->query_tables, &lex->select_lex.where,
&lex->select_lex.leaf_tables, FALSE))
DBUG_RETURN(TRUE);
@@ -415,9 +417,8 @@ bool mysql_multi_delete_prepare(THD *thd)
}
-multi_delete::multi_delete(THD *thd_arg, TABLE_LIST *dt,
- uint num_of_tables_arg)
- : delete_tables(dt), thd(thd_arg), deleted(0), found(0),
+multi_delete::multi_delete(TABLE_LIST *dt, uint num_of_tables_arg)
+ : delete_tables(dt), deleted(0), found(0),
num_of_tables(num_of_tables_arg), error(0),
do_delete(0), transactional_tables(0), normal_tables(0)
{
@@ -826,7 +827,7 @@ bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
ha_enable_transaction(thd, FALSE);
mysql_init_select(thd->lex);
error= mysql_delete(thd, table_list, (COND*) 0, (SQL_LIST*) 0,
- HA_POS_ERROR, 0);
+ HA_POS_ERROR, LL(0));
ha_enable_transaction(thd, TRUE);
thd->options= save_options;
DBUG_RETURN(error);
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index fc9d15e94c4..7b9191cd841 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -125,6 +125,11 @@ int mysql_derived_prepare(THD *thd, LEX *lex, TABLE_LIST *orig_table_list)
if ((res= unit->prepare(thd, derived_result, 0, orig_table_list->alias)))
goto exit;
+ if (check_duplicate_names(unit->types, 0))
+ {
+ res= -1;
+ goto exit;
+ }
derived_result->tmp_table_param.init();
derived_result->tmp_table_param.field_count= unit->types.elements;
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 84087db9719..fc7d7ac0012 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -187,7 +187,7 @@ bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen)
/* for now HANDLER can be used only for real TABLES */
tables->required_type= FRMTYPE_TABLE;
- error= open_tables(thd, &tables, &counter);
+ error= open_tables(thd, &tables, &counter, 0);
HANDLER_TABLES_HACK(thd);
if (error)
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 11045529a51..e3a2602713f 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -75,7 +75,7 @@ enum enum_used_fields
RETURN VALUES
0 all ok
- 1 one of the fileds didn't finded
+ 1 one of the fileds was not found
*/
static bool init_fields(THD *thd, TABLE_LIST *tables,
@@ -90,7 +90,7 @@ static bool init_fields(THD *thd, TABLE_LIST *tables,
Item_field *field= new Item_field(context,
"mysql", find_fields->table_name,
find_fields->field_name);
- if (!(find_fields->field= find_field_in_tables(thd, field, tables,
+ if (!(find_fields->field= find_field_in_tables(thd, field, tables, NULL,
0, REPORT_ALL_ERRORS, 1,
TRUE)))
DBUG_RETURN(1);
@@ -623,43 +623,45 @@ bool mysqld_help(THD *thd, const char *mask)
Protocol *protocol= thd->protocol;
SQL_SELECT *select;
st_find_field used_fields[array_elements(init_used_fields)];
- DBUG_ENTER("mysqld_help");
-
TABLE_LIST *leaves= 0;
TABLE_LIST tables[4];
+ List<String> topics_list, categories_list, subcategories_list;
+ String name, description, example;
+ int count_topics, count_categories, error;
+ uint mlen= strlen(mask);
+ size_t i;
+ MEM_ROOT *mem_root= thd->mem_root;
+ DBUG_ENTER("mysqld_help");
+
bzero((gptr)tables,sizeof(tables));
tables[0].alias= tables[0].table_name= (char*) "help_topic";
tables[0].lock_type= TL_READ;
- tables[0].next_global= tables[0].next_local= &tables[1];
+ tables[0].next_global= tables[0].next_local=
+ tables[0].next_name_resolution_table= &tables[1];
tables[1].alias= tables[1].table_name= (char*) "help_category";
tables[1].lock_type= TL_READ;
- tables[1].next_global= tables[1].next_local= &tables[2];
+ tables[1].next_global= tables[1].next_local=
+ tables[1].next_name_resolution_table= &tables[2];
tables[2].alias= tables[2].table_name= (char*) "help_relation";
tables[2].lock_type= TL_READ;
- tables[2].next_global= tables[2].next_local= &tables[3];
+ tables[2].next_global= tables[2].next_local=
+ tables[2].next_name_resolution_table= &tables[3];
tables[3].alias= tables[3].table_name= (char*) "help_keyword";
tables[3].lock_type= TL_READ;
tables[0].db= tables[1].db= tables[2].db= tables[3].db= (char*) "mysql";
- List<String> topics_list, categories_list, subcategories_list;
- String name, description, example;
- int count_topics, count_categories, error;
- uint mlen= strlen(mask);
- MEM_ROOT *mem_root= thd->mem_root;
-
if (open_and_lock_tables(thd, tables))
goto error;
/*
Init tables and fields to be usable from items
-
tables do not contain VIEWs => we can pass 0 as conds
*/
setup_tables(thd, &thd->lex->select_lex.context,
+ &thd->lex->select_lex.top_join_list,
tables, 0, &leaves, FALSE);
memcpy((char*) used_fields, (char*) init_used_fields, sizeof(used_fields));
if (init_fields(thd, tables, used_fields, array_elements(used_fields)))
goto error;
- size_t i;
for (i=0; i<sizeof(tables)/sizeof(TABLE_LIST); i++)
tables[i].table->file->init_table_handle_for_HANDLER();
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 6e83017a4e4..869ae688e34 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -111,12 +111,15 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
}
else
{ // Part field list
- Name_resolution_context *context= &thd->lex->select_lex.context;
- TABLE_LIST *save_next= table_list->next_local,
- *save_context= context->table_list;
- bool save_resolve_in_select_list=
- thd->lex->select_lex.context.resolve_in_select_list;
+ SELECT_LEX *select_lex= &thd->lex->select_lex;
+ Name_resolution_context *context= &select_lex->context;
+ TABLE_LIST *save_next_local;
+ TABLE_LIST *save_table_list;
+ TABLE_LIST *save_first_name_resolution_table;
+ TABLE_LIST *save_next_name_resolution_table;
+ bool save_resolve_in_select_list;
int res;
+
if (fields.elements != values.elements)
{
my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), 1L);
@@ -124,8 +127,22 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
}
thd->dupp_field=0;
- thd->lex->select_lex.no_wrap_view_item= TRUE;
- /* fields only from first table */
+ select_lex->no_wrap_view_item= TRUE;
+
+ /* Save the state of the current name resolution context. */
+ save_table_list= context->table_list;
+ save_first_name_resolution_table= context->first_name_resolution_table;
+ save_next_name_resolution_table= (context->first_name_resolution_table) ?
+ context->first_name_resolution_table->
+ next_name_resolution_table :
+ NULL;
+ save_resolve_in_select_list= context->resolve_in_select_list;
+ save_next_local= table_list->next_local;
+
+ /*
+ Perform name resolution only in the first table - 'table_list',
+ which is the table that is inserted into.
+ */
table_list->next_local= 0;
context->resolve_in_table_list_only(table_list);
/*
@@ -133,12 +150,20 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
parameter to 2. This sets the bit in the write_set for each field.
*/
res= setup_fields(thd, 0, fields, 2, 0, 0);
- table_list->next_local= save_next;
+
+ /* Restore the current context. */
+ table_list->next_local= save_next_local;
+ context->table_list= save_table_list;
+ context->first_name_resolution_table= save_first_name_resolution_table;
+ if (context->first_name_resolution_table)
+ context->first_name_resolution_table->
+ next_name_resolution_table= save_next_name_resolution_table;
+ context->resolve_in_select_list= save_resolve_in_select_list;
thd->lex->select_lex.no_wrap_view_item= FALSE;
- context->table_list= save_context;
- context->resolve_in_select_list= save_resolve_in_select_list;
+
if (res)
return -1;
+
if (table_list->effective_algorithm == VIEW_ALGORITHM_MERGE)
{
/* it is join view => we need to find table for update */
@@ -267,9 +292,13 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
ulonglong id;
COPY_INFO info;
TABLE *table= 0;
- TABLE_LIST *next_local;
+ TABLE_LIST *save_table_list;
+ TABLE_LIST *save_next_local;
+ TABLE_LIST *save_first_name_resolution_table;
+ TABLE_LIST *save_next_name_resolution_table;
List_iterator_fast<List_item> its(values_list);
List_item *values;
+ Name_resolution_context *context;
#ifndef EMBEDDED_LIBRARY
char *query= thd->query;
#endif
@@ -348,9 +377,23 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
/* mysql_prepare_insert set table_list->table if it was not set */
table= table_list->table;
- next_local= table_list->next_local;
+ context= &thd->lex->select_lex.context;
+ /* Save the state of the current name resolution context. */
+ save_table_list= context->table_list;
+ save_first_name_resolution_table= context->first_name_resolution_table;
+ save_next_name_resolution_table= (context->first_name_resolution_table) ?
+ context->first_name_resolution_table->
+ next_name_resolution_table :
+ NULL;
+ save_next_local= table_list->next_local;
+
+ /*
+ Perform name resolution only in the first table - 'table_list',
+ which is the table that is inserted into.
+ */
table_list->next_local= 0;
- thd->lex->select_lex.context.resolve_in_table_list_only(table_list);
+ context->resolve_in_table_list_only(table_list);
+
value_count= values->elements;
while ((values= its++))
{
@@ -364,7 +407,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
goto abort;
}
its.rewind ();
- table_list->next_local= next_local;
+
+ /* Restore the current context. */
+ table_list->next_local= save_next_local;
+ context->first_name_resolution_table= save_first_name_resolution_table;
+ if (context->first_name_resolution_table)
+ context->first_name_resolution_table->
+ next_name_resolution_table= save_next_name_resolution_table;
+
/*
Fill in the given fields and dump it to the table file
*/
@@ -720,6 +770,7 @@ static bool mysql_prepare_insert_check_table(THD *thd, TABLE_LIST *table_list,
DBUG_ENTER("mysql_prepare_insert_check_table");
if (setup_tables(thd, &thd->lex->select_lex.context,
+ &thd->lex->select_lex.top_join_list,
table_list, where, &thd->lex->select_lex.leaf_tables,
select_insert))
DBUG_RETURN(TRUE);
@@ -774,10 +825,13 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
COND **where, bool select_insert)
{
SELECT_LEX *select_lex= &thd->lex->select_lex;
+ Name_resolution_context *context= &select_lex->context;
TABLE_LIST *save_table_list;
TABLE_LIST *save_next_local;
+ TABLE_LIST *save_first_name_resolution_table;
+ TABLE_LIST *save_next_name_resolution_table;
+ bool save_resolve_in_select_list;
bool insert_into_view= (table_list->view != 0);
- bool save_resolve_in_select_list;
bool res= 0;
DBUG_ENTER("mysql_prepare_insert");
DBUG_PRINT("enter", ("table_list 0x%lx, table 0x%lx, view %d",
@@ -815,35 +869,58 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
select_insert))
DBUG_RETURN(TRUE);
- save_table_list= select_lex->context.table_list;
- save_resolve_in_select_list= select_lex->context.resolve_in_select_list;
- save_next_local= table_list->next_local;
+ /* Save the state of the current name resolution context. */
+ save_table_list= context->table_list;
+ /* Here first_name_resolution_table points to the first select table. */
+ save_first_name_resolution_table= context->first_name_resolution_table;
+ save_next_name_resolution_table= (context->first_name_resolution_table) ?
+ context->first_name_resolution_table->
+ next_name_resolution_table :
+ NULL;
+ save_resolve_in_select_list= context->resolve_in_select_list;
+ save_next_local= table_list->next_local;
+ /*
+ Perform name resolution only in the first table - 'table_list',
+ which is the table that is inserted into.
+ */
table_list->next_local= 0;
- select_lex->context.resolve_in_table_list_only(table_list);
- if ((values && check_insert_fields(thd, table_list, fields, *values,
- !insert_into_view)) ||
- (values && setup_fields(thd, 0, *values, 0, 0, 0)))
- res= TRUE;
- else if (duplic == DUP_UPDATE)
+ context->resolve_in_table_list_only(table_list);
+
+ /* Prepare the fields in the statement. */
+ if (values &&
+ !(res= check_insert_fields(thd, context->table_list, fields, *values,
+ !insert_into_view) ||
+ setup_fields(thd, 0, *values, 0, 0, 0)) &&
+ duplic == DUP_UPDATE)
{
select_lex->no_wrap_view_item= TRUE;
- res= check_update_fields(thd, table_list, update_fields);
+ res= check_update_fields(thd, context->table_list, update_fields);
select_lex->no_wrap_view_item= FALSE;
+ /*
+ When we are not using GROUP BY we can refer to other tables in the
+ ON DUPLICATE KEY part.
+ */
if (select_lex->group_list.elements == 0)
{
- /*
- When we are not using GROUP BY we can refer to other tables in the
- ON DUPLICATE KEY part
- */
- table_list->next_local= save_next_local;
+ context->table_list->next_local= save_next_local;
+ /* first_name_resolution_table was set by resolve_in_table_list_only() */
+ context->first_name_resolution_table->
+ next_name_resolution_table= save_next_local;
}
if (!res)
res= setup_fields(thd, 0, update_values, 1, 0, 0);
}
+
+ /* Restore the current context. */
table_list->next_local= save_next_local;
- select_lex->context.table_list= save_table_list;
- select_lex->context.resolve_in_select_list= save_resolve_in_select_list;
+ context->table_list= save_table_list;
+ context->first_name_resolution_table= save_first_name_resolution_table;
+ if (context->first_name_resolution_table)
+ context->first_name_resolution_table->
+ next_name_resolution_table= save_next_name_resolution_table;
+ context->resolve_in_select_list= save_resolve_in_select_list;
+
if (res)
DBUG_RETURN(res);
@@ -2102,7 +2179,55 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
*/
lex->current_select= &lex->select_lex;
res= check_insert_fields(thd, table_list, *fields, values,
- !insert_into_view);
+ !insert_into_view) ||
+ setup_fields(thd, 0, values, 0, 0, 0);
+
+ if (info.handle_duplicates == DUP_UPDATE)
+ {
+ /* Save the state of the current name resolution context. */
+ Name_resolution_context *context= &lex->select_lex.context;
+ TABLE_LIST *save_table_list;
+ TABLE_LIST *save_next_local;
+ TABLE_LIST *save_first_name_resolution_table;
+ TABLE_LIST *save_next_name_resolution_table;
+ save_table_list= context->table_list;
+ save_first_name_resolution_table= context->first_name_resolution_table;
+ save_next_name_resolution_table= (context->first_name_resolution_table) ?
+ context->first_name_resolution_table->
+ next_name_resolution_table :
+ NULL;
+ save_next_local= table_list->next_local;
+
+ /* Perform name resolution only in the first table - 'table_list'. */
+ table_list->next_local= 0;
+ context->resolve_in_table_list_only(table_list);
+
+ lex->select_lex.no_wrap_view_item= TRUE;
+ res= res || check_update_fields(thd, context->table_list,
+ *info.update_fields);
+ lex->select_lex.no_wrap_view_item= FALSE;
+ /*
+ When we are not using GROUP BY we can refer to other tables in the
+ ON DUPLICATE KEY part
+ */
+ if (lex->select_lex.group_list.elements == 0)
+ {
+ context->table_list->next_local= save_next_local;
+ /* first_name_resolution_table was set by resolve_in_table_list_only() */
+ context->first_name_resolution_table->
+ next_name_resolution_table= save_next_local;
+ }
+ res= res || setup_fields(thd, 0, *info.update_values, 1, 0, 0);
+
+ /* Restore the current context. */
+ table_list->next_local= save_next_local;
+ context->first_name_resolution_table= save_first_name_resolution_table;
+ if (context->first_name_resolution_table)
+ context->first_name_resolution_table->
+ next_name_resolution_table= save_next_name_resolution_table;
+
+ }
+
lex->current_select= lex_current_select_save;
if (res)
DBUG_RETURN(1);
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 3fdda5d31a7..9132d3177fc 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -118,8 +118,11 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->buf= lex->ptr= buf;
lex->end_of_query= buf+length;
+ lex->context_stack.empty();
lex->unit.init_query();
lex->unit.init_select();
+ /* 'parent_lex' is used in init_query() so it must be before it. */
+ lex->select_lex.parent_lex= lex;
lex->select_lex.init_query();
lex->value_list.empty();
lex->update_list.empty();
@@ -148,7 +151,6 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->leaf_tables_insert= lex->query_tables= 0;
lex->query_tables_last= &lex->query_tables;
lex->variables_used= 0;
- lex->select_lex.parent_lex= lex;
lex->empty_field_list_on_rset= 0;
lex->select_lex.select_number= 1;
lex->next_state=MY_LEX_START;
@@ -1115,6 +1117,11 @@ void st_select_lex::init_query()
having_fix_field= 0;
context.select_lex= this;
context.init();
+ /*
+ Add the name resolution context of the current (sub)query to the
+ stack of contexts for the whole query.
+ */
+ parent_lex->push_context(&context);
cond_count= with_wild= 0;
conds_processed_with_permanent_arena= 0;
ref_pointer_array= 0;
@@ -1131,7 +1138,7 @@ void st_select_lex::init_select()
{
st_select_lex_node::init_select();
group_list.empty();
- type= db= db1= table1= db2= table2= 0;
+ type= db= 0;
having= 0;
use_index_ptr= ignore_index_ptr= 0;
table_join_options= 0;
@@ -1505,13 +1512,16 @@ bool st_select_lex::setup_ref_array(THD *thd, uint order_group_num)
void st_select_lex_unit::print(String *str)
{
+ bool union_all= !union_distinct;
for (SELECT_LEX *sl= first_select(); sl; sl= sl->next_select())
{
if (sl != first_select())
{
str->append(" union ", 7);
- if (!union_distinct)
+ if (union_all)
str->append("all ", 4);
+ else if (union_distinct == sl)
+ union_all= TRUE;
}
if (sl->braces)
str->append('(');
@@ -1861,8 +1871,9 @@ TABLE_LIST *st_lex::unlink_first_table(bool *link_to_local)
*/
if ((*link_to_local= test(select_lex.table_list.first)))
{
- select_lex.table_list.first= (byte*) (select_lex.context.table_list=
- first->next_local);
+ select_lex.context.table_list=
+ select_lex.context.first_name_resolution_table= first->next_local;
+ select_lex.table_list.first= (byte*) (first->next_local);
select_lex.table_list.elements--; //safety
first->next_local= 0;
/*
@@ -1967,8 +1978,8 @@ void st_lex::link_first_table_back(TABLE_LIST *first,
if (link_to_local)
{
first->next_local= (TABLE_LIST*) select_lex.table_list.first;
- select_lex.table_list.first=
- (byte*) (select_lex.context.table_list= first);
+ select_lex.context.table_list= first;
+ select_lex.table_list.first= (byte*) first;
select_lex.table_list.elements++; //safety
}
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 369f0f44ba0..2f29ed4ebd4 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -306,7 +306,7 @@ protected:
*link_next, **link_prev; /* list of whole SELECT_LEX */
public:
- ulong options;
+ ulonglong options;
/*
result of this query can't be cached, bit field, can be :
UNCACHEABLE_DEPENDENT
@@ -459,6 +459,7 @@ public:
inline bool is_prepared() { return prepared; }
bool change_result(select_subselect *result, select_subselect *old_result);
void set_limit(st_select_lex *values);
+ void set_thd(THD *thd_arg) { thd= thd_arg; }
friend void lex_start(THD *thd, uchar *buf, uint length);
friend int subselect_union_engine::exec();
@@ -472,14 +473,16 @@ class st_select_lex: public st_select_lex_node
{
public:
Name_resolution_context context;
- char *db, *db1, *table1, *db2, *table2; /* For outer join using .. */
+ char *db;
Item *where, *having; /* WHERE & HAVING clauses */
Item *prep_where; /* saved WHERE clause for prepared statement processing */
/* point on lex in which it was created, used in view subquery detection */
st_lex *parent_lex;
enum olap_type olap;
- SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */
- List<Item> item_list; /* list of fields & expressions */
+ /* FROM clause - points to the beginning of the TABLE_LIST::next_local list. */
+ SQL_LIST table_list;
+ SQL_LIST group_list; /* GROUP BY clause. */
+ List<Item> item_list; /* list of fields & expressions */
List<String> interval_list, use_index, *use_index_ptr,
ignore_index, *ignore_index_ptr;
/*
@@ -492,7 +495,12 @@ public:
List<TABLE_LIST> top_join_list; /* join list of the top level */
List<TABLE_LIST> *join_list; /* list for the currently parsed join */
TABLE_LIST *embedding; /* table embedding to the above list */
- TABLE_LIST *leaf_tables; /* list of leaves in join table tree */
+ /*
+ Beginning of the list of leaves in a FROM clause, where the leaves
+ inlcude all base tables including view tables. The tables are connected
+ by TABLE_LIST::next_leaf, so leaf_tables points to the left-most leaf.
+ */
+ TABLE_LIST *leaf_tables;
const char *type; /* type of select for EXPLAIN */
SQL_LIST order_list; /* ORDER clause */
@@ -594,7 +602,6 @@ public:
bool init_nested_join(THD *thd);
TABLE_LIST *end_nested_join(THD *thd);
TABLE_LIST *nest_last_join(THD *thd);
- void save_names_for_using_list(TABLE_LIST *tab1, TABLE_LIST *tab2);
void add_joined_table(TABLE_LIST *table);
TABLE_LIST *convert_right_join();
List<Item>* get_item_list();
@@ -751,6 +758,21 @@ typedef struct st_lex
List<set_var_base> var_list;
List<Item_param> param_list;
List<LEX_STRING> view_list; // view list (list of field names in view)
+ /*
+ A stack of name resolution contexts for the query. This stack is used
+ at parse time to set local name resolution contexts for various parts
+ of a query. For example, in a JOIN ... ON (some_condition) clause the
+ Items in 'some_condition' must be resolved only against the operands
+ of the the join, and not against the whole clause. Similarly, Items in
+ subqueries should be resolved against the subqueries (and outer queries).
+ The stack is used in the following way: when the parser detects that
+ all Items in some clause need a local context, it creates a new context
+ and pushes it on the stack. All newly created Items always store the
+ top-most context in the stack. Once the parser leaves the clause that
+ required a local context, the parser pops the top-most context.
+ */
+ List<Name_resolution_context> context_stack;
+
SQL_LIST proc_list, auxilliary_table_list, save_list;
create_field *last_field;
udf_func udf;
@@ -942,6 +964,21 @@ typedef struct st_lex
return ( query_tables_own_last ? *query_tables_own_last : 0);
}
void cleanup_after_one_table_open();
+
+ void push_context(Name_resolution_context *context)
+ {
+ context_stack.push_front(context);
+ }
+
+ void pop_context()
+ {
+ context_stack.pop();
+ }
+
+ Name_resolution_context *current_context()
+ {
+ return context_stack.head();
+ }
} LEX;
struct st_lex_local: public st_lex
diff --git a/sql/sql_list.h b/sql/sql_list.h
index 09c01931c38..e4a34cc0aa1 100644
--- a/sql/sql_list.h
+++ b/sql/sql_list.h
@@ -426,9 +426,14 @@ struct ilink
template <class T> class I_List_iterator;
+/*
+ WARNING: copy constructor of this class does not create a usable
+ copy, as its members may point at each other.
+*/
+
class base_ilist
{
- public:
+public:
struct ilink *first,last;
inline void empty() { first= &last; last.prev= &first; }
base_ilist() { empty(); }
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 0090f956521..8eec970b4df 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -150,6 +150,7 @@ bool mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
if (open_and_lock_tables(thd, table_list))
DBUG_RETURN(TRUE);
if (setup_tables(thd, &thd->lex->select_lex.context,
+ &thd->lex->select_lex.top_join_list,
table_list, &unused_conds,
&thd->lex->select_lex.leaf_tables, FALSE))
DBUG_RETURN(-1);
diff --git a/sql/sql_olap.cc b/sql/sql_olap.cc
index 71e8fe4149f..b457ff5a6d6 100644
--- a/sql/sql_olap.cc
+++ b/sql/sql_olap.cc
@@ -153,7 +153,7 @@ int handle_olaps(LEX *lex, SELECT_LEX *select_lex)
List<Item> all_fields(select_lex->item_list);
- if (setup_tables(lex->thd, &select_lex->context,
+ if (setup_tables(lex->thd, &select_lex->context, &select_lex->top_join_list,
(TABLE_LIST *)select_lex->table_list.first
&select_lex->where, &select_lex->leaf_tables, FALSE) ||
setup_fields(lex->thd, 0, select_lex->item_list, 1, &all_fields,1) ||
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 341cc84e90c..9fbbee7d8d3 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -242,7 +242,8 @@ end:
/*
- Check if user exist and password supplied is correct.
+ Check if user exist and password supplied is correct.
+
SYNOPSIS
check_user()
thd thread handle, thd->{host,user,ip} are used
@@ -277,9 +278,13 @@ int check_user(THD *thd, enum enum_server_command command,
/* Change database if necessary */
if (db && db[0])
{
+ /*
+ thd->db is saved in caller and needs to be freed by caller if this
+ function returns 0
+ */
thd->db= 0;
thd->db_length= 0;
- if (mysql_change_db(thd, db))
+ if (mysql_change_db(thd, db, FALSE))
{
/* Send the error to the client */
net_send_error(thd);
@@ -288,8 +293,7 @@ int check_user(THD *thd, enum enum_server_command command,
DBUG_RETURN(-1);
}
}
- else
- send_ok(thd);
+ send_ok(thd);
DBUG_RETURN(0);
#else
@@ -414,7 +418,7 @@ int check_user(THD *thd, enum enum_server_command command,
/* Change database if necessary */
if (db && db[0])
{
- if (mysql_change_db(thd, db))
+ if (mysql_change_db(thd, db, FALSE))
{
/* Send error to the client */
net_send_error(thd);
@@ -423,8 +427,7 @@ int check_user(THD *thd, enum enum_server_command command,
DBUG_RETURN(-1);
}
}
- else
- send_ok(thd);
+ send_ok(thd);
thd->password= test(passwd_len); // remember for error messages
/* Ready to handle queries */
DBUG_RETURN(0);
@@ -1105,11 +1108,11 @@ pthread_handler_decl(handle_one_connection,arg)
execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect);
if (thd->query_error)
thd->killed= THD::KILL_CONNECTION;
+ thd->proc_info=0;
+ thd->set_time();
+ thd->init_for_queries();
}
- thd->proc_info=0;
- thd->set_time();
- thd->init_for_queries();
while (!net->error && net->vio != 0 &&
!(thd->killed == THD::KILL_CONNECTION))
{
@@ -1219,7 +1222,8 @@ extern "C" pthread_handler_decl(handle_bootstrap,arg)
length--;
buff[length]=0;
thd->query_length=length;
- 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+QUERY_CACHE_FLAGS_SIZE);
thd->query[length] = '\0';
/*
We don't need to obtain LOCK_thread_count here because in bootstrap
@@ -1468,6 +1472,7 @@ bool do_command(THD *thd)
/*
Perform one connection-level (COM_XXXX) command.
+
SYNOPSIS
dispatch_command()
thd connection handle
@@ -1518,8 +1523,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
&LOCK_status);
thd->convert_string(&tmp, system_charset_info,
packet, strlen(packet), thd->charset());
- if (!mysql_change_db(thd, tmp.str))
+ if (!mysql_change_db(thd, tmp.str, FALSE))
+ {
mysql_log.write(thd,command,"%s",thd->db);
+ send_ok(thd);
+ }
break;
}
#ifdef HAVE_REPLICATION
@@ -1867,17 +1875,18 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
}
#endif
case COM_REFRESH:
- {
- statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH],
- &LOCK_status);
- ulong options= (ulong) (uchar) packet[0];
- if (check_global_access(thd,RELOAD_ACL))
- break;
- mysql_log.write(thd,command,NullS);
- if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, NULL))
- send_ok(thd);
+ {
+ bool not_used;
+ statistic_increment(thd->status_var.com_stat[SQLCOM_FLUSH],
+ &LOCK_status);
+ ulong options= (ulong) (uchar) packet[0];
+ if (check_global_access(thd,RELOAD_ACL))
break;
- }
+ mysql_log.write(thd,command,NullS);
+ if (!reload_acl_and_cache(thd, options, (TABLE_LIST*) 0, &not_used))
+ send_ok(thd);
+ break;
+ }
#ifndef EMBEDDED_LIBRARY
case COM_SHUTDOWN:
{
@@ -2021,7 +2030,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
*/
bzero(&thd->transaction.stmt, sizeof(thd->transaction.stmt));
if (!thd->active_transaction())
- thd->transaction.xid.null();
+ thd->transaction.xid_state.xid.null();
/* report error issued during command execution */
if (thd->killed_errno() && !thd->net.report_error)
@@ -2047,7 +2056,17 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
void log_slow_statement(THD *thd)
{
- time_t start_of_query=thd->start_time;
+ time_t start_of_query;
+
+ /*
+ The following should never be true with our current code base,
+ but better to keep this here so we don't accidently try to log a
+ statement in a trigger or stored function
+ */
+ if (unlikely(thd->in_sub_stmt))
+ return; // Don't set time for sub stmt
+
+ start_of_query= thd->start_time;
thd->end_time(); // Set start time
/*
@@ -2138,6 +2157,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
{
TABLE_LIST **query_tables_last= lex->query_tables_last;
sel= new SELECT_LEX();
+ /* 'parent_lex' is used in init_query() so it must be before it. */
+ sel->parent_lex= lex;
sel->init_query();
if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
(List<String> *) 0, (List<String> *) 0))
@@ -2946,8 +2967,8 @@ end_with_restore_list:
*/
if (thd->locked_tables || thd->active_transaction())
{
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION, ER(ER_LOCK_OR_ACTIVE_TRANSACTION),
- MYF(0));
+ my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
+ ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
{
@@ -3241,19 +3262,26 @@ end_with_restore_list:
if (!(res= open_and_lock_tables(thd, all_tables)))
{
/* Skip first table, which is the table we are inserting in */
- select_lex->table_list.first= (byte*)first_table->next_local;
-
+ TABLE_LIST *second_table= first_table->next_local;
+ select_lex->table_list.first= (byte*) second_table;
+ select_lex->context.table_list=
+ select_lex->context.first_name_resolution_table= second_table;
res= mysql_insert_select_prepare(thd);
- lex->select_lex.context.table_list= first_table->next_local;
if (!res && (result= new select_insert(first_table, first_table->table,
&lex->field_list,
&lex->update_list,
&lex->value_list,
lex->duplicates, lex->ignore)))
{
- /* Skip first table, which is the table we are inserting in */
- select_lex->context.table_list= first_table->next_local;
-
+ /*
+ Skip first table, which is the table we are inserting in.
+ Below we set context.table_list again because the call above to
+ mysql_insert_select_prepare() calls resolve_in_table_list_only(),
+ which in turn resets context.table_list and
+ context.first_name_resolution_table.
+ */
+ select_lex->context.table_list=
+ select_lex->context.first_name_resolution_table= second_table;
res= handle_select(thd, lex, result, OPTION_SETUP_TABLES_DONE);
delete result;
}
@@ -3322,7 +3350,7 @@ end_with_restore_list:
if ((res= mysql_multi_delete_prepare(thd)))
goto error;
- if (!thd->is_fatal_error && (result= new multi_delete(thd,aux_tables,
+ if (!thd->is_fatal_error && (result= new multi_delete(aux_tables,
lex->table_count)))
{
res= mysql_select(thd, &select_lex->ref_pointer_array,
@@ -3411,7 +3439,8 @@ end_with_restore_list:
}
#endif
case SQLCOM_CHANGE_DB:
- mysql_change_db(thd,select_lex->db);
+ if (!mysql_change_db(thd,select_lex->db,FALSE))
+ send_ok(thd);
break;
case SQLCOM_LOAD:
@@ -3642,7 +3671,7 @@ end_with_restore_list:
if (!(res = mysql_create_function(thd, &lex->udf)))
send_ok(thd);
#else
- net_printf_error(thd, ER_CANT_OPEN_LIBRARY, lex->udf.dl, 0, "feature disabled");
+ my_error(ER_CANT_OPEN_LIBRARY, MYF(0), lex->udf.dl, 0, "feature disabled");
res= TRUE;
#endif
break;
@@ -3826,13 +3855,13 @@ end_with_restore_list:
lex->no_write_to_binlog= 1;
case SQLCOM_FLUSH:
{
+ bool write_to_binlog;
if (check_global_access(thd,RELOAD_ACL) || check_db_used(thd, all_tables))
goto error;
/*
reload_acl_and_cache() will tell us if we are allowed to write to the
binlog or not.
*/
- bool write_to_binlog;
if (!reload_acl_and_cache(thd, lex->type, first_table, &write_to_binlog))
{
/*
@@ -4064,8 +4093,8 @@ end_with_restore_list:
name= thd->strdup(name);
db= thd->strmake(lex->sphead->m_db.str, lex->sphead->m_db.length);
res= (result= lex->sphead->create(thd));
- switch (result) {
- case SP_OK:
+ if (result == SP_OK)
+ {
lex->unit.cleanup();
delete lex->sphead;
lex->sphead= 0;
@@ -4085,33 +4114,26 @@ end_with_restore_list:
}
#endif
send_ok(thd);
- break;
- case SP_WRITE_ROW_FAILED:
- my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
- lex->unit.cleanup();
- delete lex->sphead;
- lex->sphead= 0;
- goto error;
- case SP_NO_DB_ERROR:
- my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
- lex->unit.cleanup();
- delete lex->sphead;
- lex->sphead= 0;
- goto error;
- case SP_BAD_IDENTIFIER:
- my_error(ER_TOO_LONG_IDENT, MYF(0), name);
- lex->unit.cleanup();
- delete lex->sphead;
- lex->sphead= 0;
- goto error;
- case SP_BODY_TOO_LONG:
- my_error(ER_TOO_LONG_BODY, MYF(0), name);
- lex->unit.cleanup();
- delete lex->sphead;
- lex->sphead= 0;
- goto error;
- default:
- my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
+ }
+ else
+ {
+ switch (result) {
+ case SP_WRITE_ROW_FAILED:
+ my_error(ER_SP_ALREADY_EXISTS, MYF(0), SP_TYPE_STRING(lex), name);
+ break;
+ case SP_NO_DB_ERROR:
+ my_error(ER_BAD_DB_ERROR, MYF(0), lex->sphead->m_db.str);
+ break;
+ case SP_BAD_IDENTIFIER:
+ my_error(ER_TOO_LONG_IDENT, MYF(0), name);
+ break;
+ case SP_BODY_TOO_LONG:
+ my_error(ER_TOO_LONG_BODY, MYF(0), name);
+ break;
+ default:
+ my_error(ER_SP_STORE_FAILED, MYF(0), SP_TYPE_STRING(lex), name);
+ break;
+ }
lex->unit.cleanup();
delete lex->sphead;
lex->sphead= 0;
@@ -4506,14 +4528,15 @@ end_with_restore_list:
break;
}
case SQLCOM_XA_START:
- if (thd->transaction.xa_state == XA_IDLE && thd->lex->xa_opt == XA_RESUME)
+ if (thd->transaction.xid_state.xa_state == XA_IDLE &&
+ thd->lex->xa_opt == XA_RESUME)
{
- if (! thd->transaction.xid.eq(thd->lex->xid))
+ if (! thd->transaction.xid_state.xid.eq(thd->lex->xid))
{
my_error(ER_XAER_NOTA, MYF(0));
break;
}
- thd->transaction.xa_state=XA_ACTIVE;
+ thd->transaction.xid_state.xa_state=XA_ACTIVE;
send_ok(thd);
break;
}
@@ -4522,10 +4545,10 @@ end_with_restore_list:
my_error(ER_XAER_INVAL, MYF(0));
break;
}
- if (thd->transaction.xa_state != XA_NOTR)
+ if (thd->transaction.xid_state.xa_state != XA_NOTR)
{
my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xa_state]);
+ xa_state_names[thd->transaction.xid_state.xa_state]);
break;
}
if (thd->active_transaction() || thd->locked_tables)
@@ -4533,9 +4556,15 @@ end_with_restore_list:
my_error(ER_XAER_OUTSIDE, MYF(0));
break;
}
- DBUG_ASSERT(thd->transaction.xid.is_null());
- thd->transaction.xa_state=XA_ACTIVE;
- thd->transaction.xid.set(thd->lex->xid);
+ if (xid_cache_search(thd->lex->xid))
+ {
+ my_error(ER_XAER_DUPID, MYF(0));
+ break;
+ }
+ DBUG_ASSERT(thd->transaction.xid_state.xid.is_null());
+ thd->transaction.xid_state.xa_state=XA_ACTIVE;
+ thd->transaction.xid_state.xid.set(thd->lex->xid);
+ xid_cache_insert(&thd->transaction.xid_state);
thd->options= ((thd->options & (ulong) ~(OPTION_STATUS_NO_TRANS_UPDATE)) |
OPTION_BEGIN);
thd->server_status|= SERVER_STATUS_IN_TRANS;
@@ -4548,28 +4577,28 @@ end_with_restore_list:
my_error(ER_XAER_INVAL, MYF(0));
break;
}
- if (thd->transaction.xa_state != XA_ACTIVE)
+ if (thd->transaction.xid_state.xa_state != XA_ACTIVE)
{
my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xa_state]);
+ xa_state_names[thd->transaction.xid_state.xa_state]);
break;
}
- if (!thd->transaction.xid.eq(thd->lex->xid))
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
{
my_error(ER_XAER_NOTA, MYF(0));
break;
}
- thd->transaction.xa_state=XA_IDLE;
+ thd->transaction.xid_state.xa_state=XA_IDLE;
send_ok(thd);
break;
case SQLCOM_XA_PREPARE:
- if (thd->transaction.xa_state != XA_IDLE)
+ if (thd->transaction.xid_state.xa_state != XA_IDLE)
{
my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xa_state]);
+ xa_state_names[thd->transaction.xid_state.xa_state]);
break;
}
- if (!thd->transaction.xid.eq(thd->lex->xid))
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
{
my_error(ER_XAER_NOTA, MYF(0));
break;
@@ -4577,22 +4606,28 @@ end_with_restore_list:
if (ha_prepare(thd))
{
my_error(ER_XA_RBROLLBACK, MYF(0));
- thd->transaction.xa_state=XA_NOTR;
+ xid_cache_delete(&thd->transaction.xid_state);
+ thd->transaction.xid_state.xa_state=XA_NOTR;
break;
}
- thd->transaction.xa_state=XA_PREPARED;
+ thd->transaction.xid_state.xa_state=XA_PREPARED;
send_ok(thd);
break;
case SQLCOM_XA_COMMIT:
- if (!thd->transaction.xid.eq(thd->lex->xid))
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
{
- if (!(res= !ha_commit_or_rollback_by_xid(thd->lex->xid, 1)))
+ XID_STATE *xs=xid_cache_search(thd->lex->xid);
+ if (!xs || xs->in_thd)
my_error(ER_XAER_NOTA, MYF(0));
else
+ {
+ ha_commit_or_rollback_by_xid(thd->lex->xid, 1);
+ xid_cache_delete(xs);
send_ok(thd);
+ }
break;
}
- if (thd->transaction.xa_state == XA_IDLE &&
+ if (thd->transaction.xid_state.xa_state == XA_IDLE &&
thd->lex->xa_opt == XA_ONE_PHASE)
{
int r;
@@ -4601,7 +4636,7 @@ end_with_restore_list:
else
send_ok(thd);
}
- else if (thd->transaction.xa_state == XA_PREPARED &&
+ else if (thd->transaction.xid_state.xa_state == XA_PREPARED &&
thd->lex->xa_opt == XA_NONE)
{
if (wait_if_global_read_lock(thd, 0, 0))
@@ -4621,27 +4656,33 @@ end_with_restore_list:
else
{
my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xa_state]);
+ xa_state_names[thd->transaction.xid_state.xa_state]);
break;
}
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- thd->transaction.xa_state=XA_NOTR;
+ xid_cache_delete(&thd->transaction.xid_state);
+ thd->transaction.xid_state.xa_state=XA_NOTR;
break;
case SQLCOM_XA_ROLLBACK:
- if (!thd->transaction.xid.eq(thd->lex->xid))
+ if (!thd->transaction.xid_state.xid.eq(thd->lex->xid))
{
- if (!(res= !ha_commit_or_rollback_by_xid(thd->lex->xid, 0)))
+ XID_STATE *xs=xid_cache_search(thd->lex->xid);
+ if (!xs || xs->in_thd)
my_error(ER_XAER_NOTA, MYF(0));
else
+ {
+ ha_commit_or_rollback_by_xid(thd->lex->xid, 0);
+ xid_cache_delete(xs);
send_ok(thd);
+ }
break;
}
- if (thd->transaction.xa_state != XA_IDLE &&
- thd->transaction.xa_state != XA_PREPARED)
+ if (thd->transaction.xid_state.xa_state != XA_IDLE &&
+ thd->transaction.xid_state.xa_state != XA_PREPARED)
{
my_error(ER_XAER_RMFAIL, MYF(0),
- xa_state_names[thd->transaction.xa_state]);
+ xa_state_names[thd->transaction.xid_state.xa_state]);
break;
}
if (ha_rollback(thd))
@@ -4650,7 +4691,8 @@ end_with_restore_list:
send_ok(thd);
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
thd->server_status&= ~SERVER_STATUS_IN_TRANS;
- thd->transaction.xa_state=XA_NOTR;
+ xid_cache_delete(&thd->transaction.xid_state);
+ thd->transaction.xid_state.xa_state=XA_NOTR;
break;
case SQLCOM_XA_RECOVER:
res= mysql_xa_recover(thd);
@@ -5167,17 +5209,21 @@ void mysql_reset_thd_for_next_command(THD *thd)
DBUG_ENTER("mysql_reset_thd_for_next_command");
thd->free_list= 0;
thd->select_number= 1;
- thd->total_warn_count=0; // Warnings for this query
thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
- thd->sent_row_count= thd->examined_row_count= 0;
- thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0;
+ thd->is_fatal_error= thd->time_zone_used= 0;
thd->server_status&= ~ (SERVER_MORE_RESULTS_EXISTS |
SERVER_QUERY_NO_INDEX_USED |
SERVER_QUERY_NO_GOOD_INDEX_USED);
thd->tmp_table_used= 0;
- if (opt_bin_log)
- reset_dynamic(&thd->user_var_events);
- thd->clear_error();
+ if (!thd->in_sub_stmt)
+ {
+ if (opt_bin_log)
+ reset_dynamic(&thd->user_var_events);
+ thd->clear_error();
+ thd->total_warn_count=0; // Warnings for this query
+ thd->rand_used= 0;
+ thd->sent_row_count= thd->examined_row_count= 0;
+ }
DBUG_VOID_RETURN;
}
@@ -5206,9 +5252,9 @@ mysql_new_select(LEX *lex, bool move_down)
if (!(select_lex= new (thd->mem_root) SELECT_LEX()))
DBUG_RETURN(1);
select_lex->select_number= ++thd->select_number;
+ select_lex->parent_lex= lex; /* Used in init_query. */
select_lex->init_query();
select_lex->init_select();
- select_lex->parent_lex= lex;
/*
Don't evaluate this subquery during statement prepare even if
it's a constant one. The flag is switched off in the end of
@@ -5266,6 +5312,7 @@ mysql_new_select(LEX *lex, bool move_down)
fake->include_standalone(unit,
(SELECT_LEX_NODE**)&unit->fake_select_lex);
fake->select_number= INT_MAX;
+ fake->parent_lex= lex; /* Used in init_query. */
fake->make_empty_select();
fake->linkage= GLOBAL_OPTIONS_TYPE;
fake->select_limit= 0;
@@ -5274,6 +5321,11 @@ mysql_new_select(LEX *lex, bool move_down)
/* allow item list resolving in fake select for ORDER BY */
fake->context.resolve_in_select_list= TRUE;
fake->context.select_lex= fake;
+ /*
+ Remove the name resolution context of the fake select from the
+ context stack.
+ */
+ lex->pop_context();
}
select_lex->context.outer_context= outer_context;
}
@@ -5322,10 +5374,12 @@ void create_select_for_variable(const char *var_name)
We set the name of Item to @@session.var_name because that then is used
as the column name in the output.
*/
- var= get_system_var(thd, OPT_SESSION, tmp, null_lex_string);
- end= strxmov(buff, "@@session.", var_name, NullS);
- var->set_name(buff, end-buff, system_charset_info);
- add_item_to_list(thd, var);
+ if ((var= get_system_var(thd, OPT_SESSION, tmp, null_lex_string)))
+ {
+ end= strxmov(buff, "@@session.", var_name, NullS);
+ var->set_name(buff, end-buff, system_charset_info);
+ add_item_to_list(thd, var);
+ }
DBUG_VOID_RETURN;
}
@@ -5351,11 +5405,12 @@ void mysql_init_multi_delete(LEX *lex)
void mysql_parse(THD *thd, char *inBuf, uint length)
{
DBUG_ENTER("mysql_parse");
-
mysql_init_query(thd, (uchar*) inBuf, length);
if (query_cache_send_result_to_client(thd, inBuf, length) <= 0)
{
LEX *lex= thd->lex;
+ sp_cache_flush_obsolete(&thd->sp_proc_cache);
+ sp_cache_flush_obsolete(&thd->sp_func_cache);
if (!yyparse((void *)thd) && ! thd->is_fatal_error)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -5823,7 +5878,7 @@ new_create_field(THD *thd, char *field_name, enum_field_types type,
new_field->length= 1;
if (new_field->length > MAX_BIT_FIELD_LENGTH)
{
- my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0), field_name,
+ my_error(ER_TOO_BIG_DISPLAYWIDTH, MYF(0), field_name,
MAX_BIT_FIELD_LENGTH);
DBUG_RETURN(NULL);
}
@@ -5842,7 +5897,10 @@ new_create_field(THD *thd, char *field_name, enum_field_types type,
type != MYSQL_TYPE_STRING &&
type != MYSQL_TYPE_VARCHAR && type != FIELD_TYPE_GEOMETRY)))
{
- my_error(ER_TOO_BIG_FIELDLENGTH, MYF(0),
+ my_error((type == MYSQL_TYPE_VAR_STRING || type == MYSQL_TYPE_VARCHAR ||
+ type == MYSQL_TYPE_STRING) ? ER_TOO_BIG_FIELDLENGTH :
+ ER_TOO_BIG_DISPLAYWIDTH,
+ MYF(0),
field_name, max_field_charlength); /* purecov: inspected */
DBUG_RETURN(NULL);
}
@@ -5961,9 +6019,11 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
LEX_STRING *option)
{
register TABLE_LIST *ptr;
+ TABLE_LIST *previous_table_ref; /* The table preceding the current one. */
char *alias_str;
LEX *lex= thd->lex;
DBUG_ENTER("add_table_to_list");
+ LINT_INIT(previous_table_ref);
if (!table)
DBUG_RETURN(0); // End of memory
@@ -6056,8 +6116,34 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
}
}
}
- /* Link table in local list (list for current select) */
+ /* Store the table reference preceding the current one. */
+ if (table_list.elements > 0)
+ {
+ /*
+ table_list.next points to the last inserted TABLE_LIST->next_local'
+ element
+ */
+ previous_table_ref= (TABLE_LIST*) (table_list.next -
+ offsetof(TABLE_LIST, next_local));
+ DBUG_ASSERT(previous_table_ref);
+ /*
+ Set next_name_resolution_table of the previous table reference to point
+ to the current table reference. In effect the list
+ TABLE_LIST::next_name_resolution_table coincides with
+ TABLE_LIST::next_local. Later this may be changed in
+ store_top_level_join_columns() for NATURAL/USING joins.
+ */
+ previous_table_ref->next_name_resolution_table= ptr;
+ }
+
+ /*
+ Link the current table reference in a local list (list for current select).
+ Notice that as a side effect here we set the next_local field of the
+ previous table reference to 'ptr'. Here we also add one element to the
+ list 'table_list'.
+ */
table_list.link_in_list((byte*) ptr, (byte**) &ptr->next_local);
+ ptr->next_name_resolution_table= NULL;
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
DBUG_RETURN(ptr);
@@ -6091,10 +6177,12 @@ bool st_select_lex::init_nested_join(THD *thd)
NESTED_JOIN *nested_join;
DBUG_ENTER("init_nested_join");
- if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) ||
- !(nested_join= ptr->nested_join=
- (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
+ if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
+ sizeof(NESTED_JOIN))))
DBUG_RETURN(1);
+ nested_join= ptr->nested_join=
+ ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
+
join_list->push_front(ptr);
ptr->embedding= embedding;
ptr->join_list= join_list;
@@ -6162,30 +6250,48 @@ TABLE_LIST *st_select_lex::end_nested_join(THD *thd)
The function nest last join operation as if it was enclosed in braces.
RETURN VALUE
- Pointer to TABLE_LIST element created for the new nested join, if success
- 0, otherwise
+ 0 Error
+ # Pointer to TABLE_LIST element created for the new nested join
+
*/
TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
{
TABLE_LIST *ptr;
NESTED_JOIN *nested_join;
+ List<TABLE_LIST> *embedded_list;
DBUG_ENTER("nest_last_join");
- if (!(ptr = (TABLE_LIST *) thd->calloc(sizeof(TABLE_LIST))) ||
- !(nested_join= ptr->nested_join=
- (NESTED_JOIN *) thd->calloc(sizeof(NESTED_JOIN))))
+ if (!(ptr= (TABLE_LIST*) thd->calloc(ALIGN_SIZE(sizeof(TABLE_LIST))+
+ sizeof(NESTED_JOIN))))
DBUG_RETURN(0);
+ nested_join= ptr->nested_join=
+ ((NESTED_JOIN*) ((byte*) ptr + ALIGN_SIZE(sizeof(TABLE_LIST))));
+
ptr->embedding= embedding;
ptr->join_list= join_list;
- List<TABLE_LIST> *embedded_list= &nested_join->join_list;
+ embedded_list= &nested_join->join_list;
embedded_list->empty();
- for (int i=0; i < 2; i++)
+
+ for (uint i=0; i < 2; i++)
{
TABLE_LIST *table= join_list->pop();
table->join_list= embedded_list;
table->embedding= ptr;
embedded_list->push_back(table);
+ if (table->natural_join)
+ {
+ ptr->is_natural_join= TRUE;
+ /*
+ If this is a JOIN ... USING, move the list of joined fields to the
+ table reference that describes the join.
+ */
+ if (table->join_using_fields)
+ {
+ ptr->join_using_fields= table->join_using_fields;
+ table->join_using_fields= NULL;
+ }
+ }
}
join_list->push_front(ptr);
nested_join->used_tables= nested_join->not_null_tables= (table_map) 0;
@@ -6194,44 +6300,6 @@ TABLE_LIST *st_select_lex::nest_last_join(THD *thd)
/*
- Save names for a join with using clause
-
- SYNOPSIS
- save_names_for_using_list
- tab1 left table in join
- tab2 right table in join
-
- DESCRIPTION
- The function saves the full names of the tables in st_select_lex
- to be able to build later an on expression to replace the using clause.
-
- RETURN VALUE
- None
-*/
-
-void st_select_lex::save_names_for_using_list(TABLE_LIST *tab1,
- TABLE_LIST *tab2)
-{
- while (tab1->nested_join)
- {
- tab1= tab1->nested_join->join_list.head();
- }
- db1= tab1->db;
- table1= tab1->alias;
- while (tab2->nested_join)
- {
- TABLE_LIST *next;
- List_iterator_fast<TABLE_LIST> it(tab2->nested_join->join_list);
- tab2= it++;
- while ((next= it++))
- tab2= next;
- }
- db2= tab2->db;
- table2= tab2->alias;
-}
-
-
-/*
Add a table to the current join list
SYNOPSIS
@@ -6334,16 +6402,70 @@ void st_select_lex::set_lock_for_tables(thr_lock_type lock_type)
}
-void add_join_on(TABLE_LIST *b,Item *expr)
+/*
+ Create a new name resolution context for a JOIN ... ON clause.
+
+ SYNOPSIS
+ make_join_on_context()
+ thd pointer to current thread
+ left_op lefto operand of the JOIN
+ right_op rigth operand of the JOIN
+
+ DESCRIPTION
+ Create a new name resolution context for a JOIN ... ON clause,
+ and set the first and last leaves of the list of table references
+ to be used for name resolution.
+
+ RETURN
+ A new context if all is OK
+ NULL - if a memory allocation error occured
+*/
+
+Name_resolution_context *
+make_join_on_context(THD *thd, TABLE_LIST *left_op, TABLE_LIST *right_op)
+{
+ Name_resolution_context *on_context;
+ if (!(on_context= new (thd->mem_root) Name_resolution_context))
+ return NULL;
+ on_context->init();
+ on_context->first_name_resolution_table=
+ left_op->first_leaf_for_name_resolution();
+ on_context->last_name_resolution_table=
+ right_op->last_leaf_for_name_resolution();
+ return on_context;
+}
+
+
+/*
+ Add an ON condition to the second operand of a JOIN ... ON.
+
+ SYNOPSIS
+ add_join_on
+ b the second operand of a JOIN ... ON
+ expr the condition to be added to the ON clause
+
+ DESCRIPTION
+ Add an ON condition to the right operand of a JOIN ... ON clause.
+
+ RETURN
+ FALSE if there was some error
+ TRUE if all is OK
+*/
+
+void add_join_on(TABLE_LIST *b, Item *expr)
{
if (expr)
{
if (!b->on_expr)
- b->on_expr=expr;
+ b->on_expr= expr;
else
{
- /* This only happens if you have both a right and left join */
- b->on_expr=new Item_cond_and(b->on_expr,expr);
+ /*
+ If called from the parser, this happens if you have both a
+ right and left join. If called later, it happens if we add more
+ than one condition to the ON clause.
+ */
+ b->on_expr= new Item_cond_and(b->on_expr,expr);
}
b->on_expr->top_level_item();
}
@@ -6351,46 +6473,67 @@ void add_join_on(TABLE_LIST *b,Item *expr)
/*
- Mark that we have a NATURAL JOIN between two tables
+ Mark that there is a NATURAL JOIN or JOIN ... USING between two
+ tables.
SYNOPSIS
add_join_natural()
- a Table to do normal join with
- b Do normal join with this table
-
+ a Left join argument
+ b Right join argument
+ using_fields Field names from USING clause
+
IMPLEMENTATION
- This function just marks that table b should be joined with a.
- The function setup_cond() will create in b->on_expr a list
- of equal condition between all fields of the same name.
-
+ This function marks that table b should be joined with a either via
+ a NATURAL JOIN or via JOIN ... USING. Both join types are special
+ cases of each other, so we treat them together. The function
+ setup_conds() creates a list of equal condition between all fields
+ of the same name for NATURAL JOIN or the fields in 'using_fields'
+ for JOIN ... USING. The list of equality conditions is stored
+ either in b->on_expr, or in JOIN::conds, depending on whether there
+ was an outer join.
+
+ EXAMPLE
SELECT * FROM t1 NATURAL LEFT JOIN t2
<=>
SELECT * FROM t1 LEFT JOIN t2 ON (t1.i=t2.i and t1.j=t2.j ... )
+
+ SELECT * FROM t1 NATURAL JOIN t2 WHERE <some_cond>
+ <=>
+ SELECT * FROM t1, t2 WHERE (t1.i=t2.i and t1.j=t2.j and <some_cond>)
+
+ SELECT * FROM t1 JOIN t2 USING(j) WHERE <some_cond>
+ <=>
+ SELECT * FROM t1, t2 WHERE (t1.j=t2.j and <some_cond>)
+
+ RETURN
+ None
*/
-void add_join_natural(TABLE_LIST *a,TABLE_LIST *b)
+void add_join_natural(TABLE_LIST *a, TABLE_LIST *b, List<String> *using_fields)
{
- b->natural_join=a;
+ b->natural_join= a;
+ b->join_using_fields= using_fields;
}
+
/*
Reload/resets privileges and the different caches.
SYNOPSIS
reload_acl_and_cache()
- thd Thread handler
+ thd Thread handler (can be NULL!)
options What should be reset/reloaded (tables, privileges,
slave...)
tables Tables to flush (if any)
write_to_binlog Depending on 'options', it may be very bad to write the
query to the binlog (e.g. FLUSH SLAVE); this is a
- pointer where, if it is not NULL, reload_acl_and_cache()
- will put 0 if it thinks we really should not write to
- the binlog. Otherwise it will put 1.
+ pointer where reload_acl_and_cache() will put 0 if
+ it thinks we really should not write to the binlog.
+ Otherwise it will put 1.
RETURN
0 ok
- !=0 error
+ !=0 error. thd->killed or thd->net.report_error is set
*/
bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
@@ -6399,6 +6542,13 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
bool result=0;
select_errors=0; /* Write if more errors */
bool tmp_write_to_binlog= 1;
+
+ if (thd && thd->in_sub_stmt)
+ {
+ my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
+ return 1;
+ }
+
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (options & REFRESH_GRANT)
{
@@ -6454,15 +6604,34 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
if ((options & REFRESH_READ_LOCK) && thd)
{
/*
+ We must not try to aspire a global read lock if we have a write
+ locked table. This would lead to a deadlock when trying to
+ reopen (and re-lock) the table after the flush.
+ */
+ if (thd->locked_tables)
+ {
+ THR_LOCK_DATA **lock_p= thd->locked_tables->locks;
+ THR_LOCK_DATA **end_p= lock_p + thd->locked_tables->lock_count;
+
+ for (; lock_p < end_p; lock_p++)
+ {
+ if ((*lock_p)->type == TL_WRITE)
+ {
+ my_error(ER_LOCK_OR_ACTIVE_TRANSACTION, MYF(0));
+ return 1;
+ }
+ }
+ }
+ /*
Writing to the binlog could cause deadlocks, as we don't log
UNLOCK TABLES
*/
tmp_write_to_binlog= 0;
if (lock_global_read_lock(thd))
- return 1;
+ return 1; // Killed
result=close_cached_tables(thd,(options & REFRESH_FAST) ? 0 : 1,
tables);
- if (make_global_read_lock_block_commit(thd))
+ if (make_global_read_lock_block_commit(thd)) // Killed
{
/* Don't leave things in a half-locked state */
unlock_global_read_lock(thd);
@@ -6484,7 +6653,10 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
{
tmp_write_to_binlog= 0;
if (reset_master(thd))
+ {
result=1;
+ thd->fatal_error(); // Ensure client get error
+ }
}
#endif
#ifdef OPENSSL
@@ -6506,8 +6678,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
#endif
if (options & REFRESH_USER_RESOURCES)
reset_mqh((LEX_USER *) NULL);
- if (write_to_binlog)
- *write_to_binlog= tmp_write_to_binlog;
+ *write_to_binlog= tmp_write_to_binlog;
return result;
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index a601e7c68b7..082ae783ca8 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -73,6 +73,7 @@ Long data handling:
#include <m_ctype.h> // for isspace()
#include "sp_head.h"
#include "sp.h"
+#include "sp_cache.h"
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
#include <mysql.h>
@@ -88,6 +89,7 @@ class Prepared_statement: public Statement
{
public:
THD *thd;
+ Protocol *protocol;
Item_param **param_array;
uint param_count;
uint last_errno;
@@ -549,7 +551,9 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
case MYSQL_TYPE_LONG_BLOB:
case MYSQL_TYPE_BLOB:
param->set_param_func= set_param_str;
- param->value.cs_info.character_set_client= &my_charset_bin;
+ param->value.cs_info.character_set_of_placeholder= &my_charset_bin;
+ param->value.cs_info.character_set_client=
+ thd->variables.character_set_client;
param->value.cs_info.final_character_set_of_str_value= &my_charset_bin;
param->item_type= Item::STRING_ITEM;
param->item_result_type= STRING_RESULT;
@@ -565,6 +569,7 @@ static void setup_one_conversion_function(THD *thd, Item_param *param,
CHARSET_INFO *tocs= thd->variables.collation_connection;
uint32 dummy_offset;
+ param->value.cs_info.character_set_of_placeholder= fromcs;
param->value.cs_info.character_set_client= fromcs;
/*
@@ -927,7 +932,7 @@ static bool mysql_test_insert(Prepared_statement *stmt,
If we would use locks, then we have to ensure we are not using
TL_WRITE_DELAYED as having two such locks can cause table corruption.
*/
- if (open_normal_and_derived_tables(thd, table_list))
+ if (open_normal_and_derived_tables(thd, table_list, 0))
goto error;
if ((values= its++))
@@ -1007,7 +1012,7 @@ static int mysql_test_update(Prepared_statement *stmt,
if (update_precheck(thd, table_list))
goto error;
- if (open_tables(thd, &table_list, &table_count))
+ if (open_tables(thd, &table_list, &table_count, 0))
goto error;
if (table_list->multitable_view)
@@ -1781,6 +1786,9 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
mysql_reset_errors(thd, 0);
lex= thd->lex;
+ sp_cache_flush_obsolete(&thd->sp_proc_cache);
+ sp_cache_flush_obsolete(&thd->sp_func_cache);
+
error= yyparse((void *)thd) || thd->is_fatal_error ||
thd->net.report_error || init_param_array(stmt);
lex->safe_to_cache_query= 0;
@@ -1850,6 +1858,13 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
SELECT_LEX *sl= lex->all_selects_list;
DBUG_ENTER("reinit_stmt_before_use");
+ /*
+ We have to update "thd" pointer in LEX, all its units and in LEX::result,
+ since statements which belong to trigger body are associated with TABLE
+ object and because of this can be used in different threads.
+ */
+ lex->thd= thd;
+
if (lex->empty_field_list_on_rset)
{
lex->empty_field_list_on_rset= 0;
@@ -1888,6 +1903,7 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
unit->types.empty();
/* for derived tables & PS (which can't be reset by Item_subquery) */
unit->reinit_exec_mechanism();
+ unit->set_thd(thd);
}
}
@@ -1926,7 +1942,10 @@ void reinit_stmt_before_use(THD *thd, LEX *lex)
lex->select_lex.leaf_tables= lex->leaf_tables_insert;
if (lex->result)
+ {
lex->result->cleanup();
+ lex->result->set_thd(thd);
+ }
DBUG_VOID_RETURN;
}
@@ -2021,6 +2040,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
DBUG_VOID_RETURN;
/* If lex->result is set, mysql_execute_command will use it */
stmt->lex->result= &cursor->result;
+ stmt->protocol= &cursor->protocol;
thd->lock_id= &cursor->lock_id;
}
}
@@ -2055,9 +2075,11 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
}
mysql_log.write(thd, thd->command, "[%lu] %s", stmt->id, thd->query);
- thd->protocol= &thd->protocol_prep; // Switch to binary protocol
+ thd->protocol= stmt->protocol; // Switch to binary protocol
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
+ sp_cache_flush_obsolete(&thd->sp_proc_cache);
+ sp_cache_flush_obsolete(&thd->sp_func_cache);
mysql_execute_command(thd);
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
@@ -2247,7 +2269,7 @@ void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), QUERY_PRIOR);
- thd->protocol= &thd->protocol_prep; // Switch to binary protocol
+ thd->protocol= stmt->protocol; // Switch to binary protocol
cursor->fetch(num_rows);
thd->protocol= &thd->protocol_simple; // Use normal protocol
@@ -2419,6 +2441,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
thd_arg->variables.query_alloc_block_size,
thd_arg->variables.query_prealloc_size),
thd(thd_arg),
+ protocol(&thd_arg->protocol_prep),
param_array(0),
param_count(0),
last_errno(0)
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 21277dfc1af..593f294ae0e 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -338,7 +338,7 @@ JOIN::prepare(Item ***rref_pointer_array,
/* Check that all tables, fields, conds and order are ok */
if ((!(select_options & OPTION_SETUP_TABLES_DONE) &&
- setup_tables(thd, &select_lex->context,
+ setup_tables(thd, &select_lex->context, join_list,
tables_list, &conds, &select_lex->leaf_tables,
FALSE)) ||
setup_wild(thd, tables_list, fields_list, &all_fields, wild_num) ||
@@ -1086,9 +1086,10 @@ JOIN::optimize()
order=0;
}
}
-
- if (thd->lex->subqueries)
+
+ if (select_lex->uncacheable && !is_top_level_join())
{
+ /* If this join belongs to an uncacheable subquery */
if (!(tmp_join= (JOIN*)thd->alloc(sizeof(JOIN))))
DBUG_RETURN(-1);
error= 0; // Ensure that tmp_join.error= 0
@@ -1282,7 +1283,15 @@ JOIN::exec()
if (need_tmp)
{
if (tmp_join)
+ {
+ /*
+ We are in a non cacheable sub query. Get the saved join structure
+ after optimization.
+ (curr_join may have been modified during last exection and we need
+ to reset it)
+ */
curr_join= tmp_join;
+ }
curr_tmp_table= exec_tmp_table1;
/* Copy data to the temporary table */
@@ -1576,7 +1585,8 @@ JOIN::exec()
curr_join->tmp_having= make_cond_for_table(curr_join->tmp_having,
~ (table_map) 0,
~used_tables);
- DBUG_EXECUTE("where",print_where(conds,"having after sort"););
+ DBUG_EXECUTE("where",print_where(curr_join->tmp_having,
+ "having after sort"););
}
}
{
@@ -1647,9 +1657,7 @@ JOIN::exec()
curr_join->fields= curr_fields_list;
curr_join->procedure= procedure;
- if (unit == &thd->lex->unit &&
- (unit->fake_select_lex == 0 || select_lex == unit->fake_select_lex) &&
- thd->cursor && tables != const_tables)
+ if (is_top_level_join() && thd->cursor && tables != const_tables)
{
/*
We are here if this is JOIN::exec for the last select of the main unit
@@ -1726,6 +1734,7 @@ JOIN::destroy()
Cursor::Cursor(THD *thd)
:Query_arena(&main_mem_root, INITIALIZED),
join(0), unit(0),
+ protocol(thd),
close_at_commit(FALSE)
{
/* We will overwrite it at open anyway. */
@@ -8142,7 +8151,7 @@ Field *create_tmp_field(THD *thd, TABLE *table,Item *item, Item::Type type,
TABLE *
create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields,
- ulong select_options, ha_rows rows_limit,
+ ulonglong select_options, ha_rows rows_limit,
char *table_alias)
{
TABLE *table;
@@ -8400,7 +8409,7 @@ create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
/* If result table is small; use a heap */
if (blob_count || using_unique_constraint ||
(select_options & (OPTION_BIG_TABLES | SELECT_SMALL_RESULT)) ==
- OPTION_BIG_TABLES)
+ OPTION_BIG_TABLES || (select_options & TMP_TABLE_FORCE_MYISAM))
{
table->file=get_new_handler(table,table->s->db_type= DB_TYPE_MYISAM);
if (group &&
@@ -11934,13 +11943,14 @@ cp_buffer_from_ref(THD *thd, TABLE_REF *ref)
SYNOPSIS
find_order_in_list()
- thd Pointer to current thread structure
- ref_pointer_array All select, group and order by fields
- tables List of tables to search in (usually FROM clause)
- order Column reference to be resolved
- fields List of fields to search in (usually SELECT list)
- all_fields All select, group and order by fields
- is_group_field True if order is a GROUP field, false if ORDER by field
+ thd [in] Pointer to current thread structure
+ ref_pointer_array [in/out] All select, group and order by fields
+ tables [in] List of tables to search in (usually FROM clause)
+ order [in] Column reference to be resolved
+ fields [in] List of fields to search in (usually SELECT list)
+ all_fields [in/out] All select, group and order by fields
+ is_group_field [in] True if order is a GROUP field, false if
+ ORDER by field
DESCRIPTION
Given a column reference (represented by 'order') from a GROUP BY or ORDER
@@ -12016,7 +12026,7 @@ find_order_in_list(THD *thd, Item **ref_pointer_array, TABLE_LIST *tables,
order_item_type == Item::REF_ITEM)
{
from_field= find_field_in_tables(thd, (Item_ident*) order_item, tables,
- &view_ref, IGNORE_ERRORS, TRUE,
+ NULL, &view_ref, IGNORE_ERRORS, TRUE,
FALSE);
if (!from_field)
from_field= (Field*) not_found_field;
@@ -12250,9 +12260,7 @@ create_distinct_group(THD *thd, Item **ref_pointer_array,
li.rewind();
while ((item=li++))
{
- if (item->const_item() || item->with_sum_func)
- continue;
- if (!item->marker)
+ if (!item->const_item() && !item->with_sum_func && !item->marker)
{
ORDER *ord=(ORDER*) thd->calloc(sizeof(ORDER));
if (!ord)
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 7fdd26846a6..77a3f872064 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -203,7 +203,7 @@ class JOIN :public Sql_alloc
Item *having;
Item *tmp_having; // To store having when processed temporary table
Item *having_history; // Store having for explain
- uint select_options;
+ ulonglong select_options;
select_result *result;
TMP_TABLE_PARAM tmp_table_param;
MYSQL_LOCK *lock;
@@ -259,14 +259,14 @@ class JOIN :public Sql_alloc
bool union_part; // this subselect is part of union
bool optimized; // flag to avoid double optimization in EXPLAIN
- JOIN(THD *thd_arg, List<Item> &fields_arg, ulong select_options_arg,
+ JOIN(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg,
select_result *result_arg)
:fields_list(fields_arg)
{
init(thd_arg, fields_arg, select_options_arg, result_arg);
}
- void init(THD *thd_arg, List<Item> &fields_arg, ulong select_options_arg,
+ void init(THD *thd_arg, List<Item> &fields_arg, ulonglong select_options_arg,
select_result *result_arg)
{
join_tab= join_tab_save= 0;
@@ -367,6 +367,11 @@ class JOIN :public Sql_alloc
!group_list);
}
bool change_result(select_result *result);
+ bool is_top_level_join() const
+ {
+ return (unit == &thd->lex->unit && (unit->fake_select_lex == 0 ||
+ select_lex == unit->fake_select_lex));
+ }
};
@@ -397,6 +402,7 @@ class Cursor: public Sql_alloc, public Query_arena
};
Engine_info ht_info[MAX_HA];
public:
+ Protocol_prep protocol;
Item_change_list change_list;
select_send result;
THR_LOCK_OWNER lock_id;
@@ -432,7 +438,7 @@ void TEST_join(JOIN *join);
bool store_val_in_field(Field *field,Item *val);
TABLE *create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields,
- ulong select_options, ha_rows rows_limit,
+ ulonglong select_options, ha_rows rows_limit,
char* alias);
TABLE *create_virtual_tmp_table(THD *thd, List<create_field> &field_list);
void free_tmp_table(THD *thd, TABLE *entry);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index e0c40b3466e..feeed2716fc 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -20,6 +20,7 @@
#include "mysql_priv.h"
#include "sql_select.h" // For select_describe
#include "repl_failsafe.h"
+#include "sp.h"
#include "sp_head.h"
#include "sql_trigger.h"
#include <my_dir.h>
@@ -263,8 +264,14 @@ mysql_find_files(THD *thd,List<char> *files, const char *db,const char *path,
bzero((char*) &table_list,sizeof(table_list));
- if (!(dirp = my_dir(path,MYF(MY_WME | (dir ? MY_WANT_STAT : 0)))))
+ if (!(dirp = my_dir(path,MYF(dir ? MY_WANT_STAT : 0))))
+ {
+ if (my_errno == ENOENT)
+ my_error(ER_BAD_DB_ERROR, MYF(ME_BELL+ME_WAITTANG), db);
+ else
+ my_error(ER_CANT_READ_DIR, MYF(ME_BELL+ME_WAITTANG), path, my_errno);
DBUG_RETURN(-1);
+ }
for (i=0 ; i < (uint) dirp->number_off_files ; i++)
{
@@ -352,7 +359,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
thd->lex->view_prepare_mode= TRUE;
/* Only one table for now, but VIEW can involve several tables */
- if (open_normal_and_derived_tables(thd, table_list))
+ if (open_normal_and_derived_tables(thd, table_list, 0))
{
DBUG_RETURN(TRUE);
}
@@ -551,7 +558,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
DBUG_ENTER("mysqld_list_fields");
DBUG_PRINT("enter",("table: %s",table_list->table_name));
- if (open_normal_and_derived_tables(thd, table_list))
+ if (open_normal_and_derived_tables(thd, table_list, 0))
DBUG_VOID_RETURN;
table= table_list->table;
@@ -619,7 +626,7 @@ static const char *require_quotes(const char *name, uint name_length)
uint length;
const char *end= name + name_length;
- for ( ; name < end ; name++)
+ for (; name < end ; name++)
{
uchar chr= (uchar) *name;
length= my_mbcharlen(system_charset_info, chr);
@@ -1720,7 +1727,7 @@ void get_index_field_values(LEX *lex, INDEX_FIELD_VALUES *index_field_values)
case SQLCOM_SHOW_TABLES:
case SQLCOM_SHOW_TABLE_STATUS:
case SQLCOM_SHOW_TRIGGERS:
- index_field_values->db_value= lex->current_select->db;
+ index_field_values->db_value= lex->select_lex.db;
index_field_values->table_value= wild;
break;
default:
@@ -1929,7 +1936,7 @@ int make_db_list(THD *thd, List<char> *files,
int schema_tables_add(THD *thd, List<char> *files, const char *wild)
{
ST_SCHEMA_TABLE *tmp_schema_table= schema_tables;
- for ( ; tmp_schema_table->table_name; tmp_schema_table++)
+ for (; tmp_schema_table->table_name; tmp_schema_table++)
{
if (tmp_schema_table->hidden)
continue;
@@ -1958,6 +1965,8 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
SELECT_LEX *select_lex= &lex->select_lex;
SELECT_LEX *old_all_select_lex= lex->all_selects_list;
+ TABLE_LIST **save_query_tables_last= lex->query_tables_last;
+ enum_sql_command save_sql_command= lex->sql_command;
SELECT_LEX *lsel= tables->schema_select_lex;
ST_SCHEMA_TABLE *schema_table= tables->schema_table;
SELECT_LEX sel;
@@ -1966,40 +1975,51 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
uint len;
bool with_i_schema;
enum enum_schema_tables schema_table_idx;
- thr_lock_type lock_type;
List<char> bases;
List_iterator_fast<char> it(bases);
COND *partial_cond;
uint derived_tables= lex->derived_tables;
int error= 1;
+ Open_tables_state open_tables_state_backup;
DBUG_ENTER("get_all_tables");
LINT_INIT(end);
LINT_INIT(len);
+ /*
+ Let us set fake sql_command so views won't try to merge
+ themselves into main statement.
+ */
+ lex->sql_command= SQLCOM_SHOW_FIELDS;
+
+ /*
+ We should not introduce deadlocks even if we already have some
+ tables open and locked, since we won't lock tables which we will
+ open and will ignore possible name-locks for these tables.
+ */
+ thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
+
if (lsel)
{
- TABLE *old_open_tables= thd->open_tables;
TABLE_LIST *show_table_list= (TABLE_LIST*) lsel->table_list.first;
bool res;
lex->all_selects_list= lsel;
- res= open_normal_and_derived_tables(thd, show_table_list);
- if (schema_table->process_table(thd, show_table_list,
+ res= open_normal_and_derived_tables(thd, show_table_list,
+ MYSQL_LOCK_IGNORE_FLUSH);
+ /*
+ get_all_tables() returns 1 on failure and 0 on success thus
+ return only these and not the result code of ::process_table()
+ */
+ error= test(schema_table->process_table(thd, show_table_list,
table, res, show_table_list->db,
- show_table_list->alias))
- goto err;
- close_thread_tables(thd, 0, 0, old_open_tables);
+ show_table_list->alias));
+ close_thread_tables(thd);
show_table_list->table= 0;
- error= 0;
goto err;
}
schema_table_idx= get_schema_table_idx(schema_table);
- lock_type= TL_UNLOCK;
-
- if (schema_table_idx == SCH_TABLES)
- lock_type= TL_READ;
if (make_db_list(thd, &bases, &idx_field_vals,
&with_i_schema, 0))
@@ -2056,7 +2076,7 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
{
if (with_i_schema)
{
- table->field[3]->store("TEMPORARY", 9, system_charset_info);
+ table->field[3]->store("SYSTEM VIEW", 11, system_charset_info);
}
else
{
@@ -2082,19 +2102,24 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
else
{
int res;
- TABLE *old_open_tables= thd->open_tables;
+ /*
+ Set the parent lex of 'sel' because it is needed by sel.init_query()
+ which is called inside make_table_list.
+ */
+ sel.parent_lex= lex;
if (make_table_list(thd, &sel, base_name, file_name))
goto err;
TABLE_LIST *show_table_list= (TABLE_LIST*) sel.table_list.first;
- show_table_list->lock_type= lock_type;
lex->all_selects_list= &sel;
lex->derived_tables= 0;
- res= open_normal_and_derived_tables(thd, show_table_list);
- if (schema_table->process_table(thd, show_table_list, table,
+ res= open_normal_and_derived_tables(thd, show_table_list,
+ MYSQL_LOCK_IGNORE_FLUSH);
+ res= schema_table->process_table(thd, show_table_list, table,
res, base_name,
- show_table_list->alias))
+ show_table_list->alias);
+ close_thread_tables(thd);
+ if (res)
goto err;
- close_thread_tables(thd, 0, 0, old_open_tables);
}
}
}
@@ -2108,8 +2133,12 @@ int get_all_tables(THD *thd, TABLE_LIST *tables, COND *cond)
error= 0;
err:
+ thd->restore_backup_open_tables_state(&open_tables_state_backup);
lex->derived_tables= derived_tables;
lex->all_selects_list= old_all_select_lex;
+ lex->query_tables_last= save_query_tables_last;
+ *save_query_tables_last= 0;
+ lex->sql_command= save_sql_command;
DBUG_RETURN(error);
}
@@ -2206,7 +2235,7 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables,
else if (tables->view)
{
table->field[3]->store("VIEW", 4, cs);
- table->field[20]->store("view", 4, cs);
+ table->field[20]->store("VIEW", 4, cs);
}
else
{
@@ -2214,8 +2243,11 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables,
TABLE_SHARE *share= show_table->s;
handler *file= show_table->file;
- file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_NO_LOCK);
- if (share->tmp_table == TMP_TABLE)
+ file->info(HA_STATUS_VARIABLE | HA_STATUS_TIME | HA_STATUS_AUTO |
+ HA_STATUS_NO_LOCK);
+ if (share->tmp_table == SYSTEM_TMP_TABLE)
+ table->field[3]->store("SYSTEM VIEW", 11, cs);
+ else if (share->tmp_table)
table->field[3]->store("TEMPORARY", 9, cs);
else
table->field[3]->store("BASE TABLE", 10, cs);
@@ -2270,13 +2302,8 @@ static int get_schema_tables_record(THD *thd, struct st_table_list *tables,
table->field[12]->store((longlong) file->delete_length);
if (show_table->found_next_number_field)
{
- show_table->next_number_field=show_table->found_next_number_field;
- show_table->next_number_field->reset();
- file->update_auto_increment();
- table->field[13]->store((longlong) show_table->
- next_number_field->val_int());
+ table->field[13]->store((longlong) file->auto_increment_value);
table->field[13]->set_notnull();
- show_table->next_number_field=0;
}
if (file->create_time)
{
@@ -2371,7 +2398,13 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables,
LEX *lex= thd->lex;
const char *wild= lex->wild ? lex->wild->ptr() : NullS;
CHARSET_INFO *cs= system_charset_info;
+ TABLE *show_table;
+ handler *file;
+ Field **ptr,*field;
+ int count;
+ uint base_name_length, file_name_length;
DBUG_ENTER("get_schema_column_record");
+
if (res)
{
if (lex->orig_sql_command != SQLCOM_SHOW_FIELDS)
@@ -2388,186 +2421,187 @@ static int get_schema_column_record(THD *thd, struct st_table_list *tables,
DBUG_RETURN(res);
}
- TABLE *show_table= tables->table;
- handler *file= show_table->file;
+ show_table= tables->table;
+ file= show_table->file;
+ count= 0;
file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
restore_record(show_table, s->default_values);
- Field **ptr,*field;
- int count= 0;
+ base_name_length= strlen(base_name);
+ file_name_length= strlen(file_name);
+
for (ptr=show_table->field; (field= *ptr) ; ptr++)
{
- if (!wild || !wild[0] ||
- !wild_case_compare(system_charset_info, field->field_name,wild))
- {
- const char *tmp_buff;
- byte *pos;
- bool is_blob;
- uint flags=field->flags;
- char tmp[MAX_FIELD_WIDTH];
- char tmp1[MAX_FIELD_WIDTH];
- String type(tmp,sizeof(tmp), system_charset_info);
- char *end= tmp;
- count++;
- restore_record(table, s->default_values);
+ const char *tmp_buff;
+ byte *pos;
+ bool is_blob;
+ uint flags=field->flags;
+ char tmp[MAX_FIELD_WIDTH];
+ char tmp1[MAX_FIELD_WIDTH];
+ String type(tmp,sizeof(tmp), system_charset_info);
+ char *end;
+ int decimals, field_length;
+
+ if (wild && wild[0] &&
+ wild_case_compare(system_charset_info, field->field_name,wild))
+ continue;
+
+ flags= field->flags;
+ count++;
+ /* Get default row, with all NULL fields set to NULL */
+ restore_record(table, s->default_values);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- uint col_access;
- check_access(thd,SELECT_ACL | EXTRA_ACL, base_name,
- &tables->grant.privilege, 0, 0);
- col_access= get_column_grant(thd, &tables->grant,
- base_name, file_name,
- field->field_name) & COL_ACLS;
- if (lex->orig_sql_command != SQLCOM_SHOW_FIELDS &&
- !tables->schema_table && !col_access)
- continue;
- for (uint bitnr=0; col_access ; col_access>>=1,bitnr++)
+ uint col_access;
+ check_access(thd,SELECT_ACL | EXTRA_ACL, base_name,
+ &tables->grant.privilege, 0, 0);
+ col_access= get_column_grant(thd, &tables->grant,
+ base_name, file_name,
+ field->field_name) & COL_ACLS;
+ if (lex->orig_sql_command != SQLCOM_SHOW_FIELDS &&
+ !tables->schema_table && !col_access)
+ continue;
+ end= tmp;
+ for (uint bitnr=0; col_access ; col_access>>=1,bitnr++)
+ {
+ if (col_access & 1)
{
- if (col_access & 1)
- {
- *end++=',';
- end=strmov(end,grant_types.type_names[bitnr]);
- }
+ *end++=',';
+ end=strmov(end,grant_types.type_names[bitnr]);
}
- if (tables->schema_table) // any user has 'select' privilege on all
- // I_S table columns
- table->field[17]->store(grant_types.type_names[0],
- strlen(grant_types.type_names[0]), cs);
- else
- table->field[17]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs);
+ }
+ if (tables->schema_table) // any user has 'select' privilege on all
+ // I_S table columns
+ table->field[17]->store(grant_types.type_names[0],
+ strlen(grant_types.type_names[0]), cs);
+ else
+ table->field[17]->store(tmp+1,end == tmp ? 0 : (uint) (end-tmp-1), cs);
-#else
- *end= 0;
#endif
- table->field[1]->store(base_name, strlen(base_name), cs);
- table->field[2]->store(file_name, strlen(file_name), cs);
- table->field[3]->store(field->field_name, strlen(field->field_name),
- cs);
- table->field[4]->store((longlong) count);
- field->sql_type(type);
- table->field[14]->store(type.ptr(), type.length(), cs);
- tmp_buff= strchr(type.ptr(), '(');
- table->field[7]->store(type.ptr(),
- (tmp_buff ? tmp_buff - type.ptr() :
- type.length()), cs);
- if (show_table->timestamp_field == field &&
- field->unireg_check != Field::TIMESTAMP_UN_FIELD)
- {
- table->field[5]->store("CURRENT_TIMESTAMP", 17, cs);
- table->field[5]->set_notnull();
- }
- else if (field->unireg_check != Field::NEXT_NUMBER &&
- !field->is_null() &&
- !(field->flags & NO_DEFAULT_VALUE_FLAG))
- {
- String def(tmp1,sizeof(tmp1), cs);
- type.set(tmp, sizeof(tmp), field->charset());
- field->val_str(&type);
- uint dummy_errors;
- def.copy(type.ptr(), type.length(), type.charset(), cs, &dummy_errors);
- table->field[5]->store(def.ptr(), def.length(), def.charset());
- table->field[5]->set_notnull();
- }
- else if (field->unireg_check == Field::NEXT_NUMBER ||
- field->maybe_null())
- table->field[5]->set_null(); // Null as default
- else
- {
- table->field[5]->store("",0, cs);
- table->field[5]->set_notnull();
- }
- pos=(byte*) ((flags & NOT_NULL_FLAG) &&
- field->type() != FIELD_TYPE_TIMESTAMP ?
- "NO" : "YES");
- table->field[6]->store((const char*) pos,
- strlen((const char*) pos), cs);
- is_blob= (field->type() == FIELD_TYPE_BLOB);
- if (field->has_charset() || is_blob)
- {
- longlong c_octet_len= is_blob ? (longlong) field->max_length() :
- (longlong) field->max_length()/field->charset()->mbmaxlen;
- table->field[8]->store(c_octet_len);
- table->field[8]->set_notnull();
- table->field[9]->store((longlong) field->max_length());
- table->field[9]->set_notnull();
- }
+ table->field[1]->store(base_name, base_name_length, cs);
+ table->field[2]->store(file_name, file_name_length, cs);
+ table->field[3]->store(field->field_name, strlen(field->field_name),
+ cs);
+ table->field[4]->store((longlong) count);
+ field->sql_type(type);
+ table->field[14]->store(type.ptr(), type.length(), cs);
+ tmp_buff= strchr(type.ptr(), '(');
+ table->field[7]->store(type.ptr(),
+ (tmp_buff ? tmp_buff - type.ptr() :
+ type.length()), cs);
+ if (show_table->timestamp_field == field &&
+ field->unireg_check != Field::TIMESTAMP_UN_FIELD)
+ {
+ table->field[5]->store("CURRENT_TIMESTAMP", 17, cs);
+ table->field[5]->set_notnull();
+ }
+ else if (field->unireg_check != Field::NEXT_NUMBER &&
+ !field->is_null() &&
+ !(field->flags & NO_DEFAULT_VALUE_FLAG))
+ {
+ String def(tmp1,sizeof(tmp1), cs);
+ type.set(tmp, sizeof(tmp), field->charset());
+ field->val_str(&type);
+ uint dummy_errors;
+ def.copy(type.ptr(), type.length(), type.charset(), cs, &dummy_errors);
+ table->field[5]->store(def.ptr(), def.length(), def.charset());
+ table->field[5]->set_notnull();
+ }
+ else if (field->unireg_check == Field::NEXT_NUMBER ||
+ lex->orig_sql_command != SQLCOM_SHOW_FIELDS ||
+ field->maybe_null())
+ table->field[5]->set_null(); // Null as default
+ else
+ {
+ table->field[5]->store("",0, cs);
+ table->field[5]->set_notnull();
+ }
+ pos=(byte*) ((flags & NOT_NULL_FLAG) &&
+ field->type() != FIELD_TYPE_TIMESTAMP ?
+ "NO" : "YES");
+ table->field[6]->store((const char*) pos,
+ strlen((const char*) pos), cs);
+ is_blob= (field->type() == FIELD_TYPE_BLOB);
+ if (field->has_charset() || is_blob)
+ {
+ longlong c_octet_len= is_blob ? (longlong) field->max_length() :
+ (longlong) field->max_length()/field->charset()->mbmaxlen;
+ table->field[8]->store(c_octet_len);
+ table->field[8]->set_notnull();
+ table->field[9]->store((longlong) field->max_length());
+ table->field[9]->set_notnull();
+ }
- {
- uint dec =field->decimals();
- switch (field->type()) {
- case FIELD_TYPE_NEWDECIMAL:
- table->field[10]->store((longlong)
- ((Field_new_decimal*)field)->precision);
- table->field[10]->set_notnull();
- table->field[11]->store((longlong) field->decimals());
- table->field[11]->set_notnull();
- break;
- case FIELD_TYPE_DECIMAL:
- {
- uint int_part=field->field_length - (dec ? dec + 1 : 0);
- table->field[10]->store((longlong) (int_part + dec - 1));
- table->field[10]->set_notnull();
- table->field[11]->store((longlong) field->decimals());
- table->field[11]->set_notnull();
- break;
- }
- case FIELD_TYPE_TINY:
- case FIELD_TYPE_SHORT:
- case FIELD_TYPE_LONG:
- case FIELD_TYPE_LONGLONG:
- case FIELD_TYPE_INT24:
- {
- table->field[10]->store((longlong) field->max_length() - 1);
- table->field[10]->set_notnull();
- break;
- }
- case FIELD_TYPE_BIT:
- {
- table->field[10]->store((longlong) field->max_length());
- table->field[10]->set_notnull();
- break;
- }
- case FIELD_TYPE_FLOAT:
- case FIELD_TYPE_DOUBLE:
- {
- table->field[10]->store((longlong) field->field_length);
- table->field[10]->set_notnull();
- if (dec != NOT_FIXED_DEC)
- {
- table->field[11]->store((longlong) dec);
- table->field[11]->set_notnull();
- }
- break;
- }
- default:
- break;
- }
- }
- if (field->has_charset())
- {
- pos=(byte*) field->charset()->csname;
- table->field[12]->store((const char*) pos,
- strlen((const char*) pos), cs);
- table->field[12]->set_notnull();
- pos=(byte*) field->charset()->name;
- table->field[13]->store((const char*) pos,
- strlen((const char*) pos), cs);
- table->field[13]->set_notnull();
- }
- pos=(byte*) ((field->flags & PRI_KEY_FLAG) ? "PRI" :
- (field->flags & UNIQUE_KEY_FLAG) ? "UNI" :
- (field->flags & MULTIPLE_KEY_FLAG) ? "MUL":"");
- table->field[15]->store((const char*) pos,
- strlen((const char*) pos), cs);
- end= tmp;
- if (field->unireg_check == Field::NEXT_NUMBER)
- end=strmov(tmp,"auto_increment");
- table->field[16]->store(tmp, (uint) (end-tmp), cs);
+ /*
+ Calculate field_length and decimals.
+ They are set to -1 if they should not be set (we should return NULL)
+ */
- end=tmp;
- table->field[18]->store(field->comment.str, field->comment.length, cs);
- if (schema_table_store_record(thd, table))
- DBUG_RETURN(1);
+ decimals= field->decimals();
+ switch (field->type()) {
+ case FIELD_TYPE_NEWDECIMAL:
+ field_length= ((Field_new_decimal*) field)->precision;
+ break;
+ case FIELD_TYPE_DECIMAL:
+ field_length= field->field_length - (decimals ? 2 : 1);
+ break;
+ case FIELD_TYPE_TINY:
+ case FIELD_TYPE_SHORT:
+ case FIELD_TYPE_LONG:
+ case FIELD_TYPE_LONGLONG:
+ case FIELD_TYPE_INT24:
+ field_length= field->max_length() - 1;
+ break;
+ case FIELD_TYPE_BIT:
+ field_length= field->max_length();
+ decimals= -1; // return NULL
+ break;
+ case FIELD_TYPE_FLOAT:
+ case FIELD_TYPE_DOUBLE:
+ field_length= field->field_length;
+ if (decimals == NOT_FIXED_DEC)
+ decimals= -1; // return NULL
+ break;
+ default:
+ field_length= decimals= -1;
+ break;
+ }
+
+ if (field_length >= 0)
+ {
+ table->field[10]->store((longlong) field_length);
+ table->field[10]->set_notnull();
}
+ if (decimals >= 0)
+ {
+ table->field[11]->store((longlong) decimals);
+ table->field[11]->set_notnull();
+ }
+
+ if (field->has_charset())
+ {
+ pos=(byte*) field->charset()->csname;
+ table->field[12]->store((const char*) pos,
+ strlen((const char*) pos), cs);
+ table->field[12]->set_notnull();
+ pos=(byte*) field->charset()->name;
+ table->field[13]->store((const char*) pos,
+ strlen((const char*) pos), cs);
+ table->field[13]->set_notnull();
+ }
+ pos=(byte*) ((field->flags & PRI_KEY_FLAG) ? "PRI" :
+ (field->flags & UNIQUE_KEY_FLAG) ? "UNI" :
+ (field->flags & MULTIPLE_KEY_FLAG) ? "MUL":"");
+ table->field[15]->store((const char*) pos,
+ strlen((const char*) pos), cs);
+
+ end= tmp;
+ if (field->unireg_check == Field::NEXT_NUMBER)
+ end=strmov(tmp,"auto_increment");
+ table->field[16]->store(tmp, (uint) (end-tmp), cs);
+
+ table->field[18]->store(field->comment.str, field->comment.length, cs);
+ if (schema_table_store_record(thd, table))
+ DBUG_RETURN(1);
}
DBUG_RETURN(0);
}
@@ -2580,7 +2614,8 @@ int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond)
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
TABLE *table= tables->table;
CHARSET_INFO *scs= system_charset_info;
- for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ )
+
+ for (cs= all_charsets ; cs < all_charsets+255 ; cs++)
{
CHARSET_INFO *tmp_cs= cs[0];
if (tmp_cs && (tmp_cs->state & MY_CS_PRIMARY) &&
@@ -2588,12 +2623,12 @@ int fill_schema_charsets(THD *thd, TABLE_LIST *tables, COND *cond)
!(wild && wild[0] &&
wild_case_compare(scs, tmp_cs->csname,wild)))
{
+ const char *comment;
restore_record(table, s->default_values);
table->field[0]->store(tmp_cs->csname, strlen(tmp_cs->csname), scs);
table->field[1]->store(tmp_cs->name, strlen(tmp_cs->name), scs);
- table->field[2]->store(tmp_cs->comment ? tmp_cs->comment : "",
- strlen(tmp_cs->comment ? tmp_cs->comment : ""),
- scs);
+ comment= tmp_cs->comment ? tmp_cs->comment : "";
+ table->field[2]->store(comment, strlen(comment), scs);
table->field[3]->store((longlong) tmp_cs->mbmaxlen);
if (schema_table_store_record(thd, table))
return 1;
@@ -2609,14 +2644,14 @@ int fill_schema_collation(THD *thd, TABLE_LIST *tables, COND *cond)
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
TABLE *table= tables->table;
CHARSET_INFO *scs= system_charset_info;
- for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ )
+ for (cs= all_charsets ; cs < all_charsets+255 ; cs++ )
{
CHARSET_INFO **cl;
CHARSET_INFO *tmp_cs= cs[0];
if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) ||
!(tmp_cs->state & MY_CS_PRIMARY))
continue;
- for ( cl= all_charsets; cl < all_charsets+255 ;cl ++)
+ for (cl= all_charsets; cl < all_charsets+255 ;cl ++)
{
CHARSET_INFO *tmp_cl= cl[0];
if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) ||
@@ -2649,14 +2684,14 @@ int fill_schema_coll_charset_app(THD *thd, TABLE_LIST *tables, COND *cond)
CHARSET_INFO **cs;
TABLE *table= tables->table;
CHARSET_INFO *scs= system_charset_info;
- for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ )
+ for (cs= all_charsets ; cs < all_charsets+255 ; cs++ )
{
CHARSET_INFO **cl;
CHARSET_INFO *tmp_cs= cs[0];
if (!tmp_cs || !(tmp_cs->state & MY_CS_AVAILABLE) ||
!(tmp_cs->state & MY_CS_PRIMARY))
continue;
- for ( cl= all_charsets; cl < all_charsets+255 ;cl ++)
+ for (cl= all_charsets; cl < all_charsets+255 ;cl ++)
{
CHARSET_INFO *tmp_cl= cl[0];
if (!tmp_cl || !(tmp_cl->state & MY_CS_AVAILABLE) ||
@@ -2749,12 +2784,14 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE_LIST proc_tables;
const char *wild= thd->lex->wild ? thd->lex->wild->ptr() : NullS;
int res= 0;
- TABLE *table= tables->table, *old_open_tables= thd->open_tables;
+ TABLE *table= tables->table;
bool full_access;
char definer[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
+ Open_tables_state open_tables_state_backup;
DBUG_ENTER("fill_schema_proc");
strxmov(definer, thd->priv_user, "@", thd->priv_host, NullS);
+ /* We use this TABLE_LIST instance only for checking of privileges. */
bzero((char*) &proc_tables,sizeof(proc_tables));
proc_tables.db= (char*) "mysql";
proc_tables.db_length= 5;
@@ -2762,7 +2799,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
proc_tables.table_name_length= 4;
proc_tables.lock_type= TL_READ;
full_access= !check_table_access(thd, SELECT_ACL, &proc_tables, 1);
- if (!(proc_table= open_ltable(thd, &proc_tables, TL_READ)))
+ if (!(proc_table= open_proc_table_for_read(thd, &open_tables_state_backup)))
{
DBUG_RETURN(1);
}
@@ -2788,7 +2825,7 @@ int fill_schema_proc(THD *thd, TABLE_LIST *tables, COND *cond)
err:
proc_table->file->ha_index_end();
- close_thread_tables(thd, 0, 0, old_open_tables);
+ close_proc_table(thd, &open_tables_state_backup);
DBUG_RETURN(res);
}
@@ -3192,7 +3229,8 @@ int fill_open_tables(THD *thd, TABLE_LIST *tables, COND *cond)
TABLE *table= tables->table;
CHARSET_INFO *cs= system_charset_info;
OPEN_TABLE_LIST *open_list;
- if (!(open_list=list_open_tables(thd,wild)) && thd->is_fatal_error)
+ if (!(open_list=list_open_tables(thd,thd->lex->select_lex.db, wild))
+ && thd->is_fatal_error)
DBUG_RETURN(1);
for (; open_list ; open_list=open_list->next)
@@ -3258,7 +3296,7 @@ int fill_status(THD *thd, TABLE_LIST *tables, COND *cond)
ST_SCHEMA_TABLE *find_schema_table(THD *thd, const char* table_name)
{
ST_SCHEMA_TABLE *schema_table= schema_tables;
- for ( ; schema_table->table_name; schema_table++)
+ for (; schema_table->table_name; schema_table++)
{
if (!my_strcasecmp(system_charset_info,
schema_table->table_name,
@@ -3299,7 +3337,7 @@ TABLE *create_schema_table(THD *thd, TABLE_LIST *table_list)
CHARSET_INFO *cs= system_charset_info;
DBUG_ENTER("create_schema_table");
- for ( ; fields_info->field_name; fields_info++)
+ for (; fields_info->field_name; fields_info++)
{
switch (fields_info->field_type) {
case MYSQL_TYPE_LONG:
@@ -3368,7 +3406,7 @@ int make_old_format(THD *thd, ST_SCHEMA_TABLE *schema_table)
{
ST_FIELD_INFO *field_info= schema_table->fields_info;
Name_resolution_context *context= &thd->lex->select_lex.context;
- for ( ; field_info->field_name; field_info++)
+ for (; field_info->field_name; field_info++)
{
if (field_info->old_name)
{
@@ -3554,7 +3592,7 @@ int mysql_schema_table(THD *thd, LEX *lex, TABLE_LIST *table_list)
{
DBUG_RETURN(1);
}
- table->s->tmp_table= TMP_TABLE;
+ table->s->tmp_table= SYSTEM_TMP_TABLE;
table->grant.privilege= SELECT_ACL;
/*
This test is necessary to make
@@ -3685,11 +3723,6 @@ bool get_schema_tables_result(JOIN *join)
TABLE_LIST *table_list= tab->table->pos_in_table_list;
if (table_list->schema_table && thd->fill_derived_tables())
{
- TABLE_LIST **query_tables_last= lex->query_tables_last;
- TABLE *old_derived_tables= thd->derived_tables;
- MYSQL_LOCK *sql_lock= thd->lock;
- lex->sql_command= SQLCOM_SHOW_FIELDS;
- DBUG_ASSERT(!*query_tables_last);
if (&lex->unit != lex->current_select->master_unit()) // is subselect
{
table_list->table->file->extra(HA_EXTRA_RESET_STATE);
@@ -3700,16 +3733,9 @@ bool get_schema_tables_result(JOIN *join)
else
table_list->table->file->records= 0;
- thd->derived_tables= 0;
- thd->lock=0;
if (table_list->schema_table->fill_table(thd, table_list,
tab->select_cond))
result= 1;
- thd->lock= sql_lock;
- lex->sql_command= SQLCOM_SELECT;
- thd->derived_tables= old_derived_tables;
- lex->query_tables_last= query_tables_last;
- *query_tables_last= 0;
}
}
thd->no_warnings_for_error= 0;
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 6f2f1bf344f..d20908c9f1c 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -288,6 +288,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
for (table= tables; table; table= table->next_local)
{
char *db=table->db;
+ table->was_dropped= 0;
mysql_ha_flush(thd, table, MYSQL_HA_CLOSE_FINAL);
if (!close_temporary_table(thd, db, table->table_name))
{
@@ -359,6 +360,8 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
wrong_tables.append(',');
wrong_tables.append(String(table->table_name,system_charset_info));
}
+ else
+ table->was_dropped= 1;
}
thd->tmp_table_used= tmp_table_deleted;
error= 0;
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 722568e7f53..ddd0f3fe162 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -370,7 +370,7 @@ static void display_table_locks(void)
VOID(my_init_dynamic_array(&saved_table_locks,sizeof(TABLE_LOCK_INFO),open_cache.records + 20,50));
VOID(pthread_mutex_lock(&THR_LOCK_lock));
- for (list=thr_lock_thread_list ; list ; list=rest(list))
+ for (list= thr_lock_thread_list; list; list= list_rest(list))
{
THR_LOCK *lock=(THR_LOCK*) list->data;
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 09eeff02de6..053dfdfc990 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -869,3 +869,39 @@ end:
free_root(&table.mem_root, MYF(0));
DBUG_RETURN(result);
}
+
+
+
+bool Table_triggers_list::process_triggers(THD *thd, trg_event_type event,
+ trg_action_time_type time_type,
+ bool old_row_is_record1)
+{
+ int res= 0;
+
+ if (bodies[event][time_type])
+ {
+ Sub_statement_state statement_state;
+
+ if (old_row_is_record1)
+ {
+ old_field= record1_field;
+ new_field= table->field;
+ }
+ else
+ {
+ new_field= record1_field;
+ old_field= table->field;
+ }
+
+ /*
+ FIXME: We should juggle with security context here (because trigger
+ should be invoked with creator rights).
+ */
+
+ thd->reset_sub_statement_state(&statement_state, SUB_STMT_TRIGGER);
+ res= bodies[event][time_type]->execute_function(thd, 0, 0, 0);
+ thd->restore_sub_statement_state(&statement_state);
+ }
+
+ return res;
+}
diff --git a/sql/sql_trigger.h b/sql/sql_trigger.h
index e2ed4bcc0f4..d9b39cc3034 100644
--- a/sql/sql_trigger.h
+++ b/sql/sql_trigger.h
@@ -20,6 +20,7 @@
QQ: Will it be merged into TABLE in future ?
*/
+
class Table_triggers_list: public Sql_alloc
{
/* Triggers as SPs grouped by event, action_time */
@@ -76,55 +77,7 @@ public:
bool drop_trigger(THD *thd, TABLE_LIST *table);
bool process_triggers(THD *thd, trg_event_type event,
trg_action_time_type time_type,
- bool old_row_is_record1)
- {
- int res= 0;
-
- if (bodies[event][time_type])
- {
- bool save_in_sub_stmt= thd->in_sub_stmt;
-#ifndef EMBEDDED_LIBRARY
- /* Surpress OK packets in case if we will execute statements */
- my_bool nsok= thd->net.no_send_ok;
- thd->net.no_send_ok= TRUE;
-#endif
-
- if (old_row_is_record1)
- {
- old_field= record1_field;
- new_field= table->field;
- }
- else
- {
- new_field= record1_field;
- old_field= table->field;
- }
-
- /*
- FIXME: We should juggle with security context here (because trigger
- should be invoked with creator rights).
- */
- /*
- We disable binlogging, as in SP/functions, even though currently
- triggers can't do updates. When triggers can do updates, someone
- should add such a trigger to rpl_sp.test to verify that the update
- does NOT go into binlog.
- */
- tmp_disable_binlog(thd);
- thd->in_sub_stmt= TRUE;
-
- res= bodies[event][time_type]->execute_function(thd, 0, 0, 0);
-
- thd->in_sub_stmt= save_in_sub_stmt;
- reenable_binlog(thd);
-
-#ifndef EMBEDDED_LIBRARY
- thd->net.no_send_ok= nsok;
-#endif
- }
-
- return res;
- }
+ bool old_row_is_record1);
bool get_trigger_info(THD *thd, trg_event_type event,
trg_action_time_type time_type,
LEX_STRING *trigger_name, LEX_STRING *trigger_stmt,
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index c414f5e9e72..72c96e81682 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -119,8 +119,6 @@ void
st_select_lex_unit::init_prepare_fake_select_lex(THD *thd)
{
thd->lex->current_select= fake_select_lex;
- fake_select_lex->ftfunc_list_alloc.empty();
- fake_select_lex->ftfunc_list= &fake_select_lex->ftfunc_list_alloc;
fake_select_lex->table_list.link_in_list((byte *)&result_table_list,
(byte **)
&result_table_list.next_local);
@@ -291,6 +289,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
List_iterator_fast<Item> tp(types);
Query_arena *arena= thd->current_arena;
Item *type;
+ ulonglong create_options;
while ((type= tp++))
{
@@ -301,15 +300,24 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
goto err;
}
}
+
+ create_options= (first_select_in_union()->options | thd_arg->options |
+ TMP_TABLE_ALL_COLUMNS);
+ /*
+ Force the temporary table to be a MyISAM table if we're going to use
+ fullext functions (MATCH ... AGAINST .. IN BOOLEAN MODE) when reading
+ from it (this should be removed in 5.2 when fulltext search is moved
+ out of MyISAM).
+ */
+ if (global_parameters->ftfunc_list->elements)
+ create_options= create_options | TMP_TABLE_FORCE_MYISAM;
union_result->tmp_table_param.field_count= types.elements;
if (!(table= create_tmp_table(thd_arg,
&union_result->tmp_table_param, types,
(ORDER*) 0, (bool) union_distinct, 1,
- (first_select_in_union()->options |
- thd_arg->options |
- TMP_TABLE_ALL_COLUMNS),
- HA_POS_ERROR, (char *) tmp_table_alias)))
+ create_options, HA_POS_ERROR,
+ (char *) tmp_table_alias)))
goto err;
table->file->extra(HA_EXTRA_WRITE_CACHE);
table->file->extra(HA_EXTRA_IGNORE_DUP_KEY);
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 95d0f500df8..993b1a409ba 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -138,7 +138,7 @@ int mysql_update(THD *thd,
LINT_INIT(timestamp_query_id);
- if (open_tables(thd, &table_list, &table_count))
+ if (open_tables(thd, &table_list, &table_count, 0))
DBUG_RETURN(1);
if (table_list->multitable_view)
@@ -660,7 +660,7 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
tables.table= table;
tables.alias= table_list->alias;
- if (setup_tables(thd, &select_lex->context,
+ if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list,
table_list, conds, &select_lex->leaf_tables,
FALSE) ||
setup_conds(thd, table_list, select_lex->leaf_tables, conds) ||
@@ -737,7 +737,8 @@ bool mysql_multi_update_prepare(THD *thd)
thd->lex->sql_command= SQLCOM_UPDATE_MULTI;
/* open tables and create derived ones, but do not lock and fill them */
- if ((original_multiupdate && open_tables(thd, &table_list, & table_count)) ||
+ if ((original_multiupdate &&
+ open_tables(thd, &table_list, &table_count, 0)) ||
mysql_handle_derived(lex, &mysql_derived_prepare))
DBUG_RETURN(TRUE);
/*
@@ -747,6 +748,7 @@ bool mysql_multi_update_prepare(THD *thd)
*/
if (setup_tables(thd, &lex->select_lex.context,
+ &lex->select_lex.top_join_list,
table_list, &lex->select_lex.where,
&lex->select_lex.leaf_tables, FALSE))
DBUG_RETURN(TRUE);
@@ -865,6 +867,7 @@ bool mysql_multi_update_prepare(THD *thd)
tbl->cleanup_items();
if (setup_tables(thd, &lex->select_lex.context,
+ &lex->select_lex.top_join_list,
table_list, &lex->select_lex.where,
&lex->select_lex.leaf_tables, FALSE) ||
setup_fields_with_no_wrap(thd, 0, *fields, 2, 0, 0))
@@ -915,7 +918,7 @@ bool mysql_multi_update(THD *thd,
List<Item> *fields,
List<Item> *values,
COND *conds,
- ulong options,
+ ulonglong options,
enum enum_duplicates handle_duplicates, bool ignore,
SELECT_LEX_UNIT *unit, SELECT_LEX *select_lex)
{
@@ -925,7 +928,7 @@ bool mysql_multi_update(THD *thd,
if (mysql_multi_update_prepare(thd))
DBUG_RETURN(TRUE);
- if (!(result= new multi_update(thd, table_list,
+ if (!(result= new multi_update(table_list,
thd->lex->select_lex.leaf_tables,
fields, values,
handle_duplicates, ignore)))
@@ -951,13 +954,13 @@ bool mysql_multi_update(THD *thd,
}
-multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list,
+multi_update::multi_update(TABLE_LIST *table_list,
TABLE_LIST *leaves_list,
List<Item> *field_list, List<Item> *value_list,
enum enum_duplicates handle_duplicates_arg,
bool ignore_arg)
:all_tables(table_list), leaves(leaves_list), update_tables(0),
- thd(thd_arg), tmp_tables(0), updated(0), found(0), fields(field_list),
+ tmp_tables(0), updated(0), found(0), fields(field_list),
values(value_list), table_count(0), copy_field(0),
handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(0),
transactional_tables(1), ignore(ignore_arg)
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index aedff648e5c..dcada0c0780 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -94,6 +94,67 @@ static void make_unique_view_field_name(Item *target,
target->set_name(buff, name_len, system_charset_info);
}
+
+/*
+ Check if items with same names are present in list and possibly
+ generate unique names for them.
+
+ SYNOPSIS
+ item_list list of Items which should be checked for duplicates
+ gen_unique_view_name flag: generate unique name or return with error when
+ duplicate names are found.
+
+ DESCRIPTION
+ This function is used on view creation and preparation of derived tables.
+ It checks item_list for items with duplicate names. If it founds two
+ items with same name and conversion to unique names isn't allowed, or
+ names for both items are set by user - function fails.
+ Otherwise it generates unique name for one item with autogenerated name
+ using make_unique_view_field_name()
+
+ RETURN VALUE
+ FALSE no duplicate names found, or they are converted to unique ones
+ TRUE duplicate names are found and they can't be converted or conversion
+ isn't allowed
+*/
+
+bool check_duplicate_names(List<Item> &item_list, bool gen_unique_view_name)
+{
+ Item *item;
+ List_iterator_fast<Item> it(item_list);
+ List_iterator_fast<Item> itc(item_list);
+ DBUG_ENTER("check_duplicate_names");
+
+ while ((item= it++))
+ {
+ Item *check;
+ /* treat underlying fields like set by user names */
+ if (item->real_item()->type() == Item::FIELD_ITEM)
+ item->is_autogenerated_name= FALSE;
+ itc.rewind();
+ while ((check= itc++) && check != item)
+ {
+ if (my_strcasecmp(system_charset_info, item->name, check->name) == 0)
+ {
+ if (!gen_unique_view_name)
+ goto err;
+ if (item->is_autogenerated_name)
+ make_unique_view_field_name(item, item_list, item);
+ else if (check->is_autogenerated_name)
+ make_unique_view_field_name(check, item_list, item);
+ else
+ goto err;
+ }
+ }
+ }
+ DBUG_RETURN(FALSE);
+
+err:
+ my_error(ER_DUP_FIELDNAME, MYF(0), item->name);
+ DBUG_RETURN(TRUE);
+}
+
+
/*
Creating/altering VIEW procedure
@@ -245,9 +306,11 @@ bool mysql_create_view(THD *thd,
/*
check that tables are not temporary and this VIEW do not used in query
- (it is possible with ALTERing VIEW)
- */
- for (tbl= tables; tbl; tbl= tbl->next_global)
+ (it is possible with ALTERing VIEW).
+ open_and_lock_tables can change the value of tables,
+ e.g. it may happen if before the function call tables was equal to 0.
+ */
+ for (tbl= lex->query_tables; tbl; tbl= tbl->next_global)
{
/* is this table temporary and is not view? */
if (tbl->table->s->tmp_table != NO_TMP_TABLE && !tbl->view &&
@@ -308,35 +371,8 @@ bool mysql_create_view(THD *thd,
}
}
- /* Test absence of duplicates names */
- {
- Item *item;
- List_iterator_fast<Item> it(select_lex->item_list);
- List_iterator_fast<Item> itc(select_lex->item_list);
- while ((item= it++))
- {
- Item *check;
- /* treat underlying fields like set by user names */
- if (item->real_item()->type() == Item::FIELD_ITEM)
- item->is_autogenerated_name= FALSE;
- itc.rewind();
- while ((check= itc++) && check != item)
- {
- if (my_strcasecmp(system_charset_info, item->name, check->name) == 0)
- {
- if (item->is_autogenerated_name)
- make_unique_view_field_name(item, select_lex->item_list, item);
- else if (check->is_autogenerated_name)
- make_unique_view_field_name(check, select_lex->item_list, item);
- else
- {
- my_error(ER_DUP_FIELDNAME, MYF(0), item->name);
- DBUG_RETURN(TRUE);
- }
- }
- }
- }
- }
+ if (check_duplicate_names(select_lex->item_list, 1))
+ DBUG_RETURN(TRUE);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
/*
@@ -844,6 +880,9 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
view_tables);
lex->select_lex.context.outer_context= 0;
lex->select_lex.context.select_lex= table->select_lex;
+ lex->select_lex.select_n_having_items+=
+ table->select_lex->select_n_having_items;
+
/* do not check privileges & hide errors for view underlyings */
for (SELECT_LEX *sl= lex->all_selects_list;
sl;
@@ -864,7 +903,11 @@ mysql_make_view(File_parser *parser, TABLE_LIST *table)
{
if (view_tables->next_local)
+ {
table->multitable_view= TRUE;
+ if (table->belong_to_view)
+ table->belong_to_view->multitable_view= TRUE;
+ }
/* make nested join structure for view tables */
NESTED_JOIN *nested_join;
if (!(nested_join= table->nested_join=
@@ -1110,7 +1153,10 @@ bool check_key_in_view(THD *thd, TABLE_LIST *view)
for (Field_translator *fld= trans; fld < end_of_trans; fld++)
{
if (!fld->item->fixed && fld->item->fix_fields(thd, &fld->item))
+ {
+ thd->set_query_id= save_set_query_id;
return TRUE;
+ }
}
thd->set_query_id= save_set_query_id;
}
diff --git a/sql/sql_view.h b/sql/sql_view.h
index 3246dbae383..9d961feb143 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -33,5 +33,7 @@ int view_checksum(THD *thd, TABLE_LIST *view);
extern TYPELIB updatable_views_with_limit_typelib;
+bool check_duplicate_names(List<Item>& item_list, bool gen_unique_view_names);
+
#define VIEW_ANY_ACL (SELECT_ACL | UPDATE_ACL | INSERT_ACL | DELETE_ACL)
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 6dd808006c3..9cda6efca06 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -592,6 +592,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token SUM_SYM
%token SUPER_SYM
%token SUSPEND_SYM
+%token SYSDATE
%token TABLES
%token TABLESPACE
%token TABLE_SYM
@@ -730,7 +731,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
bool_term bool_factor bool_test bool_pri
predicate bit_expr bit_term bit_factor value_expr term factor
table_wild simple_expr udf_expr
- using_list expr_or_default set_expr_or_default interval_expr
+ expr_or_default set_expr_or_default interval_expr
param_marker singlerow_subselect singlerow_subselect_init
exists_subselect exists_subselect_init geometry_function
signed_literal now_or_signed_literal opt_escape
@@ -753,7 +754,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
key_alg opt_btree_or_rtree
%type <string_list>
- key_usage_list
+ key_usage_list using_list
%type <key_part>
key_part
@@ -1327,6 +1328,12 @@ create:
YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
sp->restore_thd_mem_root(YYTHD);
+ if (sp->m_multi_results)
+ {
+ my_error(ER_SP_NO_RETSET, MYF(0), "trigger");
+ YYABORT;
+ }
+
/*
We have to do it after parsing trigger body, because some of
sp_proc_stmt alternatives are not saving/restoring LEX, so
@@ -1479,8 +1486,7 @@ create_function_tail:
if (sp->m_multi_results)
{
- my_message(ER_SP_NO_RETSET_IN_FUNC, ER(ER_SP_NO_RETSET_IN_FUNC),
- MYF(0));
+ my_error(ER_SP_NO_RETSET, MYF(0), "function");
YYABORT;
}
if (sp->check_backpatch(YYTHD))
@@ -1976,7 +1982,7 @@ sp_proc_stmt:
}
if (lex->sql_command == SQLCOM_CHANGE_DB)
{ /* "USE db" doesn't work in a procedure */
- my_message(ER_SP_NO_USE, ER(ER_SP_NO_USE), MYF(0));
+ my_error(ER_SP_BADSTATEMENT, MYF(0), "USE");
YYABORT;
}
/*
@@ -5007,10 +5013,10 @@ simple_expr:
my_error(ER_WRONG_COLUMN_NAME, MYF(0), name->str);
YYABORT;
}
- $$= new Item_default_value(&Select->context, $3);
+ $$= new Item_default_value(Lex->current_context(), $3);
}
| VALUES '(' simple_ident ')'
- { $$= new Item_insert_value(&Select->context, $3); }
+ { $$= new Item_insert_value(Lex->current_context(), $3); }
| FUNC_ARG0 '(' ')'
{
if (!$1.symbol->create_func)
@@ -5265,6 +5271,10 @@ simple_expr:
{ $$= new Item_func_substr($3,$5); }
| SUBSTRING_INDEX '(' expr ',' expr ',' expr ')'
{ $$= new Item_func_substr_index($3,$5,$7); }
+ | SYSDATE optional_braces
+ { $$= new Item_func_sysdate_local(); Lex->safe_to_cache_query=0;}
+ | SYSDATE '(' expr ')'
+ { $$= new Item_func_sysdate_local($3); Lex->safe_to_cache_query=0;}
| TIME_SYM '(' expr ')'
{ $$= new Item_time_typecast($3); }
| TIMESTAMP '(' expr ')'
@@ -5301,9 +5311,9 @@ simple_expr:
name->init_qname(YYTHD);
sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
if ($5)
- $$= new Item_func_sp(&lex->current_select->context, name, *$5);
+ $$= new Item_func_sp(Lex->current_context(), name, *$5);
else
- $$= new Item_func_sp(&lex->current_select->context, name);
+ $$= new Item_func_sp(Lex->current_context(), name);
lex->safe_to_cache_query=0;
}
| IDENT_sys '(' udf_expr_list ')'
@@ -5391,9 +5401,9 @@ simple_expr:
sp_add_used_routine(lex, YYTHD, name, TYPE_ENUM_FUNCTION);
if ($3)
- $$= new Item_func_sp(&lex->current_select->context, name, *$3);
+ $$= new Item_func_sp(Lex->current_context(), name, *$3);
else
- $$= new Item_func_sp(&lex->current_select->context, name);
+ $$= new Item_func_sp(Lex->current_context(), name);
lex->safe_to_cache_query=0;
}
}
@@ -5597,7 +5607,7 @@ sum_expr:
{
SELECT_LEX *sel= Select;
sel->in_sum_expr--;
- $$=new Item_func_group_concat(&sel->context, $3, $5,
+ $$=new Item_func_group_concat(Lex->current_context(), $3, $5,
sel->gorder_list, $7);
$5->empty();
};
@@ -5733,68 +5743,116 @@ join_table:
table_ref normal_join table_ref { YYERROR_UNLESS($1 && ($$=$3)); }
| table_ref STRAIGHT_JOIN table_factor
{ YYERROR_UNLESS($1 && ($$=$3)); $3->straight=1; }
- | table_ref normal_join table_ref ON expr
- { YYERROR_UNLESS($1 && ($$=$3)); add_join_on($3,$5); }
- | table_ref STRAIGHT_JOIN table_factor ON expr
- { YYERROR_UNLESS($1 && ($$=$3)); $3->straight=1; add_join_on($3,$5); }
+ | table_ref normal_join table_ref
+ ON
+ {
+ YYERROR_UNLESS($1 && ($$=$3));
+ /* Change the current name resolution context to a local context. */
+ Name_resolution_context *on_context;
+ if (!(on_context= make_join_on_context(YYTHD,$1,$3)))
+ YYABORT;
+ Lex->push_context(on_context);
+ }
+ expr
+ {
+ add_join_on($3,$6);
+ Lex->pop_context();
+ }
+ | table_ref STRAIGHT_JOIN table_factor
+ ON
+ {
+ YYERROR_UNLESS($1 && ($$=$3));
+ /* Change the current name resolution context to a local context. */
+ Name_resolution_context *on_context;
+ if (!(on_context= make_join_on_context(YYTHD,$1,$3)))
+ YYABORT;
+ Lex->push_context(on_context);
+ }
+ expr
+ {
+ $3->straight=1;
+ add_join_on($3,$6);
+ Lex->pop_context();
+ }
| table_ref normal_join table_ref
USING
{
SELECT_LEX *sel= Select;
YYERROR_UNLESS($1 && $3);
- sel->save_names_for_using_list($1, $3);
}
'(' using_list ')'
- { add_join_on($3,$7); $$=$3; }
-
- | table_ref LEFT opt_outer JOIN_SYM table_ref ON expr
- { YYERROR_UNLESS($1 && $5); add_join_on($5,$7); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
+ { add_join_natural($1,$3,$7); $$=$3; }
+ | table_ref LEFT opt_outer JOIN_SYM table_ref
+ ON
+ {
+ /* Change the current name resolution context to a local context. */
+ Name_resolution_context *on_context;
+ if (!(on_context= make_join_on_context(YYTHD,$1,$5)))
+ YYABORT;
+ Lex->push_context(on_context);
+ }
+ expr
+ {
+ YYERROR_UNLESS($1 && $5);
+ add_join_on($5,$8);
+ Lex->pop_context();
+ $5->outer_join|=JOIN_TYPE_LEFT;
+ $$=$5;
+ }
| table_ref LEFT opt_outer JOIN_SYM table_factor
{
SELECT_LEX *sel= Select;
YYERROR_UNLESS($1 && $5);
- sel->save_names_for_using_list($1, $5);
}
USING '(' using_list ')'
- { add_join_on($5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
+ { add_join_natural($1,$5,$9); $5->outer_join|=JOIN_TYPE_LEFT; $$=$5; }
| table_ref NATURAL LEFT opt_outer JOIN_SYM table_factor
{
YYERROR_UNLESS($1 && $6);
- add_join_natural($1,$6);
+ add_join_natural($1,$6,NULL);
$6->outer_join|=JOIN_TYPE_LEFT;
$$=$6;
}
- | table_ref RIGHT opt_outer JOIN_SYM table_ref ON expr
+ | table_ref RIGHT opt_outer JOIN_SYM table_ref
+ ON
+ {
+ /* Change the current name resolution context to a local context. */
+ Name_resolution_context *on_context;
+ if (!(on_context= make_join_on_context(YYTHD,$1,$5)))
+ YYABORT;
+ Lex->push_context(on_context);
+ }
+ expr
{
LEX *lex= Lex;
YYERROR_UNLESS($1 && $5);
if (!($$= lex->current_select->convert_right_join()))
YYABORT;
- add_join_on($$, $7);
+ add_join_on($$, $8);
+ Lex->pop_context();
}
| table_ref RIGHT opt_outer JOIN_SYM table_factor
{
SELECT_LEX *sel= Select;
YYERROR_UNLESS($1 && $5);
- sel->save_names_for_using_list($1, $5);
}
USING '(' using_list ')'
{
LEX *lex= Lex;
if (!($$= lex->current_select->convert_right_join()))
YYABORT;
- add_join_on($$, $9);
+ add_join_natural($$,$5,$9);
}
| table_ref NATURAL RIGHT opt_outer JOIN_SYM table_factor
{
YYERROR_UNLESS($1 && $6);
- add_join_natural($6,$1);
+ add_join_natural($6,$1,NULL);
LEX *lex= Lex;
if (!($$= lex->current_select->convert_right_join()))
YYABORT;
}
| table_ref NATURAL JOIN_SYM table_factor
- { YYERROR_UNLESS($1 && ($$=$4)); add_join_natural($1,$4); };
+ { YYERROR_UNLESS($1 && ($$=$4)); add_join_natural($1,$4,NULL); };
normal_join:
@@ -5822,8 +5880,23 @@ table_factor:
YYABORT;
sel->add_joined_table($$);
}
- | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref ON expr '}'
- { YYERROR_UNLESS($3 && $7); add_join_on($7,$9); $7->outer_join|=JOIN_TYPE_LEFT; $$=$7; }
+ | '{' ident table_ref LEFT OUTER JOIN_SYM table_ref
+ ON
+ {
+ /* Change the current name resolution context to a local context. */
+ Name_resolution_context *on_context;
+ if (!(on_context= make_join_on_context(YYTHD,$3,$7)))
+ YYABORT;
+ Lex->push_context(on_context);
+ }
+ expr '}'
+ {
+ YYERROR_UNLESS($3 && $7);
+ add_join_on($7,$10);
+ Lex->pop_context();
+ $7->outer_join|=JOIN_TYPE_LEFT;
+ $$=$7;
+ }
| select_derived_init get_select_lex select_derived2
{
LEX *lex= Lex;
@@ -5877,6 +5950,7 @@ table_factor:
YYABORT;
sel->add_joined_table($$);
+ lex->pop_context();
}
else
if ($4 || $6)
@@ -6016,32 +6090,18 @@ key_usage_list2:
using_list:
ident
{
- SELECT_LEX *sel= Select;
- if (!($$= new Item_func_eq(new Item_field(&sel->context,
- sel->db1, sel->table1,
- $1.str),
- new Item_field(&sel->context,
- sel->db2, sel->table2,
- $1.str))))
+ if (!($$= new List<String>))
YYABORT;
+ $$->push_back(new (YYTHD->mem_root)
+ String((const char *) $1.str, $1.length,
+ system_charset_info));
}
| using_list ',' ident
{
- SELECT_LEX *sel= Select;
- if (!($$=
- new Item_cond_and(new
- Item_func_eq(new
- Item_field(&sel->context,
- sel->db1,
- sel->table1,
- $3.str),
- new
- Item_field(&sel->context,
- sel->db2,
- sel->table2,
- $3.str)),
- $1)))
- YYABORT;
+ $1->push_back(new (YYTHD->mem_root)
+ String((const char *) $3.str, $3.length,
+ system_charset_info));
+ $$= $1;
};
interval:
@@ -6682,7 +6742,7 @@ values:
expr_or_default:
expr { $$= $1;}
- | DEFAULT {$$= new Item_default_value(&Select->context); }
+ | DEFAULT {$$= new Item_default_value(Lex->current_context()); }
;
opt_insert_update:
@@ -7229,9 +7289,14 @@ flush:
FLUSH_SYM opt_no_write_to_binlog
{
LEX *lex=Lex;
- if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_FUNCTION)
+ if (lex->sphead && lex->sphead->m_type != TYPE_ENUM_PROCEDURE)
{
- my_error(ER_SP_BADSTATEMENT, MYF(0), "FLUSH");
+ /*
+ Note that both FLUSH TABLES and FLUSH PRIVILEGES will break
+ execution in prelocked mode. So it is better to disable
+ FLUSH in stored functions and triggers completely.
+ */
+ my_error(ER_STMT_NOT_ALLOWED_IN_SF_OR_TRG, MYF(0), "FLUSH");
YYABORT;
}
lex->sql_command= SQLCOM_FLUSH; lex->type=0;
@@ -7638,13 +7703,13 @@ table_wild:
ident '.' '*'
{
SELECT_LEX *sel= Select;
- $$ = new Item_field(&sel->context, NullS, $1.str, "*");
+ $$ = new Item_field(Lex->current_context(), NullS, $1.str, "*");
sel->with_wild++;
}
| ident '.' ident '.' '*'
{
SELECT_LEX *sel= Select;
- $$ = new Item_field(&sel->context, (YYTHD->client_capabilities &
+ $$ = new Item_field(Lex->current_context(), (YYTHD->client_capabilities &
CLIENT_NO_SCHEMA ? NullS : $1.str),
$3.str,"*");
sel->with_wild++;
@@ -7672,8 +7737,8 @@ simple_ident:
SELECT_LEX *sel=Select;
$$= (sel->parsing_place != IN_HAVING ||
sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(&sel->context, NullS, NullS, $1.str) :
- (Item*) new Item_ref(&sel->context, NullS, NullS, $1.str);
+ (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) :
+ (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str);
}
}
| simple_ident_q { $$= $1; }
@@ -7685,8 +7750,8 @@ simple_ident_nospvar:
SELECT_LEX *sel=Select;
$$= (sel->parsing_place != IN_HAVING ||
sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(&sel->context, NullS, NullS, $1.str) :
- (Item*) new Item_ref(&sel->context, NullS, NullS, $1.str);
+ (Item*) new Item_field(Lex->current_context(), NullS, NullS, $1.str) :
+ (Item*) new Item_ref(Lex->current_context(), NullS, NullS, $1.str);
}
| simple_ident_q { $$= $1; }
;
@@ -7723,7 +7788,7 @@ simple_ident_q:
YYABORT;
}
- if (!(trg_fld= new Item_trigger_field(&lex->current_select->context,
+ if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
new_row ?
Item_trigger_field::NEW_ROW:
Item_trigger_field::OLD_ROW,
@@ -7749,8 +7814,8 @@ simple_ident_q:
}
$$= (sel->parsing_place != IN_HAVING ||
sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(&sel->context, NullS, $1.str, $3.str) :
- (Item*) new Item_ref(&sel->context, NullS, $1.str, $3.str);
+ (Item*) new Item_field(Lex->current_context(), NullS, $1.str, $3.str) :
+ (Item*) new Item_ref(Lex->current_context(), NullS, $1.str, $3.str);
}
}
| '.' ident '.' ident
@@ -7765,8 +7830,8 @@ simple_ident_q:
}
$$= (sel->parsing_place != IN_HAVING ||
sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(&sel->context, NullS, $2.str, $4.str) :
- (Item*) new Item_ref(&sel->context, NullS, $2.str, $4.str);
+ (Item*) new Item_field(Lex->current_context(), NullS, $2.str, $4.str) :
+ (Item*) new Item_ref(Lex->current_context(), NullS, $2.str, $4.str);
}
| ident '.' ident '.' ident
{
@@ -7780,11 +7845,11 @@ simple_ident_q:
}
$$= (sel->parsing_place != IN_HAVING ||
sel->get_in_sum_expr() > 0) ?
- (Item*) new Item_field(&sel->context,
+ (Item*) new Item_field(Lex->current_context(),
(YYTHD->client_capabilities &
CLIENT_NO_SCHEMA ? NullS : $1.str),
$3.str, $5.str) :
- (Item*) new Item_ref(&sel->context,
+ (Item*) new Item_ref(Lex->current_context(),
(YYTHD->client_capabilities &
CLIENT_NO_SCHEMA ? NullS : $1.str),
$3.str, $5.str);
@@ -8345,7 +8410,8 @@ sys_option_value:
/* We are in trigger and assigning value to field of new row */
Item *it;
Item_trigger_field *trg_fld;
- sp_instr_set_trigger_field *i;
+ sp_instr_set_trigger_field *sp_fld;
+ LINT_INIT(sp_fld);
if ($1)
{
yyerror(ER(ER_SYNTAX_ERROR));
@@ -8359,14 +8425,14 @@ sys_option_value:
it= new Item_null();
}
- if (!(trg_fld= new Item_trigger_field(&lex->current_select->
- context,
+ if (!(trg_fld= new Item_trigger_field(Lex->current_context(),
Item_trigger_field::NEW_ROW,
$2.base_name.str)) ||
- !(i= new sp_instr_set_trigger_field(lex->sphead->
- instructions(),
- lex->spcont, trg_fld,
- it, lex)))
+ !(sp_fld= new sp_instr_set_trigger_field(lex->sphead->
+ instructions(),
+ lex->spcont,
+ trg_fld,
+ it, lex)))
YYABORT;
/*
@@ -8376,7 +8442,7 @@ sys_option_value:
lex->trg_table_fields.link_in_list((byte *)trg_fld,
(byte **)&trg_fld->next_trg_field);
- lex->sphead->add_instr(i);
+ lex->sphead->add_instr(sp_fld);
}
else if ($2.var)
{ /* System variable */
@@ -8390,7 +8456,7 @@ sys_option_value:
/* An SP local variable */
sp_pcontext *ctx= lex->spcont;
sp_pvar_t *spv;
- sp_instr_set *i;
+ sp_instr_set *sp_set;
Item *it;
if ($1)
{
@@ -8406,9 +8472,9 @@ sys_option_value:
it= spv->dflt;
else
it= new Item_null();
- i= new sp_instr_set(lex->sphead->instructions(), ctx,
- spv->offset, it, spv->type, lex, TRUE);
- lex->sphead->add_instr(i);
+ sp_set= new sp_instr_set(lex->sphead->instructions(), ctx,
+ spv->offset, it, spv->type, lex, TRUE);
+ lex->sphead->add_instr(sp_set);
spv->isset= TRUE;
}
}
@@ -9216,7 +9282,14 @@ union_list:
lex->current_select->master_unit()->union_distinct=
lex->current_select;
}
- select_init {}
+ select_init
+ {
+ /*
+ Remove from the name resolution context stack the context of the
+ last select in the union.
+ */
+ Lex->pop_context();
+ }
;
union_opt:
@@ -9320,6 +9393,7 @@ subselect_end:
')'
{
LEX *lex=Lex;
+ lex->pop_context();
lex->current_select = lex->current_select->return_after_parsing();
};
@@ -9371,7 +9445,7 @@ xa: XA_SYM begin_or_start xid opt_join_or_resume
{
Lex->sql_command = SQLCOM_XA_START;
}
- | XA_SYM END xid opt_suspend_or_migrate
+ | XA_SYM END xid opt_suspend
{
Lex->sql_command = SQLCOM_XA_END;
}
@@ -9431,9 +9505,14 @@ opt_one_phase:
| ONE_SYM PHASE_SYM { Lex->xa_opt=XA_ONE_PHASE; }
;
-opt_suspend_or_migrate:
+opt_suspend:
/* nothing */ { Lex->xa_opt=XA_NONE; }
| SUSPEND_SYM { Lex->xa_opt=XA_SUSPEND; }
+ opt_migrate
+ ;
+
+opt_migrate:
+ /* nothing */ { }
| FOR_SYM MIGRATE_SYM { Lex->xa_opt=XA_FOR_MIGRATE; }
;
diff --git a/sql/table.cc b/sql/table.cc
index 7c1701cc4d3..810ade44b6c 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -1755,8 +1755,6 @@ void st_table_list::set_ancestor()
*/
tbl->ancestor->set_ancestor();
}
- if (tbl->multitable_view)
- multitable_view= TRUE;
} while ((tbl= tbl->next_local));
if (!multitable_view)
@@ -2182,8 +2180,290 @@ bool st_table_list::set_insert_values(MEM_ROOT *mem_root)
}
+/*
+ Test if this is a leaf with respect to name resolution.
+
+ SYNOPSIS
+ st_table_list::is_leaf_for_name_resolution()
+
+ DESCRIPTION
+ A table reference is a leaf with respect to name resolution if
+ it is either a leaf node in a nested join tree (table, view,
+ schema table, subquery), or an inner node that represents a
+ NATURAL/USING join, or a nested join with materialized join
+ columns.
+
+ RETURN
+ TRUE if a leaf, FALSE otherwise.
+*/
+bool st_table_list::is_leaf_for_name_resolution()
+{
+ return (view || is_natural_join || is_join_columns_complete ||
+ !nested_join);
+}
+
+
+/*
+ Retrieve the first (left-most) leaf in a nested join tree with
+ respect to name resolution.
+
+ SYNOPSIS
+ st_table_list::first_leaf_for_name_resolution()
+
+ DESCRIPTION
+ Given that 'this' is a nested table reference, recursively walk
+ down the left-most children of 'this' until we reach a leaf
+ table reference with respect to name resolution.
+
+ IMPLEMENTATION
+ The left-most child of a nested table reference is the last element
+ in the list of children because the children are inserted in
+ reverse order.
+
+ RETURN
+ If 'this' is a nested table reference - the left-most child of
+ the tree rooted in 'this',
+ else return 'this'
+*/
+
+TABLE_LIST *st_table_list::first_leaf_for_name_resolution()
+{
+ TABLE_LIST *cur_table_ref;
+ NESTED_JOIN *cur_nested_join;
+ LINT_INIT(cur_table_ref);
+
+ if (is_leaf_for_name_resolution())
+ return this;
+ DBUG_ASSERT(nested_join);
+
+ for (cur_nested_join= nested_join;
+ cur_nested_join;
+ cur_nested_join= cur_table_ref->nested_join)
+ {
+ List_iterator_fast<TABLE_LIST> it(cur_nested_join->join_list);
+ cur_table_ref= it++;
+ /*
+ If 'this' is a RIGHT JOIN, the operands in 'join_list' are in reverse
+ order, thus the first operand is already at the front of the list.
+ */
+ if (!(cur_table_ref->outer_join & JOIN_TYPE_RIGHT))
+ {
+ TABLE_LIST *next;
+ while ((next= it++))
+ cur_table_ref= next;
+ }
+ if (cur_table_ref->is_leaf_for_name_resolution())
+ break;
+ }
+ return cur_table_ref;
+}
+
+
+/*
+ Retrieve the last (right-most) leaf in a nested join tree with
+ respect to name resolution.
+
+ SYNOPSIS
+ st_table_list::last_leaf_for_name_resolution()
+
+ DESCRIPTION
+ Given that 'this' is a nested table reference, recursively walk
+ down the right-most children of 'this' until we reach a leaf
+ table reference with respect to name resolution.
+
+ IMPLEMENTATION
+ The right-most child of a nested table reference is the first
+ element in the list of children because the children are inserted
+ in reverse order.
+
+ RETURN
+ - If 'this' is a nested table reference - the right-most child of
+ the tree rooted in 'this',
+ - else - 'this'
+*/
+
+TABLE_LIST *st_table_list::last_leaf_for_name_resolution()
+{
+ TABLE_LIST *cur_table_ref= this;
+ NESTED_JOIN *cur_nested_join;
+
+ if (is_leaf_for_name_resolution())
+ return this;
+ DBUG_ASSERT(nested_join);
+
+ for (cur_nested_join= nested_join;
+ cur_nested_join;
+ cur_nested_join= cur_table_ref->nested_join)
+ {
+ /*
+ If 'this' is a RIGHT JOIN, the operands in 'join_list' are in reverse
+ order, thus the last operand is in the end of the list.
+ */
+ if ((cur_table_ref->outer_join & JOIN_TYPE_RIGHT))
+ {
+ List_iterator_fast<TABLE_LIST> it(cur_nested_join->join_list);
+ TABLE_LIST *next;
+ cur_table_ref= it++;
+ while ((next= it++))
+ cur_table_ref= next;
+ }
+ else
+ cur_table_ref= cur_nested_join->join_list.head();
+ if (cur_table_ref->is_leaf_for_name_resolution())
+ break;
+ }
+ return cur_table_ref;
+}
+
+
+Natural_join_column::Natural_join_column(Field_translator *field_param,
+ TABLE_LIST *tab)
+{
+ DBUG_ASSERT(tab->field_translation);
+ view_field= field_param;
+ table_field= NULL;
+ table_ref= tab;
+ is_common= FALSE;
+}
+
+
+Natural_join_column::Natural_join_column(Field *field_param,
+ TABLE_LIST *tab)
+{
+ DBUG_ASSERT(tab->table == field_param->table);
+ table_field= field_param;
+ view_field= NULL;
+ table_ref= tab;
+ is_common= FALSE;
+}
+
+
+const char *Natural_join_column::name()
+{
+ if (view_field)
+ {
+ DBUG_ASSERT(table_field == NULL);
+ return view_field->name;
+ }
+
+ return table_field->field_name;
+}
+
+
+Item *Natural_join_column::create_item(THD *thd)
+{
+ if (view_field)
+ {
+ DBUG_ASSERT(table_field == NULL);
+ return create_view_field(thd, table_ref, &view_field->item,
+ view_field->name);
+ }
+ return new Item_field(thd, &thd->lex->current_select->context, table_field);
+}
+
+
+Field *Natural_join_column::field()
+{
+ if (view_field)
+ {
+ DBUG_ASSERT(table_field == NULL);
+ return NULL;
+ }
+ return table_field;
+}
+
+
+const char *Natural_join_column::table_name()
+{
+ return table_ref->alias;
+ /*
+ TODO:
+ I think that it is sufficient to return just
+ table->alias, which is correctly set to either
+ the view name, the table name, or the alias to
+ the table reference (view or stored table).
+ */
+#ifdef NOT_YET
+ if (view_field)
+ return table_ref->view_name.str;
+
+ DBUG_ASSERT(!strcmp(table_ref->table_name,
+ table_ref->table->s->table_name));
+ return table_ref->table_name;
+}
+#endif
+}
+
+
+const char *Natural_join_column::db_name()
+{
+ if (view_field)
+ return table_ref->view_db.str;
+
+ DBUG_ASSERT(!strcmp(table_ref->db,
+ table_ref->table->s->db));
+ return table_ref->db;
+}
+
+
+GRANT_INFO *Natural_join_column::grant()
+{
+ if (view_field)
+ return &(table_ref->grant);
+ return &(table_ref->table->grant);
+}
+
+
+
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+
+/*
+ Check the access rights for the current join column.
+ columns.
+
+ SYNOPSIS
+ Natural_join_column::check_grants()
+
+ DESCRIPTION
+ Check the access rights to a column from a natural join in a generic
+ way that hides the heterogeneity of the column representation - whether
+ it is a view or a stored table colum.
+
+ RETURN
+ FALSE The column can be accessed
+ TRUE There are no access rights to all equivalent columns
+*/
+
+bool
+Natural_join_column::check_grants(THD *thd, const char *name, uint length)
+{
+ GRANT_INFO *grant;
+ const char *db_name;
+ const char *table_name;
+
+ if (view_field)
+ {
+ DBUG_ASSERT(table_field == NULL);
+ grant= &(table_ref->grant);
+ db_name= table_ref->view_db.str;
+ table_name= table_ref->view_name.str;
+ }
+ else
+ {
+ DBUG_ASSERT(table_field && view_field == NULL);
+ grant= &(table_ref->table->grant);
+ db_name= table_ref->table->s->db;
+ table_name= table_ref->table->s->table_name;
+ }
+
+ return check_grant_column(thd, grant, db_name, table_name, name, length);
+}
+#endif
+
+
void Field_iterator_view::set(TABLE_LIST *table)
{
+ DBUG_ASSERT(table->field_translation);
view= table;
ptr= table->field_translation;
array_end= table->field_translation_end;
@@ -2253,6 +2533,197 @@ Item *create_view_field(THD *thd, TABLE_LIST *view, Item **field_ref,
}
+void Field_iterator_natural_join::set(TABLE_LIST *table_ref)
+{
+ DBUG_ASSERT(table_ref->join_columns);
+ delete column_ref_it;
+
+ /*
+ TODO: try not to allocate new iterator every time. If we have to,
+ then check for out of memory condition.
+ */
+ column_ref_it= new List_iterator_fast<Natural_join_column>
+ (*(table_ref->join_columns));
+ cur_column_ref= (*column_ref_it)++;
+}
+
+
+void Field_iterator_natural_join::next()
+{
+ cur_column_ref= (*column_ref_it)++;
+ DBUG_ASSERT(!cur_column_ref || ! cur_column_ref->table_field ||
+ cur_column_ref->table_ref->table ==
+ cur_column_ref->table_field->table);
+}
+
+
+void Field_iterator_table_ref::set_field_iterator()
+{
+ DBUG_ENTER("Field_iterator_table_ref::set_field_iterator");
+ /*
+ If the table reference we are iterating over is a natural join, or it is
+ an operand of a natural join, and TABLE_LIST::join_columns contains all
+ the columns of the join operand, then we pick the columns from
+ TABLE_LIST::join_columns, instead of the orginial container of the
+ columns of the join operator.
+ */
+ if (table_ref->is_join_columns_complete)
+ {
+ /* Necesary, but insufficient conditions. */
+ DBUG_ASSERT(table_ref->is_natural_join ||
+ table_ref->nested_join ||
+ table_ref->join_columns &&
+ /* This is a merge view. */
+ ((table_ref->field_translation &&
+ table_ref->join_columns->elements ==
+ (ulong)(table_ref->field_translation_end -
+ table_ref->field_translation)) ||
+ /* This is stored table or a tmptable view. */
+ (!table_ref->field_translation &&
+ table_ref->join_columns->elements ==
+ table_ref->table->s->fields)));
+ field_it= &natural_join_it;
+ DBUG_PRINT("info",("field_it for '%s' is Field_iterator_natural_join",
+ table_ref->alias));
+ }
+ /* This is a merge view, so use field_translation. */
+ else if (table_ref->field_translation)
+ {
+ DBUG_ASSERT(table_ref->view &&
+ table_ref->effective_algorithm == VIEW_ALGORITHM_MERGE);
+ field_it= &view_field_it;
+ DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_view",
+ table_ref->alias));
+ }
+ /* This is a base table or stored view. */
+ else
+ {
+ DBUG_ASSERT(table_ref->table || table_ref->view);
+ field_it= &table_field_it;
+ DBUG_PRINT("info", ("field_it for '%s' is Field_iterator_table",
+ table_ref->alias));
+ }
+ field_it->set(table_ref);
+ DBUG_VOID_RETURN;
+}
+
+
+void Field_iterator_table_ref::set(TABLE_LIST *table)
+{
+ DBUG_ASSERT(table);
+ first_leaf= table->first_leaf_for_name_resolution();
+ last_leaf= table->last_leaf_for_name_resolution();
+ DBUG_ASSERT(first_leaf && last_leaf);
+ table_ref= first_leaf;
+ set_field_iterator();
+}
+
+
+void Field_iterator_table_ref::next()
+{
+ /* Move to the next field in the current table reference. */
+ field_it->next();
+ /*
+ If all fields of the current table reference are exhausted, move to
+ the next leaf table reference.
+ */
+ if (field_it->end_of_fields() && table_ref != last_leaf)
+ {
+ table_ref= table_ref->next_name_resolution_table;
+ DBUG_ASSERT(table_ref);
+ set_field_iterator();
+ }
+}
+
+
+const char *Field_iterator_table_ref::table_name()
+{
+ if (table_ref->view)
+ return table_ref->view_name.str;
+ else if (table_ref->is_natural_join)
+ return natural_join_it.column_ref()->table_name();
+
+ DBUG_ASSERT(!strcmp(table_ref->table_name,
+ table_ref->table->s->table_name));
+ return table_ref->table_name;
+}
+
+
+const char *Field_iterator_table_ref::db_name()
+{
+ if (table_ref->view)
+ return table_ref->view_db.str;
+ else if (table_ref->is_natural_join)
+ return natural_join_it.column_ref()->db_name();
+
+ DBUG_ASSERT(!strcmp(table_ref->db, table_ref->table->s->db));
+ return table_ref->db;
+}
+
+
+GRANT_INFO *Field_iterator_table_ref::grant()
+{
+ if (table_ref->view)
+ return &(table_ref->grant);
+ else if (table_ref->is_natural_join)
+ return natural_join_it.column_ref()->grant();
+ return &(table_ref->table->grant);
+}
+
+
+/*
+ Create new or return existing column reference to a column of a
+ natural/using join.
+
+ SYNOPSIS
+ Field_iterator_table_ref::get_or_create_column_ref()
+ thd [in] pointer to current thread
+ is_created [out] set to TRUE if the column was created,
+ FALSE if we return an already created colum
+
+ DESCRIPTION
+ TODO
+
+ RETURN
+ # Pointer to a column of a natural join (or its operand)
+ NULL No memory to allocate the column
+*/
+
+Natural_join_column *
+Field_iterator_table_ref::get_or_create_column_ref(THD *thd, bool *is_created)
+{
+ Natural_join_column *nj_col;
+
+ *is_created= TRUE;
+ if (field_it == &table_field_it)
+ {
+ /* The field belongs to a stored table. */
+ Field *field= table_field_it.field();
+ nj_col= new Natural_join_column(field, table_ref);
+ }
+ else if (field_it == &view_field_it)
+ {
+ /* The field belongs to a merge view or information schema table. */
+ Field_translator *translated_field= view_field_it.field_translator();
+ nj_col= new Natural_join_column(translated_field, table_ref);
+ }
+ else
+ {
+ /*
+ The field belongs to a NATURAL join, therefore the column reference was
+ already created via one of the two constructor calls above. In this case
+ we just return the already created column reference.
+ */
+ *is_created= FALSE;
+ nj_col= natural_join_it.column_ref();
+ DBUG_ASSERT(nj_col);
+ }
+ DBUG_ASSERT(!nj_col->table_field ||
+ nj_col->table_ref->table == nj_col->table_field->table);
+ return nj_col;
+}
+
+
/*****************************************************************************
** Instansiate templates
*****************************************************************************/
diff --git a/sql/table.h b/sql/table.h
index 30462d1b173..767acf8bdc8 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -50,7 +50,8 @@ typedef struct st_grant_info
ulong want_privilege;
} GRANT_INFO;
-enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2};
+enum tmp_table_type {NO_TMP_TABLE=0, TMP_TABLE=1, TRANSACTIONAL_TMP_TABLE=2,
+ SYSTEM_TMP_TABLE=3};
enum frm_type_enum
{
@@ -93,7 +94,10 @@ class Field_timestamp;
class Field_blob;
class Table_triggers_list;
-/* This structure is shared between different table objects */
+/*
+ This structure is shared between different table objects. There is one
+ instance of table share per one table in the database.
+*/
typedef struct st_table_share
{
@@ -372,9 +376,80 @@ struct Field_translator
};
+/*
+ Column reference of a NATURAL/USING join. Since column references in
+ joins can be both from views and stored tables, may point to either a
+ Field (for tables), or a Field_translator (for views).
+*/
+
+class Natural_join_column: public Sql_alloc
+{
+public:
+ Field_translator *view_field; /* Column reference of merge view. */
+ Field *table_field; /* Column reference of table or temp view. */
+ st_table_list *table_ref; /* Original base table/view reference. */
+ /*
+ True if a common join column of two NATURAL/USING join operands. Notice
+ that when we have a hierarchy of nested NATURAL/USING joins, a column can
+ be common at some level of nesting but it may not be common at higher
+ levels of nesting. Thus this flag may change depending on at which level
+ we are looking at some column.
+ */
+ bool is_common;
+public:
+ Natural_join_column(Field_translator *field_param, st_table_list *tab);
+ Natural_join_column(Field *field_param, st_table_list *tab);
+ const char *name();
+ Item *create_item(THD *thd);
+ Field *field();
+ const char *table_name();
+ const char *db_name();
+ GRANT_INFO *grant();
+#ifndef NO_EMBEDDED_ACCESS_CHECKS
+ bool check_grants(THD *thd, const char *name, uint length);
+#endif
+};
+
+
+/*
+ Table reference in the FROM clause.
+
+ These table references can be of several types that correspond to
+ different SQL elements. Below we list all types of TABLE_LISTs with
+ the necessary conditions to determine when a TABLE_LIST instance
+ belongs to a certain type.
+
+ 1) table (TABLE_LIST::view == NULL)
+ - base table
+ (TABLE_LIST::derived == NULL)
+ - subquery - TABLE_LIST::table is a temp table
+ (TABLE_LIST::derived != NULL)
+ - information schema table
+ (TABLE_LIST::schema_table != NULL)
+ NOTICE: for schema tables TABLE_LIST::field_translation may be != NULL
+ 2) view (TABLE_LIST::view != NULL)
+ - merge (TABLE_LIST::effective_algorithm == VIEW_ALGORITHM_MERGE)
+ also (TABLE_LIST::field_translation != NULL)
+ - tmptable (TABLE_LIST::effective_algorithm == VIEW_ALGORITHM_TMPTABLE)
+ also (TABLE_LIST::field_translation == NULL)
+ 3) nested table reference (TABLE_LIST::nested_join != NULL)
+ - table sequence - e.g. (t1, t2, t3)
+ TODO: how to distinguish from a JOIN?
+ - general JOIN
+ TODO: how to distinguish from a table sequence?
+ - NATURAL JOIN
+ (TABLE_LIST::natural_join != NULL)
+ - JOIN ... USING
+ (TABLE_LIST::join_using_fields != NULL)
+*/
+
typedef struct st_table_list
{
- /* link in a local table list (used by SQL_LIST) */
+ /*
+ List of tables local to a subquery (used by SQL_LIST). Considers
+ views as leaves (unlike 'next_leaf' below). Created at parse time
+ in st_select_lex::add_table_to_list() -> table_list.link_in_list().
+ */
struct st_table_list *next_local;
/* link in a global list of all queries tables */
struct st_table_list *next_global, **prev_global;
@@ -382,7 +457,7 @@ typedef struct st_table_list
char *option; /* Used by cache index */
Item *on_expr; /* Used with outer join */
/*
- The scturcture of ON expression presented in the member above
+ The structure of ON expression presented in the member above
can be changed during certain optimizations. This member
contains a snapshot of AND-OR structure of the ON expression
made after permanent transformations of the parse tree, and is
@@ -391,10 +466,40 @@ typedef struct st_table_list
*/
Item *prep_on_expr;
COND_EQUAL *cond_equal; /* Used with outer join */
- struct st_table_list *natural_join; /* natural join on this table*/
- /* ... join ... USE INDEX ... IGNORE INDEX */
- List<String> *use_index, *ignore_index;
- TABLE *table; /* opened table */
+ /*
+ During parsing - left operand of NATURAL/USING join where 'this' is
+ the right operand. After parsing (this->natural_join == this) iff
+ 'this' represents a NATURAL or USING join operation. Thus after
+ parsing 'this' is a NATURAL/USING join iff (natural_join != NULL).
+ */
+ struct st_table_list *natural_join;
+ /*
+ True if 'this' represents a nested join that is a NATURAL JOIN.
+ For one of the operands of 'this', the member 'natural_join' points
+ to the other operand of 'this'.
+ */
+ bool is_natural_join;
+ /* Field names in a USING clause for JOIN ... USING. */
+ List<String> *join_using_fields;
+ /*
+ Explicitly store the result columns of either a NATURAL/USING join or
+ an operand of such a join.
+ */
+ List<Natural_join_column> *join_columns;
+ /* TRUE if join_columns contains all columns of this table reference. */
+ bool is_join_columns_complete;
+
+ /*
+ List of nodes in a nested join tree, that should be considered as
+ leaves with respect to name resolution. The leaves are: views,
+ top-most nodes representing NATURAL/USING joins, subqueries, and
+ base tables. All of these TABLE_LIST instances contain a
+ materialized list of columns. The list is local to a subquery.
+ */
+ struct st_table_list *next_name_resolution_table;
+ /* Index names in a "... JOIN ... USE/IGNORE INDEX ..." clause. */
+ List<String> *use_index, *ignore_index;
+ TABLE *table; /* opened table */
/*
select_result for derived table to pass it from table creation to table
filling procedure
@@ -411,6 +516,10 @@ typedef struct st_table_list
st_select_lex_unit *derived; /* SELECT_LEX_UNIT of derived table */
ST_SCHEMA_TABLE *schema_table; /* Information_schema table */
st_select_lex *schema_select_lex;
+ /*
+ True when the view field translation table is used to convert
+ schema table fields for backwards compatibility with SHOW command.
+ */
bool schema_table_reformed;
TMP_TABLE_PARAM *schema_table_param;
/* link to select_lex where this table was used */
@@ -423,7 +532,11 @@ typedef struct st_table_list
st_table_list *ancestor;
/* most upper view this table belongs to */
st_table_list *belong_to_view;
- /* list of join table tree leaves */
+ /*
+ List of all base tables local to a subquery including all view
+ tables. Unlike 'next_local', this in this list views are *not*
+ leaves. Created in setup_tables() -> make_leaves_list().
+ */
st_table_list *next_leaf;
Item *where; /* VIEW WHERE clause condition */
Item *check_option; /* WITH CHECK OPTION condition */
@@ -464,6 +577,9 @@ typedef struct st_table_list
st_table_list *embedding; /* nested join containing the table */
List<struct st_table_list> *join_list;/* join list the table belongs to */
bool cacheable_table; /* stop PS caching */
+
+ /* used for proper partially successful DROP DATABASE binlogging */
+ bool was_dropped;
/* used in multi-upd/views privilege check */
bool table_in_first_from_clause;
bool skip_temporary; /* this table shouldn't be temporary */
@@ -493,6 +609,9 @@ typedef struct st_table_list
bool set_insert_values(MEM_ROOT *mem_root);
void hide_view_error(THD *thd);
st_table_list *find_underlying_table(TABLE *table);
+ st_table_list *first_leaf_for_name_resolution();
+ st_table_list *last_leaf_for_name_resolution();
+ bool is_leaf_for_name_resolution();
inline bool prepare_check_option(THD *thd)
{
bool res= FALSE;
@@ -514,6 +633,10 @@ private:
class Item;
+/*
+ Iterator over the fields of a generic table reference.
+*/
+
class Field_iterator: public Sql_alloc
{
public:
@@ -527,6 +650,11 @@ public:
};
+/*
+ Iterator over the fields of a base table, view with temporary
+ table, or subquery.
+*/
+
class Field_iterator_table: public Field_iterator
{
Field **ptr;
@@ -542,6 +670,8 @@ public:
};
+/* Iterator over the fields of a merge view. */
+
class Field_iterator_view: public Field_iterator
{
Field_translator *ptr, *array_end;
@@ -555,8 +685,70 @@ public:
Item *create_item(THD *thd);
Item **item_ptr() {return &ptr->item; }
Field *field() { return 0; }
-
inline Item *item() { return ptr->item; }
+ Field_translator *field_translator() { return ptr; }
+};
+
+
+/*
+ Field_iterator interface to the list of materialized fields of a
+ NATURAL/USING join.
+*/
+
+class Field_iterator_natural_join: public Field_iterator
+{
+ List_iterator_fast<Natural_join_column> *column_ref_it;
+ Natural_join_column *cur_column_ref;
+public:
+ Field_iterator_natural_join() :column_ref_it(NULL), cur_column_ref(NULL) {}
+ ~Field_iterator_natural_join() { delete column_ref_it; }
+ void set(TABLE_LIST *table);
+ void next();
+ bool end_of_fields() { return !cur_column_ref; }
+ const char *name() { return cur_column_ref->name(); }
+ Item *create_item(THD *thd) { return cur_column_ref->create_item(thd); }
+ Field *field() { return cur_column_ref->field(); }
+ Natural_join_column *column_ref() { return cur_column_ref; }
+};
+
+
+/*
+ Generic iterator over the fields of an arbitrary table reference.
+
+ DESCRIPTION
+ This class unifies the various ways of iterating over the columns
+ of a table reference depending on the type of SQL entity it
+ represents. If such an entity represents a nested table reference,
+ this iterator encapsulates the iteration over the columns of the
+ members of the table reference.
+
+ IMPLEMENTATION
+ The implementation assumes that all underlying NATURAL/USING table
+ references already contain their result columns and are linked into
+ the list TABLE_LIST::next_name_resolution_table.
+*/
+
+class Field_iterator_table_ref: public Field_iterator
+{
+ TABLE_LIST *table_ref, *first_leaf, *last_leaf;
+ Field_iterator_table table_field_it;
+ Field_iterator_view view_field_it;
+ Field_iterator_natural_join natural_join_it;
+ Field_iterator *field_it;
+ void set_field_iterator();
+public:
+ Field_iterator_table_ref() :field_it(NULL) {}
+ void set(TABLE_LIST *table);
+ void next();
+ bool end_of_fields()
+ { return (table_ref == last_leaf && field_it->end_of_fields()); }
+ const char *name() { return field_it->name(); }
+ const char *table_name();
+ const char *db_name();
+ GRANT_INFO *grant();
+ Item *create_item(THD *thd) { return field_it->create_item(thd); }
+ Field *field() { return field_it->field(); }
+ Natural_join_column *get_or_create_column_ref(THD *thd, bool *is_created);
};