Commit 2385ded0 authored by unknown's avatar unknown

Merge abelkin@work.mysql.com:/home/bk/mysql-4.1

into sanja.is.com.ua:/home/bell/mysql/mysql-4.1

parents 9c7d0627 e5573175
***************
*** 65,71 ****
static int return_zero_rows(select_result *res,TABLE_LIST *tables,
List<Item> &fields, bool send_row,
uint select_options, const char *info,
- Item *having, Procedure *proc);
static COND *optimize_cond(COND *conds,Item::cond_result *cond_value);
static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value);
static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
--- 65,72 ----
static int return_zero_rows(select_result *res,TABLE_LIST *tables,
List<Item> &fields, bool send_row,
uint select_options, const char *info,
+ Item *having, Procedure *proc,
+ SELECT_LEX *select_lex);
static COND *optimize_cond(COND *conds,Item::cond_result *cond_value);
static COND *remove_eq_conds(COND *cond,Item::cond_result *cond_value);
static bool const_expression_in_where(COND *conds,Item *item, Item **comp_item);
***************
*** 180,228 ****
** mysql_select assumes that all tables are already opened
*****************************************************************************/
int
- mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
- ORDER *order, ORDER *group,Item *having,ORDER *proc_param,
- ulong select_options,select_result *result)
- {
- TABLE *tmp_table;
- int error, tmp_error;
- bool need_tmp,hidden_group_fields;
- bool simple_order,simple_group,no_order, skip_sort_order, buffer_result;
- Item::cond_result cond_value;
- SQL_SELECT *select;
- DYNAMIC_ARRAY keyuse;
- JOIN join;
- Procedure *procedure;
- List<Item> all_fields(fields);
- bool select_distinct;
- SELECT_LEX *select_lex = &(thd->lex.select_lex);
- SELECT_LEX *cur_sel = thd->lex.select;
- DBUG_ENTER("mysql_select");
- /* Check that all tables, fields, conds and order are ok */
- select_distinct=test(select_options & SELECT_DISTINCT);
- buffer_result=test(select_options & OPTION_BUFFER_RESULT) && !test(select_options & OPTION_FOUND_ROWS);
- tmp_table=0;
- select=0;
- no_order=skip_sort_order=0;
- bzero((char*) &keyuse,sizeof(keyuse));
- thd->proc_info="init";
- thd->used_tables=0; // Updated by setup_fields
- if (setup_tables(tables) ||
- setup_fields(thd,tables,fields,1,&all_fields,1) ||
- setup_conds(thd,tables,&conds) ||
- setup_order(thd,tables,fields,all_fields,order) ||
- setup_group(thd,tables,fields,all_fields,group,&hidden_group_fields))
DBUG_RETURN(-1); /* purecov: inspected */
if (having)
{
thd->where="having clause";
thd->allow_sum_func=1;
- if (having->fix_fields(thd,tables) || thd->fatal_error)
DBUG_RETURN(-1); /* purecov: inspected */
if (having->with_sum_func)
having->split_sum_func(all_fields);
--- 195,237 ----
** mysql_select assumes that all tables are already opened
*****************************************************************************/
+ /*
+ Prepare of whole select (including subselect in future).
+ return -1 on error
+ 0 on success
+ */
int
+ JOIN::prepare(TABLE_LIST *tables_init,
+ COND *conds_init, ORDER *order_init, ORDER *group_init,
+ Item *having_init,
+ ORDER *proc_param_init, SELECT_LEX *select)
+ {
+ DBUG_ENTER("JOIN::prepare");
+
+ conds= conds_init;
+ order= order_init;
+ group_list= group_init;
+ having= having_init;
+ proc_param= proc_param_init;
+ tables_list= tables_init;
+ select_lex= select;
+ /* Check that all tables, fields, conds and order are ok */
+ if (setup_tables(tables_list) ||
+ setup_fields(thd,tables_list,fields_list,1,&all_fields,1) ||
+ setup_conds(thd,tables_list,&conds) ||
+ setup_order(thd,tables_list,fields_list,all_fields,order) ||
+ setup_group(thd,tables_list,fields_list,all_fields,group_list,
+ &hidden_group_fields))
DBUG_RETURN(-1); /* purecov: inspected */
if (having)
{
thd->where="having clause";
thd->allow_sum_func=1;
+ if (having->fix_fields(thd,tables_list) || thd->fatal_error)
DBUG_RETURN(-1); /* purecov: inspected */
if (having->with_sum_func)
having->split_sum_func(all_fields);
***************
*** 297,347 ****
}
/* Init join struct */
- join.thd=thd;
- join.lock=thd->lock;
- join.join_tab=0;
- join.tmp_table_param.copy_field=0;
- join.sum_funcs=0;
- join.send_records=join.found_records=join.examined_rows=0;
- join.tmp_table_param.end_write_records= HA_POS_ERROR;
- join.first_record=join.sort_and_group=0;
- join.select_options=select_options;
- join.result=result;
- count_field_types(&join.tmp_table_param,all_fields,0);
- join.const_tables=0;
- join.having=0;
- join.do_send_rows = 1;
- join.group= group != 0;
- join.row_limit= ((select_distinct || order || group) ? HA_POS_ERROR :
- thd->select_limit);
#ifdef RESTRICTED_GROUP
- if (join.sum_func_count && !group && (join.func_count || join.field_count))
{
my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT),MYF(0));
delete procedure;
DBUG_RETURN(-1);
}
#endif
- if (!procedure && result->prepare(fields))
{ /* purecov: inspected */
DBUG_RETURN(-1); /* purecov: inspected */
}
#ifdef HAVE_REF_TO_FIELDS // Not done yet
/* Add HAVING to WHERE if possible */
- if (having && !group && ! join.sum_func_count)
{
if (!conds)
{
- conds=having;
- having=0;
}
else if ((conds=new Item_cond_and(conds,having)))
{
- conds->fix_fields(thd,tables);
- conds->change_ref_to_fields(thd,tables);
- having=0;
}
}
#endif
--- 305,358 ----
}
/* Init join struct */
+ count_field_types(&tmp_table_param, all_fields, 0);
+ this->group= group_list != 0;
+ row_limit= ((select_distinct || order || group_list) ? HA_POS_ERROR :
+ select_lex->first_in_union->select_limit_cnt);
#ifdef RESTRICTED_GROUP
+ if (sum_func_count && !group_list && (func_count || field_count))
{
my_message(ER_WRONG_SUM_SELECT,ER(ER_WRONG_SUM_SELECT),MYF(0));
delete procedure;
DBUG_RETURN(-1);
}
#endif
+ if (!procedure && result->prepare(fields_list, select_lex->first_in_union))
{ /* purecov: inspected */
DBUG_RETURN(-1); /* purecov: inspected */
}
+ DBUG_RETURN(0); // All OK
+ }
+
+ /*
+ global select optimisation.
+ return 0 - success
+ 1 - go out
+ -1 - go out with cleaning
+
+ error code saved in field 'error'
+ */
+ int
+ JOIN::optimize()
+ {
+ DBUG_ENTER("JOIN::optimize");
+ SELECT_LEX *select_lex = &(thd->lex.select_lex);
#ifdef HAVE_REF_TO_FIELDS // Not done yet
/* Add HAVING to WHERE if possible */
+ if (having && !group_list && ! sum_func_count)
{
if (!conds)
{
+ conds= having;
+ having= 0;
}
else if ((conds=new Item_cond_and(conds,having)))
{
+ conds->fix_fields(thd, tables_list);
+ conds->change_ref_to_fields(thd, tables_list);
+ having= 0;
}
}
#endif
***************
*** 350,459 ****
if (thd->fatal_error) // Out of memory
{
delete procedure;
- DBUG_RETURN(0);
}
- if (cond_value == Item::COND_FALSE || !thd->select_limit)
{ /* Impossible cond */
- if (select_options & SELECT_DESCRIBE && select_lex->next)
- select_describe(&join,false,false,false,"Impossible WHERE");
- else
- error=return_zero_rows(result, tables, fields,
- join.tmp_table_param.sum_func_count != 0 && !group,
- select_options,"Impossible WHERE",having,
- procedure);
- delete procedure;
- DBUG_RETURN(error);
}
/* Optimize count(*), min() and max() */
- if (tables && join.tmp_table_param.sum_func_count && ! group)
{
int res;
- if ((res=opt_sum_query(tables, all_fields, conds)))
{
if (res < 0)
{
- if (select_options & SELECT_DESCRIBE && select_lex->next)
- select_describe(&join,false,false,false,"No matching min/max row");
- else
- error=return_zero_rows(result, tables, fields, !group,
- select_options,"No matching min/max row",
- having,procedure);
- delete procedure;
- DBUG_RETURN(error);
}
if (select_options & SELECT_DESCRIBE)
{
if (select_lex->next)
- select_describe(&join,false,false,false,"Select tables optimized away");
else
- describe_info(thd,"Select tables optimized away");
delete procedure;
- DBUG_RETURN(error);
}
- tables=0; // All tables resolved
}
}
- if (!tables)
- { // Only test of functions
- error=0;
- if (select_options & SELECT_DESCRIBE)
- {
- if (select_lex->next)
- select_describe(&join,false,false,false,"No tables used");
- else
- describe_info(thd,"No tables used");
- }
- else
- {
- result->send_fields(fields,1);
- if (!having || having->val_int())
- {
- if (join.do_send_rows && result->send_data(fields))
- {
- result->send_error(0,NullS); /* purecov: inspected */
- error=1;
- }
- else
- error=(int) result->send_eof();
- }
- else
- error=(int) result->send_eof();
- }
- delete procedure;
- DBUG_RETURN(error);
}
- error = -1;
- join.sort_by_table=get_sort_by_table(order,group,tables);
/* Calculate how to do the join */
- thd->proc_info="statistics";
- if (make_join_statistics(&join,tables,conds,&keyuse) || thd->fatal_error)
- goto err;
- thd->proc_info="preparing";
- result->initialize_tables(&join);
- if (join.const_table_map != join.found_const_table_map &&
!(select_options & SELECT_DESCRIBE))
{
- error=return_zero_rows(result,tables,fields,
- join.tmp_table_param.sum_func_count != 0 &&
- !group,0,"",having,procedure);
- goto err;
}
if (!(thd->options & OPTION_BIG_SELECTS) &&
- join.best_read > (double) thd->max_join_size &&
!(select_options & SELECT_DESCRIBE))
{ /* purecov: inspected */
result->send_error(ER_TOO_BIG_SELECT,ER(ER_TOO_BIG_SELECT)); /* purecov: inspected */
error= 1; /* purecov: inspected */
- goto err; /* purecov: inspected */
}
- if (join.const_tables && !thd->locked_tables &&
!(select_options & SELECT_NO_UNLOCK))
{
TABLE **table, **end;
- for (table=join.table, end=table + join.const_tables ;
table != end;
table++)
{
--- 361,436 ----
if (thd->fatal_error) // Out of memory
{
delete procedure;
+ error = 0;
+ DBUG_RETURN(1);
}
+ if (cond_value == Item::COND_FALSE ||
+ !select_lex->first_in_union->select_limit_cnt)
{ /* Impossible cond */
+ zero_result_cause= "Impossible WHERE";
+ DBUG_RETURN(0);
}
/* Optimize count(*), min() and max() */
+ if (tables_list && tmp_table_param.sum_func_count && ! group_list)
{
int res;
+ if ((res=opt_sum_query(tables_list, all_fields, conds)))
{
if (res < 0)
{
+ zero_result_cause= "No matching min/max row";
+ DBUG_RETURN(0);
}
if (select_options & SELECT_DESCRIBE)
{
if (select_lex->next)
+ select_describe(this, false, false, false,
+ "Select tables optimized away");
else
+ describe_info(thd, "Select tables optimized away");
delete procedure;
+ DBUG_RETURN(1);
}
+ tables_list=0; // All tables resolved
}
}
+ if (!tables_list)
+ {
+ test_function_query= 1;
+ DBUG_RETURN(0);
}
+ error= -1;
+ sort_by_table= get_sort_by_table(order, group_list, tables_list);
/* Calculate how to do the join */
+ thd->proc_info= "statistics";
+ if (make_join_statistics(this, tables_list, conds, &keyuse) ||
+ thd->fatal_error)
+ DBUG_RETURN(-1);
+ thd->proc_info= "preparing";
+ result->initialize_tables(this);
+ if (const_table_map != found_const_table_map &&
!(select_options & SELECT_DESCRIBE))
{
+ zero_result_cause= "";
+ select_options= 0; //TODO why option in return_zero_rows was droped
+ DBUG_RETURN(0);
}
if (!(thd->options & OPTION_BIG_SELECTS) &&
+ best_read > (double) thd->max_join_size &&
!(select_options & SELECT_DESCRIBE))
{ /* purecov: inspected */
result->send_error(ER_TOO_BIG_SELECT,ER(ER_TOO_BIG_SELECT)); /* purecov: inspected */
error= 1; /* purecov: inspected */
+ DBUG_RETURN(-1);
}
+ if (const_tables && !thd->locked_tables &&
!(select_options & SELECT_NO_UNLOCK))
{
TABLE **table, **end;
+ for (table=this->table, end=table + const_tables ;
table != end;
table++)
{
***************
*** 465,561 ****
}
(*table)->file->index_end();
}
- mysql_unlock_some_tables(thd, join.table,join.const_tables);
}
- if (!conds && join.outer_join)
{
/* Handle the case where we have an OUTER JOIN without a WHERE */
conds=new Item_int((longlong) 1,1); // Always true
}
- select=make_select(*join.table, join.const_table_map,
- join.const_table_map,conds,&error);
if (error)
{ /* purecov: inspected */
error= -1; /* purecov: inspected */
- goto err; /* purecov: inspected */
}
- if (make_join_select(&join,select,conds))
{
- if (select_options & SELECT_DESCRIBE && select_lex->next)
- select_describe(&join,false,false,false,"Impossible WHERE noticed after reading const tables");
- else
- error=return_zero_rows(result,tables,fields,
- join.tmp_table_param.sum_func_count != 0 && !group,
- select_options,
- "Impossible WHERE noticed after reading const tables",
- having,procedure);
- goto err;
}
error= -1; /* if goto err */
/* Optimize distinct away if possible */
- order=remove_const(&join,order,conds,&simple_order);
- if (group || join.tmp_table_param.sum_func_count)
{
if (! hidden_group_fields)
select_distinct=0;
}
- else if (select_distinct && join.tables - join.const_tables == 1 &&
- (thd->select_limit == HA_POS_ERROR ||
- (join.select_options & OPTION_FOUND_ROWS) ||
order &&
!(skip_sort_order=
- test_if_skip_sort_order(&join.join_tab[join.const_tables],
- order, thd->select_limit,1))))
{
- if ((group=create_distinct_group(order,fields)))
{
select_distinct=0;
no_order= !order;
- join.group=1; // For end_write_group
}
else if (thd->fatal_error) // End of memory
- goto err;
}
- group=remove_const(&join,group,conds,&simple_group);
- if (!group && join.group)
{
order=0; // The output has only one row
simple_order=1;
}
- calc_group_buffer(&join,group);
- join.send_group_parts=join.tmp_table_param.group_parts; /* Save org parts */
if (procedure && procedure->group)
{
- group=procedure->group=remove_const(&join,procedure->group,conds,
- &simple_group);
- calc_group_buffer(&join,group);
}
- if (test_if_subpart(group,order) ||
- (!group && join.tmp_table_param.sum_func_count))
order=0;
// Can't use sort on head table if using row cache
- if (join.full_join)
{
- if (group)
simple_group=0;
if (order)
simple_order=0;
}
- need_tmp= (join.const_tables != join.tables &&
((select_distinct || !simple_order || !simple_group) ||
- (group && order) || buffer_result));
// No cache for MATCH
- make_join_readinfo(&join,
(select_options & (SELECT_DESCRIBE |
SELECT_NO_JOIN_CACHE)) |
- (cur_sel->ftfunc_list.elements ? SELECT_NO_JOIN_CACHE : 0));
/* Need to tell Innobase that to play it safe, it should fetch all
columns of the tables: this is because MySQL
--- 442,535 ----
}
(*table)->file->index_end();
}
+ mysql_unlock_some_tables(thd, this->table, const_tables);
}
+ if (!conds && outer_join)
{
/* Handle the case where we have an OUTER JOIN without a WHERE */
conds=new Item_int((longlong) 1,1); // Always true
}
+ select=make_select(*table, const_table_map,
+ const_table_map, conds, &error);
if (error)
{ /* purecov: inspected */
error= -1; /* purecov: inspected */
+ DBUG_RETURN(-1);
}
+ if (make_join_select(this, select, conds))
{
+ zero_result_cause=
+ "Impossible WHERE noticed after reading const tables";
+ DBUG_RETURN(0);
}
error= -1; /* if goto err */
/* Optimize distinct away if possible */
+ order=remove_const(this,order,conds,&simple_order);
+ if (group_list || tmp_table_param.sum_func_count)
{
if (! hidden_group_fields)
select_distinct=0;
}
+ else if (select_distinct && tables - const_tables == 1 &&
+ (select_lex->first_in_union->select_limit_cnt == HA_POS_ERROR ||
+ (select_options & OPTION_FOUND_ROWS) ||
order &&
!(skip_sort_order=
+ test_if_skip_sort_order(&join_tab[const_tables],
+ order,
+ select_lex->first_in_union->select_limit_cnt,
+ 1))))
{
+ if ((group_list=create_distinct_group(order, fields_list)))
{
select_distinct=0;
no_order= !order;
+ group=1; // For end_write_group
}
else if (thd->fatal_error) // End of memory
+ DBUG_RETURN(-1);
}
+ group_list= remove_const(this, group_list, conds, &simple_group);
+ if (!group_list && group)
{
order=0; // The output has only one row
simple_order=1;
}
+ calc_group_buffer(this, group_list);
+ send_group_parts=tmp_table_param.group_parts; /* Save org parts */
if (procedure && procedure->group)
{
+ group_list= procedure->group= remove_const(this, procedure->group, conds,
+ &simple_group);
+ calc_group_buffer(this, group_list);
}
+ if (test_if_subpart(group_list, order) ||
+ (!group_list && tmp_table_param.sum_func_count))
order=0;
// Can't use sort on head table if using row cache
+ if (full_join)
{
+ if (group_list)
simple_group=0;
if (order)
simple_order=0;
}
+ need_tmp= (const_tables != tables &&
((select_distinct || !simple_order || !simple_group) ||
+ (group_list && order) || buffer_result));
// No cache for MATCH
+ make_join_readinfo(this,
(select_options & (SELECT_DESCRIBE |
SELECT_NO_JOIN_CACHE)) |
+ (thd->lex.select->ftfunc_list.elements ?
+ SELECT_NO_JOIN_CACHE : 0));
/* Need to tell Innobase that to play it safe, it should fetch all
columns of the tables: this is because MySQL
***************
*** 564,624 ****
by MySQL. */
#ifdef HAVE_INNOBASE_DB
- if (need_tmp || select_distinct || group || order)
{
- for (uint i_h = join.const_tables; i_h < join.tables; i_h++)
{
- TABLE* table_h = join.join_tab[i_h].table;
if (table_h->db_type == DB_TYPE_INNODB)
table_h->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE);
}
}
#endif
- DBUG_EXECUTE("info",TEST_join(&join););
/*
Because filesort always does a full table scan or a quick range scan
we must add the removed reference to the select for the table.
We only need to do this when we have a simple_order or simple_group
as in other cases the join is done before the sort.
*/
- if ((order || group) && join.join_tab[join.const_tables].type != JT_ALL &&
- join.join_tab[join.const_tables].type != JT_FT &&
- (order && simple_order || group && simple_group))
{
- if (add_ref_to_table_cond(thd,&join.join_tab[join.const_tables]))
- goto err;
}
if (!(select_options & SELECT_BIG_RESULT) &&
- ((group && join.const_tables != join.tables &&
(!simple_group ||
- !test_if_skip_sort_order(&join.join_tab[join.const_tables], group,
- thd->select_limit,0))) ||
select_distinct) &&
- join.tmp_table_param.quick_group && !procedure)
{
need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort
}
if (select_options & SELECT_DESCRIBE)
{
if (!order && !no_order)
- order=group;
if (order &&
- (join.const_tables == join.tables ||
(simple_order &&
- test_if_skip_sort_order(&join.join_tab[join.const_tables], order,
- (join.const_tables != join.tables - 1 ||
- (join.select_options & OPTION_FOUND_ROWS)) ?
- HA_POS_ERROR : thd->select_limit,0))))
order=0;
- select_describe(&join,need_tmp,
(order != 0 &&
- (!need_tmp || order != group || simple_group)),
select_distinct);
error=0;
- goto err;
}
/* Perform FULLTEXT search before all regular searches */
--- 538,672 ----
by MySQL. */
#ifdef HAVE_INNOBASE_DB
+ if (need_tmp || select_distinct || group_list || order)
{
+ for (uint i_h = const_tables; i_h < tables; i_h++)
{
+ TABLE* table_h = join_tab[i_h].table;
if (table_h->db_type == DB_TYPE_INNODB)
table_h->file->extra(HA_EXTRA_DONT_USE_CURSOR_TO_UPDATE);
}
}
#endif
+ DBUG_EXECUTE("info",TEST_join(this););
/*
Because filesort always does a full table scan or a quick range scan
we must add the removed reference to the select for the table.
We only need to do this when we have a simple_order or simple_group
as in other cases the join is done before the sort.
*/
+ if ((order || group_list) && join_tab[const_tables].type != JT_ALL &&
+ join_tab[const_tables].type != JT_FT &&
+ (order && simple_order || group_list && simple_group))
{
+ if (add_ref_to_table_cond(thd,&join_tab[const_tables]))
+ DBUG_RETURN(-1);
}
if (!(select_options & SELECT_BIG_RESULT) &&
+ ((group_list && const_tables != tables &&
(!simple_group ||
+ !test_if_skip_sort_order(&join_tab[const_tables], group_list,
+ select_lex->first_in_union->select_limit_cnt,
+ 0))) ||
select_distinct) &&
+ tmp_table_param.quick_group && !procedure)
{
need_tmp=1; simple_order=simple_group=0; // Force tmp table without sort
}
+ DBUG_RETURN(0);
+ }
+
+ /*
+ global uptimisation (with subselect) must be here (TODO)
+ */
+
+ int
+ JOIN::global_optimize()
+ {
+ return 0;
+ }
+
+ /*
+ exec select
+ */
+
+ void
+ JOIN::exec()
+ {
+ int tmp_error;
+
+ DBUG_ENTER("JOIN::exec");
+
+ if (test_function_query)
+ { // Only test of functions
+ error=0;
+ if (select_options & SELECT_DESCRIBE)
+ {
+ if (select_lex->next)
+ select_describe(this, false, false, false, "No tables used");
+ else
+ describe_info(thd, "No tables used");
+ }
+ else
+ {
+ result->send_fields(fields_list,1);
+ if (!having || having->val_int())
+ {
+ if (do_send_rows && result->send_data(fields_list))
+ {
+ result->send_error(0,NullS); /* purecov: inspected */
+ error=1;
+ }
+ else
+ error=(int) result->send_eof();
+ }
+ else
+ error=(int) result->send_eof();
+ }
+ delete procedure;
+ DBUG_VOID_RETURN;
+ }
+
+ if (zero_result_cause)
+ {
+ if (select_options & SELECT_DESCRIBE && select_lex->next)
+ select_describe(this, false, false, false, zero_result_cause);
+ else
+ error=return_zero_rows(result, tables_list, fields_list,
+ tmp_table_param.sum_func_count != 0 &&
+ !group_list,
+ select_options,
+ zero_result_cause,
+ having,procedure,
+ select_lex->first_in_union);
+ DBUG_VOID_RETURN;
+ }
+
+ Item *having_list = having;
+ having = 0;
if (select_options & SELECT_DESCRIBE)
{
if (!order && !no_order)
+ order=group_list;
if (order &&
+ (const_tables == tables ||
(simple_order &&
+ test_if_skip_sort_order(&join_tab[const_tables], order,
+ (const_tables != tables - 1 ||
+ (select_options & OPTION_FOUND_ROWS)) ?
+ HA_POS_ERROR :
+ select_lex->first_in_union->select_limit_cnt,
+ 0))))
order=0;
+ select_describe(this, need_tmp,
(order != 0 &&
+ (!need_tmp || order != group_list || simple_group)),
select_distinct);
error=0;
+ DBUG_VOID_RETURN;
}
/* Perform FULLTEXT search before all regular searches */
***************
*** 630,673 ****
DBUG_PRINT("info",("Creating tmp table"));
thd->proc_info="Creating tmp table";
- if (!(tmp_table =
- create_tmp_table(thd,&join.tmp_table_param,all_fields,
((!simple_group && !procedure &&
!(test_flags & TEST_NO_KEY_GROUP)) ?
- group : (ORDER*) 0),
- group ? 0 : select_distinct,
- group && simple_group,
(order == 0 || skip_sort_order) &&
- !(join.select_options & OPTION_FOUND_ROWS),
- join.select_options)))
- goto err; /* purecov: inspected */
-
- if (having && (join.sort_and_group || (tmp_table->distinct && !group)))
- join.having=having;
/* if group or order on first table, sort first */
- if (group && simple_group)
{
DBUG_PRINT("info",("Sorting for group"));
thd->proc_info="Sorting for group";
- if (create_sort_index(&join.join_tab[join.const_tables],group,
HA_POS_ERROR) ||
- make_sum_func_list(&join,all_fields) ||
- alloc_group_fields(&join,group))
- goto err;
- group=0;
}
else
{
- if (make_sum_func_list(&join,all_fields))
- goto err;
- if (!group && ! tmp_table->distinct && order && simple_order)
{
DBUG_PRINT("info",("Sorting for order"));
thd->proc_info="Sorting for order";
- if (create_sort_index(&join.join_tab[join.const_tables],order,
HA_POS_ERROR))
- goto err; /* purecov: inspected */
order=0;
}
}
--- 678,722 ----
DBUG_PRINT("info",("Creating tmp table"));
thd->proc_info="Creating tmp table";
+ if (!(exec_tmp_table =
+ create_tmp_table(thd,&tmp_table_param,all_fields,
((!simple_group && !procedure &&
!(test_flags & TEST_NO_KEY_GROUP)) ?
+ group_list : (ORDER*) 0),
+ group_list ? 0 : select_distinct,
+ group_list && simple_group,
(order == 0 || skip_sort_order) &&
+ !(select_options & OPTION_FOUND_ROWS),
+ select_options, select_lex->first_in_union)))
+ DBUG_VOID_RETURN;
+
+ if (having_list &&
+ (sort_and_group || (exec_tmp_table->distinct && !group_list)))
+ having=having_list;
/* if group or order on first table, sort first */
+ if (group_list && simple_group)
{
DBUG_PRINT("info",("Sorting for group"));
thd->proc_info="Sorting for group";
+ if (create_sort_index(&join_tab[const_tables],group_list,
HA_POS_ERROR) ||
+ make_sum_func_list(this, all_fields) ||
+ alloc_group_fields(this, group_list))
+ DBUG_VOID_RETURN;
+ group_list=0;
}
else
{
+ if (make_sum_func_list(this, all_fields))
+ DBUG_VOID_RETURN;
+ if (!group_list && ! exec_tmp_table->distinct && order && simple_order)
{
DBUG_PRINT("info",("Sorting for order"));
thd->proc_info="Sorting for order";
+ if (create_sort_index(&join_tab[const_tables], order,
HA_POS_ERROR))
+ DBUG_VOID_RETURN;
order=0;
}
}
***************
*** 678,735 ****
In this case we can stop scanning t2 when we have found one t1.a
*/
- if (tmp_table->distinct)
{
table_map used_tables= thd->used_tables;
- JOIN_TAB *join_tab=join.join_tab+join.tables-1;
do
{
if (used_tables & join_tab->table->map)
break;
join_tab->not_used_in_distinct=1;
- } while (join_tab-- != join.join_tab);
/* Optimize "select distinct b from t1 order by key_part_1 limit #" */
if (order && skip_sort_order)
{
- (void) test_if_skip_sort_order(&join.join_tab[join.const_tables],
- order, thd->select_limit,0);
order=0;
}
}
/* Copy data to the temporary table */
thd->proc_info="Copying to tmp table";
- if ((tmp_error=do_select(&join,(List<Item> *) 0,tmp_table,0)))
{
- error=tmp_error;
- goto err; /* purecov: inspected */
}
- if (join.having)
- join.having=having=0; // Allready done
/* Change sum_fields reference to calculated fields in tmp_table */
- if (join.sort_and_group || tmp_table->group)
{
if (change_to_use_tmp_fields(all_fields))
- goto err;
- join.tmp_table_param.field_count+=join.tmp_table_param.sum_func_count+
- join.tmp_table_param.func_count;
- join.tmp_table_param.sum_func_count=join.tmp_table_param.func_count=0;
}
else
{
if (change_refs_to_tmp_fields(thd,all_fields))
- goto err;
- join.tmp_table_param.field_count+=join.tmp_table_param.func_count;
- join.tmp_table_param.func_count=0;
}
if (procedure)
procedure->update_refs();
- if (tmp_table->group)
{ // Already grouped
if (!order && !no_order)
- order=group; /* order by group */
- group=0;
}
/*
--- 727,786 ----
In this case we can stop scanning t2 when we have found one t1.a
*/
+ if (exec_tmp_table->distinct)
{
table_map used_tables= thd->used_tables;
+ JOIN_TAB *join_tab= this->join_tab+tables-1;
do
{
if (used_tables & join_tab->table->map)
break;
join_tab->not_used_in_distinct=1;
+ } while (join_tab-- != this->join_tab);
/* Optimize "select distinct b from t1 order by key_part_1 limit #" */
if (order && skip_sort_order)
{
+ (void) test_if_skip_sort_order(&this->join_tab[const_tables],
+ order,
+ select_lex->first_in_union->select_limit_cnt,
+ 0);
order=0;
}
}
/* Copy data to the temporary table */
thd->proc_info="Copying to tmp table";
+ if ((tmp_error=do_select(this, (List<Item> *) 0, exec_tmp_table, 0)))
{
+ error= tmp_error;
+ DBUG_VOID_RETURN;
}
+ if (having)
+ having= having_list= 0; // Allready done
/* Change sum_fields reference to calculated fields in tmp_table */
+ if (sort_and_group || exec_tmp_table->group)
{
if (change_to_use_tmp_fields(all_fields))
+ DBUG_VOID_RETURN;
+ tmp_table_param.field_count+= tmp_table_param.sum_func_count+
+ tmp_table_param.func_count;
+ tmp_table_param.sum_func_count= tmp_table_param.func_count= 0;
}
else
{
if (change_refs_to_tmp_fields(thd,all_fields))
+ DBUG_VOID_RETURN;
+ tmp_table_param.field_count+= tmp_table_param.func_count;
+ tmp_table_param.func_count= 0;
}
if (procedure)
procedure->update_refs();
+ if (exec_tmp_table->group)
{ // Already grouped
if (!order && !no_order)
+ order= group_list; /* order by group */
+ group_list= 0;
}
/*
***************
*** 740,892 ****
** like SEC_TO_TIME(SUM(...)).
*/
- if (group && (!test_if_subpart(group,order) || select_distinct) ||
(select_distinct &&
- join.tmp_table_param.using_indirect_summary_function))
{ /* Must copy to another table */
TABLE *tmp_table2;
DBUG_PRINT("info",("Creating group table"));
/* Free first data from old join */
- join_free(&join);
- if (make_simple_join(&join,tmp_table))
- goto err;
- calc_group_buffer(&join,group);
- count_field_types(&join.tmp_table_param,all_fields,
- select_distinct && !group);
- join.tmp_table_param.hidden_field_count=(all_fields.elements-
- fields.elements);
/* group data to new table */
- if (!(tmp_table2 = create_tmp_table(thd,&join.tmp_table_param,all_fields,
(ORDER*) 0,
- select_distinct && !group,
1, 0,
- join.select_options)))
- goto err; /* purecov: inspected */
- if (group)
{
thd->proc_info="Creating sort index";
- if (create_sort_index(join.join_tab,group,HA_POS_ERROR) ||
- alloc_group_fields(&join,group))
{
free_tmp_table(thd,tmp_table2); /* purecov: inspected */
- goto err; /* purecov: inspected */
}
- group=0;
}
thd->proc_info="Copying to group table";
tmp_error= -1;
- if (make_sum_func_list(&join,all_fields) ||
- (tmp_error=do_select(&join,(List<Item> *) 0,tmp_table2,0)))
{
error=tmp_error;
free_tmp_table(thd,tmp_table2);
- goto err; /* purecov: inspected */
}
- end_read_record(&join.join_tab->read_record);
- free_tmp_table(thd,tmp_table);
- join.const_tables=join.tables; // Mark free for join_free()
- tmp_table=tmp_table2;
- join.join_tab[0].table=0; // Table is freed
if (change_to_use_tmp_fields(all_fields)) // No sum funcs anymore
- goto err;
- join.tmp_table_param.field_count+=join.tmp_table_param.sum_func_count;
- join.tmp_table_param.sum_func_count=0;
}
- if (tmp_table->distinct)
select_distinct=0; /* Each row is unique */
- join_free(&join); /* Free quick selects */
- if (select_distinct && ! group)
{
thd->proc_info="Removing duplicates";
- if (having)
- having->update_used_tables();
- if (remove_duplicates(&join,tmp_table,fields, having))
- goto err; /* purecov: inspected */
- having=0;
select_distinct=0;
}
- tmp_table->reginfo.lock_type=TL_UNLOCK;
- if (make_simple_join(&join,tmp_table))
- goto err;
- calc_group_buffer(&join,group);
- count_field_types(&join.tmp_table_param,all_fields,0);
}
if (procedure)
{
- if (procedure->change_columns(fields) ||
- result->prepare(fields))
- goto err;
- count_field_types(&join.tmp_table_param,all_fields,0);
}
- if (join.group || join.tmp_table_param.sum_func_count ||
(procedure && (procedure->flags & PROC_GROUP)))
{
- alloc_group_fields(&join,group);
- setup_copy_fields(thd, &join.tmp_table_param,all_fields);
- if (make_sum_func_list(&join,all_fields) || thd->fatal_error)
- goto err; /* purecov: inspected */
}
- if (group || order)
{
DBUG_PRINT("info",("Sorting for send_fields"));
thd->proc_info="Sorting result";
/* If we have already done the group, add HAVING to sorted table */
- if (having && ! group && ! join.sort_and_group)
{
- having->update_used_tables(); // Some tables may have been const
- JOIN_TAB *table=&join.join_tab[join.const_tables];
- table_map used_tables= join.const_table_map | table->table->map;
- Item* sort_table_cond=make_cond_for_table(having,used_tables,used_tables);
if (sort_table_cond)
{
if (!table->select)
if (!(table->select=new SQL_SELECT))
- goto err;
if (!table->select->cond)
table->select->cond=sort_table_cond;
else // This should never happen
if (!(table->select->cond=new Item_cond_and(table->select->cond,
sort_table_cond)))
- goto err;
table->select_cond=table->select->cond;
DBUG_EXECUTE("where",print_where(table->select->cond,
"select and having"););
- having=make_cond_for_table(having,~ (table_map) 0,~used_tables);
DBUG_EXECUTE("where",print_where(conds,"having after sort"););
}
}
- if (create_sort_index(&join.join_tab[join.const_tables],
- group ? group : order,
- (having || group ||
- join.const_tables != join.tables - 1 ||
- (join.select_options & OPTION_FOUND_ROWS)) ?
- HA_POS_ERROR : thd->select_limit))
- goto err; /* purecov: inspected */
}
- join.having=having; // Actually a parameter
thd->proc_info="Sending data";
- error=do_select(&join,&fields,NULL,procedure);
- err:
- thd->limit_found_rows = join.send_records;
- thd->examined_row_count = join.examined_rows;
- thd->proc_info="end";
- join.lock=0; // It's faster to unlock later
- join_free(&join);
- thd->proc_info="end2"; // QQ
- if (tmp_table)
- free_tmp_table(thd,tmp_table);
- thd->proc_info="end3"; // QQ
delete select;
delete_dynamic(&keyuse);
delete procedure;
- thd->proc_info="end4"; // QQ
DBUG_RETURN(error);
}
--- 791,987 ----
** like SEC_TO_TIME(SUM(...)).
*/
+ if (group_list && (!test_if_subpart(group_list,order) || select_distinct) ||
(select_distinct &&
+ tmp_table_param.using_indirect_summary_function))
{ /* Must copy to another table */
TABLE *tmp_table2;
DBUG_PRINT("info",("Creating group table"));
/* Free first data from old join */
+ join_free(this);
+ if (make_simple_join(this, exec_tmp_table))
+ DBUG_VOID_RETURN;
+ calc_group_buffer(this, group_list);
+ count_field_types(&tmp_table_param, all_fields,
+ select_distinct && !group_list);
+ tmp_table_param.hidden_field_count= (all_fields.elements-
+ fields_list.elements);
/* group data to new table */
+ if (!(tmp_table2 = create_tmp_table(thd,&tmp_table_param, all_fields,
(ORDER*) 0,
+ select_distinct && !group_list,
1, 0,
+ select_options,
+ select_lex->first_in_union)))
+ DBUG_VOID_RETURN;
+ if (group_list)
{
thd->proc_info="Creating sort index";
+ if (create_sort_index(join_tab,group_list,HA_POS_ERROR) ||
+ alloc_group_fields(this, group_list))
{
free_tmp_table(thd,tmp_table2); /* purecov: inspected */
+ DBUG_VOID_RETURN;
}
+ group_list=0;
}
thd->proc_info="Copying to group table";
tmp_error= -1;
+ if (make_sum_func_list(this, all_fields) ||
+ (tmp_error=do_select(this, (List<Item> *) 0,tmp_table2,0)))
{
error=tmp_error;
free_tmp_table(thd,tmp_table2);
+ DBUG_VOID_RETURN;
}
+ end_read_record(&join_tab->read_record);
+ free_tmp_table(thd,exec_tmp_table);
+ const_tables= tables; // Mark free for join_free()
+ exec_tmp_table= tmp_table2;
+ join_tab[0].table= 0; // Table is freed
if (change_to_use_tmp_fields(all_fields)) // No sum funcs anymore
+ DBUG_VOID_RETURN;
+ tmp_table_param.field_count+= tmp_table_param.sum_func_count;
+ tmp_table_param.sum_func_count=0;
}
+ if (exec_tmp_table->distinct)
select_distinct=0; /* Each row is unique */
+ join_free(this); /* Free quick selects */
+ if (select_distinct && ! group_list)
{
thd->proc_info="Removing duplicates";
+ if (having_list)
+ having_list->update_used_tables();
+ if (remove_duplicates(this, exec_tmp_table, fields_list, having_list))
+ DBUG_VOID_RETURN;
+ having_list=0;
select_distinct=0;
}
+ exec_tmp_table->reginfo.lock_type=TL_UNLOCK;
+ if (make_simple_join(this, exec_tmp_table))
+ DBUG_VOID_RETURN;
+ calc_group_buffer(this, group_list);
+ count_field_types(&tmp_table_param, all_fields, 0);
}
if (procedure)
{
+ if (procedure->change_columns(fields_list) ||
+ result->prepare(fields_list, select_lex->first_in_union))
+ DBUG_VOID_RETURN;
+ count_field_types(&tmp_table_param,all_fields,0);
}
+ if (group || tmp_table_param.sum_func_count ||
(procedure && (procedure->flags & PROC_GROUP)))
{
+ alloc_group_fields(this, group_list);
+ setup_copy_fields(thd, &tmp_table_param,all_fields);
+ if (make_sum_func_list(this, all_fields) || thd->fatal_error)
+ DBUG_VOID_RETURN;
}
+ if (group_list || order)
{
DBUG_PRINT("info",("Sorting for send_fields"));
thd->proc_info="Sorting result";
/* If we have already done the group, add HAVING to sorted table */
+ if (having_list && ! group_list && ! sort_and_group)
{
+ having_list->update_used_tables(); // Some tables may have been const
+ JOIN_TAB *table=&join_tab[const_tables];
+ table_map used_tables= const_table_map | table->table->map;
+ Item* sort_table_cond=make_cond_for_table(having_list, used_tables,
+ used_tables);
if (sort_table_cond)
{
if (!table->select)
if (!(table->select=new SQL_SELECT))
+ DBUG_VOID_RETURN;
if (!table->select->cond)
table->select->cond=sort_table_cond;
else // This should never happen
if (!(table->select->cond=new Item_cond_and(table->select->cond,
sort_table_cond)))
+ DBUG_VOID_RETURN;
table->select_cond=table->select->cond;
DBUG_EXECUTE("where",print_where(table->select->cond,
"select and having"););
+ having_list= make_cond_for_table(having_list, ~ (table_map) 0,
+ ~used_tables);
DBUG_EXECUTE("where",print_where(conds,"having after sort"););
}
}
+ if (create_sort_index(&join_tab[const_tables],
+ group_list ? group_list : order,
+ (having_list || group_list ||
+ const_tables != tables - 1 ||
+ (select_options & OPTION_FOUND_ROWS)) ?
+ HA_POS_ERROR :
+ select_lex->first_in_union->select_limit_cnt))
+ DBUG_VOID_RETURN;
}
+ having=having_list; // Actually a parameter
thd->proc_info="Sending data";
+ error=do_select(this, &fields_list, NULL, procedure);
+ DBUG_VOID_RETURN;
+ }
+ /*
+ Clean up join. Return error that hold JOIN.
+ */
+
+ int
+ JOIN::cleanup(THD *thd)
+ {
+ lock=0; // It's faster to unlock later
+ join_free(this);
+ if (exec_tmp_table)
+ free_tmp_table(thd, exec_tmp_table);
delete select;
delete_dynamic(&keyuse);
delete procedure;
+ return error;
+ }
+
+ int
+ mysql_select(THD *thd,TABLE_LIST *tables,List<Item> &fields,COND *conds,
+ ORDER *order, ORDER *group,Item *having,ORDER *proc_param,
+ ulong select_options,select_result *result)
+ {
+ JOIN *join = new JOIN(thd, fields, select_options, result);
+
+ DBUG_ENTER("mysql_select");
+ thd->proc_info="init";
+ thd->used_tables=0; // Updated by setup_fields
+
+ if (join->prepare(tables, conds, order, group, having, proc_param,
+ &(thd->lex.select_lex)))
+ {
+ DBUG_RETURN(-1);
+ }
+ switch(join->optimize())
+ {
+ case 1:
+ DBUG_RETURN(join->error);
+ case -1:
+ goto err;
+ }
+
+ if(join->global_optimize())
+ goto err;
+
+ join->exec();
+
+ err:
+ thd->limit_found_rows = join->send_records;
+ thd->examined_row_count = join->examined_rows;
+ thd->proc_info="end";
+ int error= join->cleanup(thd);
+ delete join;
DBUG_RETURN(error);
}
***************
*** 2480,2486 ****
if ((tab->keys & ~ tab->const_keys && i > 0) ||
(tab->const_keys && i == join->const_tables &&
- join->thd->select_limit < join->best_positions[i].records_read &&
!(join->select_options & OPTION_FOUND_ROWS)))
{
/* Join with outer join condition */
--- 2575,2582 ----
if ((tab->keys & ~ tab->const_keys && i > 0) ||
(tab->const_keys && i == join->const_tables &&
+ join->select_lex->first_in_union->select_limit_cnt <
+ join->best_positions[i].records_read &&
!(join->select_options & OPTION_FOUND_ROWS)))
{
/* Join with outer join condition */
***************
*** 2491,2497 ****
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
- join->thd->select_limit)) < 0)
DBUG_RETURN(1); // Impossible range
sel->cond=orig_cond;
}
--- 2587,2593 ----
(join->select_options &
OPTION_FOUND_ROWS ?
HA_POS_ERROR :
+ join->select_lex->first_in_union->select_limit_cnt)) < 0)
DBUG_RETURN(1); // Impossible range
sel->cond=orig_cond;
}
***************
*** 2933,2939 ****
static int
return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields,
bool send_row, uint select_options,const char *info,
- Item *having, Procedure *procedure)
{
DBUG_ENTER("return_zero_rows");
--- 3029,3035 ----
static int
return_zero_rows(select_result *result,TABLE_LIST *tables,List<Item> &fields,
bool send_row, uint select_options,const char *info,
+ Item *having, Procedure *procedure, SELECT_LEX *select_lex)
{
DBUG_ENTER("return_zero_rows");
***************
*** 2944,2950 ****
}
if (procedure)
{
- if (result->prepare(fields)) // This hasn't been done yet
DBUG_RETURN(-1);
}
if (send_row)
--- 3040,3047 ----
}
if (procedure)
{
+ if (result->prepare(fields,
+ select_lex->first_in_union))//This hasn't been done yet
DBUG_RETURN(-1);
}
if (send_row)
***************
*** 3479,3485 ****
TABLE *
create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields,
- bool allow_distinct_limit, ulong select_options)
{
TABLE *table;
uint i,field_count,reclength,null_count,null_pack_length,
--- 3576,3583 ----
TABLE *
create_tmp_table(THD *thd,TMP_TABLE_PARAM *param,List<Item> &fields,
ORDER *group, bool distinct, bool save_sum_fields,
+ bool allow_distinct_limit, ulong select_options,
+ SELECT_LEX *first_select)
{
TABLE *table;
uint i,field_count,reclength,null_count,null_pack_length,
***************
*** 3850,3857 ****
test(null_pack_length));
if (allow_distinct_limit)
{
- set_if_smaller(table->max_rows,thd->select_limit);
- param->end_write_records=thd->select_limit;
}
else
param->end_write_records= HA_POS_ERROR;
--- 3948,3955 ----
test(null_pack_length));
if (allow_distinct_limit)
{
+ set_if_smaller(table->max_rows,first_select->select_limit_cnt);
+ param->end_write_records=first_select->select_limit_cnt;
}
else
param->end_write_records= HA_POS_ERROR;
***************
*** 4896,4902 ****
error=join->result->send_data(*join->fields);
if (error)
DBUG_RETURN(-1); /* purecov: inspected */
- if (++join->send_records >= join->thd->select_limit && join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
{
--- 4994,5002 ----
error=join->result->send_data(*join->fields);
if (error)
DBUG_RETURN(-1); /* purecov: inspected */
+ if (++join->send_records >=
+ join->select_lex->first_in_union->select_limit_cnt &&
+ join->do_send_rows)
{
if (join->select_options & OPTION_FOUND_ROWS)
{
***************
*** 4912,4918 ****
else
{
join->do_send_rows=0;
- join->thd->select_limit = HA_POS_ERROR;
DBUG_RETURN(0);
}
}
--- 5012,5018 ----
else
{
join->do_send_rows=0;
+ join->select_lex->first_in_union->select_limit_cnt = HA_POS_ERROR;
DBUG_RETURN(0);
}
}
***************
*** 4973,4985 ****
DBUG_RETURN(-1); /* purecov: inspected */
if (end_of_records)
DBUG_RETURN(0);
- if (!error && ++join->send_records >= join->thd->select_limit &&
join->do_send_rows)
{
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(-3); // Abort nicely
join->do_send_rows=0;
- join->thd->select_limit = HA_POS_ERROR;
}
}
}
--- 5073,5086 ----
DBUG_RETURN(-1); /* purecov: inspected */
if (end_of_records)
DBUG_RETURN(0);
+ if (!error && ++join->send_records >=
+ join->select_lex->first_in_union->select_limit_cnt &&
join->do_send_rows)
{
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(-3); // Abort nicely
join->do_send_rows=0;
+ join->select_lex->first_in_union->select_limit_cnt = HA_POS_ERROR;
}
}
}
***************
*** 5060,5066 ****
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(-3);
join->do_send_rows=0;
- join->thd->select_limit = HA_POS_ERROR;
DBUG_RETURN(0);
}
}
--- 5161,5167 ----
if (!(join->select_options & OPTION_FOUND_ROWS))
DBUG_RETURN(-3);
join->do_send_rows=0;
+ join->select_lex->first_in_union->select_limit_cnt = HA_POS_ERROR;
DBUG_RETURN(0);
}
}
***************
*** 5743,5749 ****
if (!field_count)
{ // only const items
- join->thd->select_limit=1; // Only send first row
DBUG_RETURN(0);
}
Field **first_field=entry->field+entry->fields - field_count;
--- 5844,5850 ----
if (!field_count)
{ // only const items
+ join->select_lex->first_in_union->select_limit_cnt=1;// Only send first row
DBUG_RETURN(0);
}
Field **first_field=entry->field+entry->fields - field_count;
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment