summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc35
-rw-r--r--sql/field.h1
-rw-r--r--sql/ha_myisam.h8
-rw-r--r--sql/handler.h3
-rw-r--r--sql/item.cc65
-rw-r--r--sql/item.h4
-rw-r--r--sql/item_func.cc21
-rw-r--r--sql/item_subselect.cc2
-rw-r--r--sql/mysql_priv.h2
-rw-r--r--sql/opt_range.cc2
-rw-r--r--sql/sp_head.cc4
-rw-r--r--sql/sql_base.cc75
-rw-r--r--sql/sql_lex.cc14
-rw-r--r--sql/sql_parse.cc14
-rw-r--r--sql/sql_select.cc4
-rw-r--r--sql/sql_union.cc14
-rw-r--r--sql/table.h14
17 files changed, 188 insertions, 94 deletions
diff --git a/sql/field.cc b/sql/field.cc
index aa2c5805bfe..f95eaaba5df 100644
--- a/sql/field.cc
+++ b/sql/field.cc
@@ -261,6 +261,7 @@ static Field::field_cast_enum field_cast_date[]=
Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP};
static Field::field_cast_enum field_cast_newdate[]=
{Field::FIELD_CAST_NEWDATE,
+ Field::FIELD_CAST_DATE,
Field::FIELD_CAST_DATETIME,
Field::FIELD_CAST_STRING, Field::FIELD_CAST_VARSTRING,
Field::FIELD_CAST_BLOB, Field::FIELD_CAST_STOP};
@@ -6834,6 +6835,40 @@ Field *make_field(char *ptr, uint32 field_length,
}
+/*
+ Check if field_type is appropriate field type
+ to create field for tmp table using
+ item->tmp_table_field() method
+
+ SYNOPSIS
+ field_types_to_be_kept()
+ field_type - field type
+
+ NOTE
+ it is used in function get_holder_example_field()
+ from item.cc
+
+ RETURN
+ 1 - can use item->tmp_table_field() method
+ 0 - can not use item->tmp_table_field() method
+
+*/
+
+bool field_types_to_be_kept(enum_field_types field_type)
+{
+ switch (field_type)
+ {
+ case FIELD_TYPE_DATE:
+ case FIELD_TYPE_NEWDATE:
+ case FIELD_TYPE_TIME:
+ case FIELD_TYPE_DATETIME:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+
/* Create a field suitable for create of table */
create_field::create_field(Field *old_field,Field *orig_field)
diff --git a/sql/field.h b/sql/field.h
index af300c8d471..756fa713707 100644
--- a/sql/field.h
+++ b/sql/field.h
@@ -1333,6 +1333,7 @@ enum_field_types get_blob_type_from_length(ulong length);
uint32 calc_pack_length(enum_field_types type,uint32 length);
int set_field_to_null(Field *field);
int set_field_to_null_with_conversions(Field *field, bool no_conversions);
+bool field_types_to_be_kept(enum_field_types field_type);
/*
The following are for the interface with the .frm file
diff --git a/sql/ha_myisam.h b/sql/ha_myisam.h
index d2fe36c8357..7e14a3b7941 100644
--- a/sql/ha_myisam.h
+++ b/sql/ha_myisam.h
@@ -88,8 +88,12 @@ class ha_myisam: public handler
ft_handler->please->reinit_search(ft_handler);
return 0;
}
- FT_INFO *ft_init_ext(uint flags, uint inx,const byte *key, uint keylen)
- { return ft_init_search(flags,file,inx,(byte*) key,keylen, table->record[0]); }
+ FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
+ {
+ return ft_init_search(flags,file,inx,
+ (byte *)key->ptr(), key->length(), key->charset(),
+ table->record[0]);
+ }
int ft_read(byte *buf);
int rnd_init(bool scan);
int rnd_next(byte *buf);
diff --git a/sql/handler.h b/sql/handler.h
index 04f196dccca..8ad49456bf6 100644
--- a/sql/handler.h
+++ b/sql/handler.h
@@ -455,8 +455,7 @@ public:
int compare_key(key_range *range);
virtual int ft_init() { return HA_ERR_WRONG_COMMAND; }
void ft_end() { ft_handler=NULL; }
- virtual FT_INFO *ft_init_ext(uint flags,uint inx,const byte *key,
- uint keylen)
+ virtual FT_INFO *ft_init_ext(uint flags, uint inx,String *key)
{ return NULL; }
virtual int ft_read(byte *buf) { return HA_ERR_WRONG_COMMAND; }
virtual int rnd_next(byte *buf)=0;
diff --git a/sql/item.cc b/sql/item.cc
index 08de3889ae6..a0d1d8cbacf 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -3633,7 +3633,53 @@ void Item_cache_row::bring_value()
}
-Item_type_holder::Item_type_holder(THD *thd, Item *item)
+/*
+ Returns field for temporary table dependind on item type
+
+ SYNOPSIS
+ get_holder_example_field()
+ thd - thread handler
+ item - pointer to item
+ table - empty table object
+
+ NOTE
+ It is possible to return field for Item_func
+ items only if field type of this item is
+ date or time or datetime type.
+ also see function field_types_to_be_kept() from
+ field.cc
+
+ RETURN
+ # - field
+ 0 - no field
+*/
+
+Field *get_holder_example_field(THD *thd, Item *item, TABLE *table)
+{
+ DBUG_ASSERT(table);
+
+ Item_func *tmp_item= 0;
+ if (item->type() == Item::FIELD_ITEM)
+ return (((Item_field*) item)->field);
+ if (item->type() == Item::FUNC_ITEM)
+ tmp_item= (Item_func *) item;
+ else if (item->type() == Item::SUM_FUNC_ITEM)
+ {
+ Item_sum *item_sum= (Item_sum *) item;
+ if (item_sum->keep_field_type())
+ {
+ if (item_sum->args[0]->type() == Item::FIELD_ITEM)
+ return (((Item_field*) item_sum->args[0])->field);
+ if (item_sum->args[0]->type() == Item::FUNC_ITEM)
+ tmp_item= (Item_func *) item_sum->args[0];
+ }
+ }
+ return (tmp_item && field_types_to_be_kept(tmp_item->field_type()) ?
+ tmp_item->tmp_table_field(table) : 0);
+}
+
+
+Item_type_holder::Item_type_holder(THD *thd, Item *item, TABLE *table)
:Item(thd, item), item_type(item->result_type()),
orig_type(item_type)
{
@@ -3643,10 +3689,7 @@ Item_type_holder::Item_type_holder(THD *thd, Item *item)
It is safe assign pointer on field, because it will be used just after
all JOIN::prepare calls and before any SELECT execution
*/
- if (item->type() == Item::FIELD_ITEM)
- field_example= ((Item_field*) item)->field;
- else
- field_example= 0;
+ field_example= get_holder_example_field(thd, item, table);
max_length= real_length(item);
maybe_null= item->maybe_null;
collation.set(item->collation);
@@ -3686,25 +3729,23 @@ inline bool is_attr_compatible(Item *from, Item *to)
(to->maybe_null || !from->maybe_null) &&
(to->result_type() != STRING_RESULT ||
from->result_type() != STRING_RESULT ||
- my_charset_same(from->collation.collation,
- to->collation.collation)));
+ (from->collation.collation == to->collation.collation)));
}
-bool Item_type_holder::join_types(THD *thd, Item *item)
+bool Item_type_holder::join_types(THD *thd, Item *item, TABLE *table)
{
uint32 new_length= real_length(item);
bool use_new_field= 0, use_expression_type= 0;
Item_result new_result_type= type_convertor[item_type][item->result_type()];
- bool item_is_a_field= item->type() == Item::FIELD_ITEM;
-
+ Field *field= get_holder_example_field(thd, item, table);
+ bool item_is_a_field= field;
/*
Check if both items point to fields: in this case we
can adjust column types of result table in the union smartly.
*/
if (field_example && item_is_a_field)
{
- Field *field= ((Item_field *)item)->field;
/* Can 'field_example' field store data of the column? */
if ((use_new_field=
(!field->field_cast_compatible(field_example->field_cast_type()) ||
@@ -3745,7 +3786,7 @@ bool Item_type_holder::join_types(THD *thd, Item *item)
It is safe to assign a pointer to field here, because it will be used
before any table is closed.
*/
- field_example= ((Item_field*) item)->field;
+ field_example= field;
}
old_cs= collation.collation->name;
diff --git a/sql/item.h b/sql/item.h
index 6857b4acf82..8209c566025 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -1579,14 +1579,14 @@ protected:
Item_result orig_type;
Field *field_example;
public:
- Item_type_holder(THD*, Item*);
+ Item_type_holder(THD*, Item*, TABLE *);
Item_result result_type () const { return item_type; }
enum Type type() const { return TYPE_HOLDER; }
double val_real();
longlong val_int();
String *val_str(String*);
- bool join_types(THD *thd, Item *);
+ bool join_types(THD *thd, Item *, TABLE *);
Field *example() { return field_example; }
static uint32 real_length(Item *item);
void cleanup()
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 8ee1891eafd..7731ccee773 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -2377,16 +2377,17 @@ longlong Item_func_release_lock::val_int()
longlong Item_func_last_insert_id::val_int()
{
+ THD *thd= current_thd;
DBUG_ASSERT(fixed == 1);
if (arg_count)
{
- longlong value=args[0]->val_int();
- current_thd->insert_id(value);
- null_value=args[0]->null_value;
+ longlong value= args[0]->val_int();
+ thd->insert_id(value);
+ null_value= args[0]->null_value;
+ return value; // Avoid side effect of insert_id()
}
- else
- current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
- return current_thd->insert_id();
+ thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT);
+ return thd->insert_id();
}
/* This function is just used to test speed of different functions */
@@ -3138,9 +3139,7 @@ void Item_func_match::init_search(bool no_order)
if (join_key && !no_order)
flags|=FT_SORTED;
- ft_handler=table->file->ft_init_ext(flags, key,
- (byte*) ft_tmp->ptr(),
- ft_tmp->length());
+ ft_handler=table->file->ft_init_ext(flags, key, ft_tmp);
if (join_key)
table->file->ft_handler=ft_handler;
@@ -3182,12 +3181,12 @@ bool Item_func_match::fix_fields(THD *thd, TABLE_LIST *tlist, Item **ref)
}
/*
Check that all columns come from the same table.
- We've already checked that columns in MATCH are fields so
+ We've already checked that columns in MATCH are fields so
PARAM_TABLE_BIT can only appear from AGAINST argument.
*/
if ((used_tables_cache & ~PARAM_TABLE_BIT) != item->used_tables())
key=NO_SUCH_KEY;
-
+
if (key == NO_SUCH_KEY && !(flags & FT_BOOL))
{
my_error(ER_WRONG_ARGUMENTS,MYF(0),"MATCH");
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 3ac75bfdd30..b5cb01494fa 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -53,7 +53,7 @@ void Item_subselect::init(st_select_lex *select_lex,
{
DBUG_ENTER("Item_subselect::init");
- DBUG_PRINT("subs", ("select_lex 0x%xl", (ulong) select_lex));
+ DBUG_PRINT("enter", ("select_lex: 0x%x", (ulong) select_lex));
unit= select_lex->master_unit();
if (unit->item)
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index d0ae15daff9..2cabcb6d49c 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -1332,7 +1332,7 @@ inline void setup_table_map(TABLE *table, TABLE_LIST *table_list, uint tablenr)
table->null_row= 0;
table->status= STATUS_NO_RECORD;
table->keys_in_use_for_query= table->s->keys_in_use;
- table->maybe_null= test(table->outer_join= table_list->outer_join);
+ table->maybe_null= table_list->outer_join;
table->tablenr= tablenr;
table->map= (table_map) 1 << tablenr;
table->force_index= table_list->force_index;
diff --git a/sql/opt_range.cc b/sql/opt_range.cc
index e5799bfd509..ceb9f97bbbc 100644
--- a/sql/opt_range.cc
+++ b/sql/opt_range.cc
@@ -3611,7 +3611,7 @@ get_mm_leaf(PARAM *param, COND *conf_func, Field *field, KEY_PART *key_part,
if (!value) // IS NULL or IS NOT NULL
{
- if (field->table->outer_join) // Can't use a key on this
+ if (field->table->maybe_null) // Can't use a key on this
DBUG_RETURN(0);
if (!maybe_null) // Not null field
DBUG_RETURN(type == Item_func::ISNULL_FUNC ? &null_element : 0);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 3f2969768c5..d52474998a8 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -424,10 +424,10 @@ sp_head::~sp_head()
void
sp_head::destroy()
{
- DBUG_ENTER("sp_head::destroy");
- DBUG_PRINT("info", ("name: %s", m_name.str));
sp_instr *i;
LEX *lex;
+ DBUG_ENTER("sp_head::destroy");
+ DBUG_PRINT("info", ("name: %s", m_name.str));
for (uint ip = 0 ; (i = get_instr(ip)) ; ip++)
delete i;
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 8a7ae2dffc3..5f9f743932d 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -497,58 +497,58 @@ void close_temporary(TABLE *table,bool delete_table)
void close_temporary_tables(THD *thd)
{
TABLE *table,*next;
- char *query, *end;
- uint query_buf_size;
- bool found_user_tables = 0;
+ char *query, *name_in_query, *end;
+ uint greatest_key_length= 0;
if (!thd->temporary_tables)
return;
+ /*
+ We write a DROP TEMPORARY TABLE for each temp table left, so that our
+ replication slave can clean them up. Not one multi-table DROP TABLE binlog
+ event: this would cause problems if slave uses --replicate-*-table.
+ */
LINT_INIT(end);
- query_buf_size= 50; // Enough for DROP ... TABLE IF EXISTS
+ /* We'll re-use always same buffer so make it big enough for longest name */
for (table=thd->temporary_tables ; table ; table=table->next)
- /*
- We are going to add 4 ` around the db/table names, so 1 does not look
- enough; indeed it is enough, because table->key_length is greater (by 8,
- because of server_id and thread_id) than db||table.
- */
- query_buf_size+= table->s->key_length+1;
+ greatest_key_length= max(greatest_key_length, table->s->key_length);
- if ((query = alloc_root(thd->mem_root, query_buf_size)))
+ if ((query = alloc_root(thd->mem_root, greatest_key_length+50)))
// Better add "if exists", in case a RESET MASTER has been done
- end=strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS ");
+ name_in_query= strmov(query, "DROP /*!40005 TEMPORARY */ TABLE IF EXISTS `");
for (table=thd->temporary_tables ; table ; table=next)
{
- if (query) // we might be out of memory, but this is not fatal
+ /*
+ In we are OOM for 'query' this is not fatal. We skip temporary tables
+ not created directly by the user.
+ */
+ if (query && mysql_bin_log.is_open() && (table->s->table_name[0] != '#'))
{
- // skip temporary tables not created directly by the user
- if (table->s->table_name[0] != '#')
- found_user_tables = 1;
- end = strxmov(end,"`",table->s->db,"`.`",
- table->s->table_name,"`,", NullS);
+ /*
+ Here we assume table_cache_key always starts
+ with \0 terminated db name
+ */
+ end = strxmov(name_in_query, table->s->db, "`.`",
+ table->s->table_name, "`", NullS);
+ Query_log_event qinfo(thd, query, (ulong)(end-query), 0, FALSE);
+ /*
+ Imagine the thread had created a temp table, then was doing a SELECT, and
+ the SELECT was killed. Then it's not clever to mark the statement above as
+ "killed", because it's not really a statement updating data, and there
+ are 99.99% chances it will succeed on slave. And, if thread is
+ killed now, it's not clever either.
+ If a real update (one updating a persistent table) was killed on the
+ master, then this real update will be logged with error_code=killed,
+ rightfully causing the slave to stop.
+ */
+ qinfo.error_code= 0;
+ mysql_bin_log.write(&qinfo);
}
next=table->next;
close_temporary(table);
}
- if (query && found_user_tables && mysql_bin_log.is_open())
- {
- /* The -1 is to remove last ',' */
- thd->clear_error();
- Query_log_event qinfo(thd, query, (ulong)(end-query)-1, 0, FALSE);
- /*
- Imagine the thread had created a temp table, then was doing a SELECT, and
- the SELECT was killed. Then it's not clever to mark the statement above as
- "killed", because it's not really a statement updating data, and there
- are 99.99% chances it will succeed on slave.
- If a real update (one updating a persistent table) was killed on the
- master, then this real update will be logged with error_code=killed,
- rightfully causing the slave to stop.
- */
- qinfo.error_code= 0;
- mysql_bin_log.write(&qinfo);
- }
thd->temporary_tables=0;
}
@@ -854,7 +854,7 @@ TABLE *reopen_name_locked_table(THD* thd, TABLE_LIST* table_list)
table->tablenr=thd->current_tablenr++;
table->used_fields=0;
table->const_table=0;
- table->outer_join= table->null_row= table->maybe_null= table->force_index= 0;
+ table->null_row= table->maybe_null= table->force_index= 0;
table->status=STATUS_NO_RECORD;
table->keys_in_use_for_query= share->keys_in_use;
table->used_keys= share->keys_for_keyread;
@@ -1078,7 +1078,7 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table->tablenr=thd->current_tablenr++;
table->used_fields=0;
table->const_table=0;
- table->outer_join= table->null_row= table->maybe_null= table->force_index= 0;
+ table->null_row= table->maybe_null= table->force_index= 0;
table->status=STATUS_NO_RECORD;
table->keys_in_use_for_query= table->s->keys_in_use;
table->insert_values= 0;
@@ -1150,7 +1150,6 @@ bool reopen_table(TABLE *table,bool locked)
tmp.tablenr= table->tablenr;
tmp.used_fields= table->used_fields;
tmp.const_table= table->const_table;
- tmp.outer_join= table->outer_join;
tmp.null_row= table->null_row;
tmp.maybe_null= table->maybe_null;
tmp.status= table->status;
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 06e271333bf..0644ca5af68 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -115,9 +115,14 @@ void lex_free(void)
void lex_start(THD *thd, uchar *buf,uint length)
{
LEX *lex= thd->lex;
+ DBUG_ENTER("lex_start");
+
+ lex->thd= lex->unit.thd= thd;
+ lex->buf= lex->ptr= buf;
+ lex->end_of_query= buf+length;
+
lex->unit.init_query();
lex->unit.init_select();
- lex->thd= lex->unit.thd= thd;
lex->select_lex.init_query();
lex->value_list.empty();
lex->update_list.empty();
@@ -150,8 +155,6 @@ void lex_start(THD *thd, uchar *buf,uint length)
lex->empty_field_list_on_rset= 0;
lex->select_lex.select_number= 1;
lex->next_state=MY_LEX_START;
- lex->buf= lex->ptr= buf;
- lex->end_of_query=buf+length;
lex->yylineno = 1;
lex->in_comment=0;
lex->length=0;
@@ -173,14 +176,11 @@ void lex_start(THD *thd, uchar *buf,uint length)
if (lex->spfuns.records)
my_hash_reset(&lex->spfuns);
+ DBUG_VOID_RETURN;
}
void lex_end(LEX *lex)
{
- for (SELECT_LEX *sl= lex->all_selects_list;
- sl;
- sl= sl->next_select_in_list())
- sl->expr_list.delete_elements(); // If error when parsing sql-varargs
x_free(lex->yacc_yyss);
x_free(lex->yacc_yyvs);
}
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index d309f58a37c..2fb90502863 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -4719,19 +4719,21 @@ bool
mysql_new_select(LEX *lex, bool move_down)
{
SELECT_LEX *select_lex;
+ DBUG_ENTER("mysql_new_select");
+
if (!(select_lex= new(lex->thd->mem_root) SELECT_LEX()))
- return 1;
+ DBUG_RETURN(1);
select_lex->select_number= ++lex->thd->select_number;
select_lex->init_query();
select_lex->init_select();
select_lex->parent_lex= lex;
if (move_down)
{
+ SELECT_LEX_UNIT *unit;
lex->subqueries= TRUE;
/* first select_lex of subselect or derived table */
- SELECT_LEX_UNIT *unit;
if (!(unit= new(lex->thd->mem_root) SELECT_LEX_UNIT()))
- return 1;
+ DBUG_RETURN(1);
unit->init_query();
unit->init_select();
@@ -4748,7 +4750,7 @@ mysql_new_select(LEX *lex, bool move_down)
if (lex->current_select->order_list.first && !lex->current_select->braces)
{
my_error(ER_WRONG_USAGE, MYF(0), "UNION", "ORDER BY");
- return 1;
+ DBUG_RETURN(1);
}
select_lex->include_neighbour(lex->current_select);
SELECT_LEX_UNIT *unit= select_lex->master_unit();
@@ -4760,7 +4762,7 @@ mysql_new_select(LEX *lex, bool move_down)
fake SELECT_LEX for UNION processing
*/
if (!(fake= unit->fake_select_lex= new(lex->thd->mem_root) SELECT_LEX()))
- return 1;
+ DBUG_RETURN(1);
fake->include_standalone(unit,
(SELECT_LEX_NODE**)&unit->fake_select_lex);
fake->select_number= INT_MAX;
@@ -4774,7 +4776,7 @@ mysql_new_select(LEX *lex, bool move_down)
select_lex->include_global((st_select_lex_node**)&lex->all_selects_list);
lex->current_select= select_lex;
select_lex->resolve_mode= SELECT_LEX::SELECT_MODE;
- return 0;
+ DBUG_RETURN(0);
}
/*
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 89b84f40eb6..68438f7a785 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -9181,7 +9181,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
tab->info="const row not found";
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
- if (!table->outer_join || error > 0)
+ if (!table->maybe_null || error > 0)
DBUG_RETURN(error);
}
}
@@ -9200,7 +9200,7 @@ join_read_const_table(JOIN_TAB *tab, POSITION *pos)
tab->info="unique row not found";
/* Mark for EXPLAIN that the row was not found */
pos->records_read=0.0;
- if (!table->outer_join || error > 0)
+ if (!table->maybe_null || error > 0)
DBUG_RETURN(error);
}
if (table->key_read)
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 0a91eb4c0e1..29897c70023 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -153,6 +153,7 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
SELECT_LEX *sl, *first_select;
select_result *tmp_result;
bool is_union;
+ TABLE *empty_table= 0;
DBUG_ENTER("st_select_lex_unit::prepare");
describe= test(additional_options & SELECT_DESCRIBE);
@@ -249,13 +250,21 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
goto err;
if (sl == first_select)
{
+ /*
+ We need to create an empty table object. It is used
+ to create tmp_table fields in Item_type_holder.
+ The main reason of this is that we can't create
+ field object without table.
+ */
+ DBUG_ASSERT(!empty_table);
+ empty_table= (TABLE*) thd->calloc(sizeof(TABLE));
types.empty();
List_iterator_fast<Item> it(sl->item_list);
Item *item_tmp;
while ((item_tmp= it++))
{
/* Error's in 'new' will be detected after loop */
- types.push_back(new Item_type_holder(thd_arg, item_tmp));
+ types.push_back(new Item_type_holder(thd_arg, item_tmp, empty_table));
}
if (thd_arg->is_fatal_error)
@@ -274,7 +283,8 @@ bool st_select_lex_unit::prepare(THD *thd_arg, select_result *sel_result,
Item *type, *item_tmp;
while ((type= tp++, item_tmp= it++))
{
- if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp))
+ if (((Item_type_holder*)type)->join_types(thd_arg, item_tmp,
+ empty_table))
DBUG_RETURN(TRUE);
}
}
diff --git a/sql/table.h b/sql/table.h
index 432267bf72b..8dcd4e658db 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -217,14 +217,18 @@ struct st_table {
uint derived_select_number;
int current_lock; /* Type of lock on table */
my_bool copy_blobs; /* copy_blobs when storing */
+
+ /*
+ 0 or JOIN_TYPE_{LEFT|RIGHT}. Currently this is only compared to 0.
+ If maybe_null !=0, this table is inner w.r.t. some outer join operation,
+ and null_row may be true.
+ */
+ uint maybe_null;
/*
- Used in outer joins: if true, all columns are considered to have NULL
- values, including columns declared as "not null".
+ If true, the current table row is considered to have all columns set to
+ NULL, including columns declared as "not null" (see maybe_null).
*/
my_bool null_row;
- /* 0 or JOIN_TYPE_{LEFT|RIGHT}, same as TABLE_LIST::outer_join */
- my_bool outer_join;
- my_bool maybe_null; /* true if (outer_join != 0) */
my_bool force_index;
my_bool distinct,const_table,no_rows;
my_bool key_read, no_keyread;