/* Copyright (C) 2000-2003 MySQL AB

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */

/**
  @file

  @brief
  Functions to create an item. Used by sql_yac.yy
*/

#include <config.h>
#include <drizzled/item/create.h>
#include <drizzled/item/func.h>
#include <drizzled/error.h>
#include <drizzled/system_variables.h>

#include <drizzled/function_container.h>

#include <drizzled/function/str/binary.h>
#include <drizzled/function/str/concat.h>
#include <drizzled/function/str/conv.h>
#include <drizzled/function/str/export_set.h>
#include <drizzled/function/str/load_file.h>
#include <drizzled/function/str/make_set.h>
#include <drizzled/function/str/pad.h>
#include <drizzled/function/str/repeat.h>
#include <drizzled/function/str/str_conv.h>
#include <drizzled/function/str/trim.h>

#include <drizzled/function/time/date_format.h>
#include <drizzled/function/time/dayname.h>
#include <drizzled/function/time/dayofmonth.h>
#include <drizzled/function/time/dayofyear.h>
#include <drizzled/function/time/from_unixtime.h>
#include <drizzled/function/time/from_days.h>
#include <drizzled/function/time/last_day.h>
#include <drizzled/function/time/makedate.h>
#include <drizzled/function/time/month.h>
#include <drizzled/function/time/period_add.h>
#include <drizzled/function/time/period_diff.h>
#include <drizzled/function/time/to_days.h>
#include <drizzled/function/time/typecast.h>
#include <drizzled/function/time/unix_timestamp.h>
#include <drizzled/function/time/weekday.h>

#include <drizzled/item/cmpfunc.h>
#include <drizzled/plugin/function.h>
#include <drizzled/session.h>

/* Function declarations */

#include <drizzled/function/func.h>
#include <drizzled/function/additive_op.h>
#include <drizzled/function/math/dec.h>
#include <drizzled/function/math/decimal_typecast.h>
#include <drizzled/function/field.h>
#include <drizzled/function/find_in_set.h>
#include <drizzled/function/found_rows.h>
#include <drizzled/function/get_system_var.h>
#include <drizzled/function/math/int_val.h>
#include <drizzled/function/math/integer.h>
#include <drizzled/function/last_insert.h>
#include <drizzled/function/locate.h>
#include <drizzled/function/min_max.h>
#include <drizzled/function/num1.h>
#include <drizzled/function/num_op.h>
#include <drizzled/function/numhybrid.h>
#include <drizzled/function/math/real.h>
#include <drizzled/function/row_count.h>
#include <drizzled/function/set_user_var.h>
#include <drizzled/function/sign.h>
#include <drizzled/function/math/tan.h>
#include <drizzled/function/units.h>

#include <drizzled/function/cast/boolean.h>
#include <drizzled/function/cast/signed.h>
#include <drizzled/function/cast/time.h>
#include <drizzled/function/cast/unsigned.h>

using namespace std;

namespace drizzled {

/*
=============================================================================
  LOCAL DECLARATIONS
=============================================================================
*/

/**
  Adapter for native functions with a variable number of arguments.
  The main use of this class is to discard the following calls:
  <code>foo(expr1 AS name1, expr2 AS name2, ...)</code>
  which are syntactically correct (the syntax can refer to a UDF),
  but semantically invalid for native functions.
*/

/**
  Checks if there are named parameters in a parameter list.
  The syntax to name parameters in a function call is as follow:
  <code>foo(expr AS named, expr named, expr AS "named", expr "named")</code>
  @param params The parameter list, can be null
  @return true if one or more parameter is named
*/
static bool has_named_parameters(List<Item>& params)
{
  List<Item>::iterator it(params.begin());
  while (Item* param= it++)
  {
    if (not param->is_autogenerated_name)
      return true;
  }
  return false;
}

class Create_native_func : public Create_func
{
public:
  virtual Item* create(Session* session, str_ref name, List<Item>* item_list)
  {
    if (item_list && has_named_parameters(*item_list))
    {
      my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.data());
      return NULL;
    }
    return create_native(session, name, item_list);
  }

  /**
    Builder method, with no arguments.
    @param session The current thread
    @param name The native function name
    @param item_list The function parameters, none of which are named
    @return An item representing the function call
  */
  virtual Item* create_native(Session*, str_ref name, List<Item>*) = 0;
};


/**
  Adapter for functions that takes exactly zero arguments.
*/

class Create_func_arg0 : public Create_func
{
public:
  virtual Item* create(Session *session, str_ref name, List<Item> *item_list);

  /**
    Builder method, with no arguments.
    @param session The current thread
    @return An item representing the function call
  */
  virtual Item* create(Session *session) = 0;
};


/**
  Adapter for functions that takes exactly one argument.
*/

class Create_func_arg1 : public Create_func
{
public:
  virtual Item* create(Session *session, str_ref name, List<Item> *item_list);

  /**
    Builder method, with one argument.
    @param session The current thread
    @param arg1 The first argument of the function
    @return An item representing the function call
  */
  virtual Item* create(Session *session, Item *arg1) = 0;
};


/**
  Adapter for functions that takes exactly two arguments.
*/

class Create_func_arg2 : public Create_func
{
public:
  virtual Item* create(Session *session, str_ref name, List<Item> *item_list);

  /**
    Builder method, with two arguments.
    @param session The current thread
    @param arg1 The first argument of the function
    @param arg2 The second argument of the function
    @return An item representing the function call
  */
  virtual Item* create(Session *session, Item *arg1, Item *arg2) = 0;
};


/**
  Adapter for functions that takes exactly three arguments.
*/

class Create_func_arg3 : public Create_func
{
public:
  virtual Item* create(Session *session, str_ref name, List<Item> *item_list);

  /**
    Builder method, with three arguments.
    @param session The current thread
    @param arg1 The first argument of the function
    @param arg2 The second argument of the function
    @param arg3 The third argument of the function
    @return An item representing the function call
  */
  virtual Item* create(Session *session, Item *arg1, Item *arg2, Item *arg3) = 0;
};


/**
  Function builder for Stored Functions.
*/

/*
  Concrete functions builders (native functions).
  Please keep this list sorted in alphabetical order,
  it helps to compare code between versions, and helps with merges conflicts.
*/


class Create_func_bin : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1);

  static Create_func_bin s_singleton;
};

class Create_func_concat : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_concat s_singleton;
};


class Create_func_concat_ws : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_concat_ws s_singleton;
};


class Create_func_conv : public Create_func_arg3
{
public:
  using Create_func_arg3::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2, Item *arg3)
  {
    return new (session->mem) Item_func_conv(arg1, arg2, arg3);
  }

  static Create_func_conv s_singleton;
};

Create_func_conv Create_func_conv::s_singleton;

class Create_func_cot : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1);

  static Create_func_cot s_singleton;
};

class Create_func_date_format : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_date_format(arg1, arg2, 0);
  }

  static Create_func_date_format s_singleton;
};

Create_func_date_format Create_func_date_format::s_singleton;

class Create_func_datediff : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2);

  static Create_func_datediff s_singleton;
};


class Create_func_dayname : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_dayname(arg1);
  }

  static Create_func_dayname s_singleton;
};

Create_func_dayname Create_func_dayname::s_singleton;

class Create_func_dayofmonth : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_dayofmonth(arg1);
  }

  static Create_func_dayofmonth s_singleton;
};

Create_func_dayofmonth Create_func_dayofmonth::s_singleton;

class Create_func_dayofweek : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_weekday(arg1, 1);
  }

  static Create_func_dayofweek s_singleton;
};

Create_func_dayofweek Create_func_dayofweek::s_singleton;

class Create_func_dayofyear : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_dayofyear(arg1);
  }

  static Create_func_dayofyear s_singleton;
};

Create_func_dayofyear Create_func_dayofyear::s_singleton;

class Create_func_decode : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2);

  static Create_func_decode s_singleton;
};


class Create_func_degrees : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_units("degrees", arg1, 180/M_PI, 0.0);
  }

  static Create_func_degrees s_singleton;
};

Create_func_degrees Create_func_degrees::s_singleton;

class Create_func_export_set : public Create_native_func
{

public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_export_set s_singleton;
};


class Create_func_field : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_field s_singleton;
};


class Create_func_find_in_set : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_find_in_set(arg1, arg2);
  }

  static Create_func_find_in_set s_singleton;
};

class Create_func_found_rows : public Create_func_arg0
{
public:
  using Create_func_arg0::create;

  virtual Item* create(Session *session)
  {
    return new (session->mem) Item_func_found_rows();
  }

  static Create_func_found_rows s_singleton;
};


class Create_func_from_days : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_from_days(arg1);
  }

  static Create_func_from_days s_singleton;
};


class Create_func_from_unixtime : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_from_unixtime s_singleton;
};


class Create_func_greatest : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_greatest s_singleton;
};


class Create_func_ifnull : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_ifnull(arg1, arg2);
  }

  static Create_func_ifnull s_singleton;
};


class Create_func_instr : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_locate(arg1, arg2);
  }

  static Create_func_instr s_singleton;
};


class Create_func_isnull : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_isnull(arg1);
  }

  static Create_func_isnull s_singleton;
};


class Create_func_last_day : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_last_day(arg1);
  }

  static Create_func_last_day s_singleton;
};


class Create_func_last_insert_id : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_last_insert_id s_singleton;
};


class Create_func_lcase : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_lcase(arg1);
  }

  static Create_func_lcase s_singleton;
};


class Create_func_least : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_least s_singleton;
};

class Create_func_load_file : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_load_file(*session, arg1);
  }

  static Create_func_load_file s_singleton;
};

class Create_func_locate : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_locate s_singleton;
};

class Create_func_lpad : public Create_func_arg3
{
public:
  using Create_func_arg3::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2, Item *arg3)
  {
    return new (session->mem) Item_func_lpad(*session, arg1, arg2, arg3);
  }

  static Create_func_lpad s_singleton;
};

Create_func_lpad Create_func_lpad::s_singleton;

class Create_func_ltrim : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_ltrim(arg1);
  }

  static Create_func_ltrim s_singleton;
};

Create_func_ltrim Create_func_ltrim::s_singleton;

class Create_func_makedate : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_makedate(arg1, arg2);
  }

  static Create_func_makedate s_singleton;
};

Create_func_makedate Create_func_makedate::s_singleton;

class Create_func_make_set : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_make_set s_singleton;
};


class Create_func_monthname : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_monthname(arg1);
  }

  static Create_func_monthname s_singleton;
};

Create_func_monthname Create_func_monthname::s_singleton;

class Create_func_name_const : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2);

  static Create_func_name_const s_singleton;
};


class Create_func_nullif : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_nullif(arg1, arg2);
  }

  static Create_func_nullif s_singleton;
};

Create_func_nullif Create_func_nullif::s_singleton;

class Create_func_oct : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1);

  static Create_func_oct s_singleton;
};

class Create_func_period_add : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_period_add(arg1, arg2);
  }

  static Create_func_period_add s_singleton;
};


class Create_func_period_diff : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_period_diff(arg1, arg2);
  }

  static Create_func_period_diff s_singleton;
};


class Create_func_pi : public Create_func_arg0
{
public:
  using Create_func_arg0::create;

  virtual Item* create(Session *session)
  {
    return new (session->mem) Item_static_float_func("pi()",  M_PI, 6, 8);
  }

  static Create_func_pi s_singleton;
};

class Create_func_radians : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_units("radians", arg1, M_PI/180, 0.0);
  }

  static Create_func_radians s_singleton;
};


class Create_func_round : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_round s_singleton;
};


class Create_func_row_count : public Create_func_arg0
{
public:
  using Create_func_arg0::create;

  virtual Item* create(Session *session)
  {
    return new (session->mem) Item_func_row_count();
  }

  static Create_func_row_count s_singleton;
};

Create_func_row_count Create_func_row_count::s_singleton;

class Create_func_rpad : public Create_func_arg3
{
public:
  using Create_func_arg3::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2, Item *arg3)
  {
    return new (session->mem) Item_func_rpad(*session, arg1, arg2, arg3);
  }

  static Create_func_rpad s_singleton;
};

Create_func_rpad Create_func_rpad::s_singleton;

class Create_func_rtrim : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_rtrim(arg1);
  }

  static Create_func_rtrim s_singleton;
};

Create_func_rtrim Create_func_rtrim::s_singleton;

class Create_func_sign : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_sign(arg1);
  }

  static Create_func_sign s_singleton;
};

Create_func_sign Create_func_sign::s_singleton;

class Create_func_space : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1);

  static Create_func_space s_singleton;
};

class Create_func_strcmp : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_strcmp(arg1, arg2);
  }
  
  static Create_func_strcmp s_singleton;
};

Create_func_strcmp Create_func_strcmp::s_singleton;

class Create_func_tan : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_tan(arg1);
  }

  static Create_func_tan s_singleton;
};

Create_func_tan Create_func_tan::s_singleton;

class Create_func_time_format : public Create_func_arg2
{
public:
  using Create_func_arg2::create;

  virtual Item* create(Session *session, Item *arg1, Item *arg2)
  {
    return new (session->mem) Item_func_date_format(arg1, arg2, 1);
  }

  static Create_func_time_format s_singleton;
};

Create_func_time_format Create_func_time_format::s_singleton;

class Create_func_to_days : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_to_days(arg1);
  }

  static Create_func_to_days s_singleton;
};

Create_func_to_days Create_func_to_days::s_singleton;

class Create_func_ucase : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_ucase(arg1);
  }

  static Create_func_ucase s_singleton;
};

Create_func_ucase Create_func_ucase::s_singleton;

class Create_func_unix_timestamp : public Create_native_func
{
public:
  virtual Item* create_native(Session *session, str_ref name, List<Item> *item_list);

  static Create_func_unix_timestamp s_singleton;
};


class Create_func_weekday : public Create_func_arg1
{
public:
  using Create_func_arg1::create;

  virtual Item* create(Session *session, Item *arg1)
  {
    return new (session->mem) Item_func_weekday(arg1, 0);
  }

  static Create_func_weekday s_singleton;
};

Create_func_weekday Create_func_weekday::s_singleton;

/*
=============================================================================
  IMPLEMENTATION
=============================================================================
*/

Create_udf_func Create_udf_func::s_singleton;

Item* Create_udf_func::create(Session *session, str_ref name, List<Item> *item_list)
{
  return create(session, plugin::Function::get(to_string(name)), item_list);
}

Item* Create_udf_func::create(Session *session, const plugin::Function *udf, List<Item> *item_list)
{
  assert(udf);
  Item_func* func= (*udf)(&session->mem);
  if (!func->check_argument_count(item_list ? item_list->size() : 0))
  {
    my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), func->func_name());
    return NULL;
  }
  if (item_list)
    func->set_arguments(*item_list);
  return func;
}

Item* Create_func_arg0::create(Session *session, str_ref name, List<Item> *item_list)
{
  if (not item_list || not item_list->size())
    return create(session);
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Item* Create_func_arg1::create(Session *session, str_ref name, List<Item> *item_list)
{
  if (not item_list || item_list->size() != 1)
  {
    my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
    return NULL;
  }

  Item *param_1= item_list->pop();
  if (param_1->is_autogenerated_name)
    return create(session, param_1);
  my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.data());
  return NULL;
}

Item* Create_func_arg2::create(Session *session, str_ref name, List<Item> *item_list)
{
  if (not item_list || item_list->size() != 2)
  {
    my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
    return NULL;
  }

  Item *param_1= item_list->pop();
  Item *param_2= item_list->pop();

  if (param_1->is_autogenerated_name
    && param_2->is_autogenerated_name)
  return create(session, param_1, param_2);
  my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.data());
  return NULL;
}

Item* Create_func_arg3::create(Session *session, str_ref name, List<Item> *item_list)
{
  if (not item_list || item_list->size() != 3)
  {
    my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
    return NULL;
  }

  Item *param_1= item_list->pop();
  Item *param_2= item_list->pop();
  Item *param_3= item_list->pop();

  if (param_1->is_autogenerated_name
    && param_2->is_autogenerated_name
    && param_3->is_autogenerated_name)
    return create(session, param_1, param_2, param_3);
  my_error(ER_WRONG_PARAMETERS_TO_NATIVE_FCT, MYF(0), name.data());
  return NULL;
}

Create_func_bin Create_func_bin::s_singleton;

Item* Create_func_bin::create(Session *session, Item *arg1)
{
  Item *i10= new (session->mem) Item_int(10, 2);
  Item *i2= new (session->mem) Item_int(2, 1);
  return new (session->mem) Item_func_conv(arg1, i10, i2);
}

Create_func_concat Create_func_concat::s_singleton;

Item* Create_func_concat::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  if (item_list && item_list->size() >= 1)
    return new (session->mem) Item_func_concat(*session, *item_list);
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Create_func_concat_ws Create_func_concat_ws::s_singleton;

Item* Create_func_concat_ws::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  /* "WS" stands for "With Separator": this function takes 2+ arguments */
  if (item_list && item_list->size() >= 2)
    return new (session->mem) Item_func_concat_ws(*session, *item_list);
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Create_func_cot Create_func_cot::s_singleton;

Item*
Create_func_cot::create(Session *session, Item *arg1)
{
  Item *i1= new (session->mem) Item_int("1", 1, 1);
  Item *i2= new (session->mem) Item_func_tan(arg1);
  return new (session->mem) Item_func_div(session, i1, i2);
}

Create_func_datediff Create_func_datediff::s_singleton;

Item*
Create_func_datediff::create(Session *session, Item *arg1, Item *arg2)
{
  Item *i1= new (session->mem) Item_func_to_days(arg1);
  Item *i2= new (session->mem) Item_func_to_days(arg2);
  return new (session->mem) Item_func_minus(i1, i2);
}

Create_func_export_set Create_func_export_set::s_singleton;

Item*
Create_func_export_set::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  switch (item_list ? item_list->size() : 0) 
  {
  case 3:
    {
      Item *param_1= item_list->pop();
      Item *param_2= item_list->pop();
      Item *param_3= item_list->pop();
      return new (session->mem) Item_func_export_set(param_1, param_2, param_3);
    }
  case 4:
    {
      Item *param_1= item_list->pop();
      Item *param_2= item_list->pop();
      Item *param_3= item_list->pop();
      Item *param_4= item_list->pop();
      return new (session->mem) Item_func_export_set(param_1, param_2, param_3, param_4);
    }
  case 5:
    {
      Item *param_1= item_list->pop();
      Item *param_2= item_list->pop();
      Item *param_3= item_list->pop();
      Item *param_4= item_list->pop();
      Item *param_5= item_list->pop();
      return new (session->mem) Item_func_export_set(param_1, param_2, param_3, param_4, param_5);
    }
  }
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}


Create_func_field Create_func_field::s_singleton;

Item* Create_func_field::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  if (item_list && item_list->size() >= 2)
    return new (session->mem) Item_func_field(*item_list);
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Create_func_find_in_set Create_func_find_in_set::s_singleton;
Create_func_found_rows Create_func_found_rows::s_singleton;
Create_func_from_days Create_func_from_days::s_singleton;
Create_func_from_unixtime Create_func_from_unixtime::s_singleton;

Item* Create_func_from_unixtime::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  switch (item_list ? item_list->size() : 0) 
  {
  case 1:
    {
      Item *param_1= item_list->pop();
      return new (session->mem) Item_func_from_unixtime(param_1);
    }
  case 2:
    {
      Item *param_1= item_list->pop();
      Item *param_2= item_list->pop();
      Item *ut= new (session->mem) Item_func_from_unixtime(param_1);
      return new (session->mem) Item_func_date_format(ut, param_2, 0);
    }
  }
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Create_func_greatest Create_func_greatest::s_singleton;

Item* Create_func_greatest::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  if (item_list && item_list->size() >= 2)
  return new (session->mem) Item_func_max(*item_list);
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Create_func_ifnull Create_func_ifnull::s_singleton;
Create_func_instr Create_func_instr::s_singleton;
Create_func_isnull Create_func_isnull::s_singleton;
Create_func_last_day Create_func_last_day::s_singleton;
Create_func_last_insert_id Create_func_last_insert_id::s_singleton;

Item* Create_func_last_insert_id::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  int arg_count= item_list ? item_list->size() : 0;
  switch (arg_count) 
  {
  case 0:
    {
      return new (session->mem) Item_func_last_insert_id();
    }
  case 1:
    {
      Item *param_1= item_list->pop();
      return new (session->mem) Item_func_last_insert_id(param_1);
    }
  }
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Create_func_lcase Create_func_lcase::s_singleton;
Create_func_least Create_func_least::s_singleton;

Item* Create_func_least::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  if (item_list && item_list->size() >= 2)
    return new (session->mem) Item_func_min(*item_list);
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Create_func_load_file Create_func_load_file::s_singleton;
Create_func_locate Create_func_locate::s_singleton;

Item* Create_func_locate::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  switch (item_list ? item_list->size() : 0) 
  {
  case 2:
    {
      Item *param_1= item_list->pop();
      Item *param_2= item_list->pop();
      /* Yes, parameters in that order : 2, 1 */
      return new (session->mem) Item_func_locate(param_2, param_1);
    }
  case 3:
    {
      Item *param_1= item_list->pop();
      Item *param_2= item_list->pop();
      Item *param_3= item_list->pop();
      /* Yes, parameters in that order : 2, 1, 3 */
      return new (session->mem) Item_func_locate(param_2, param_1, param_3);
    }
  }
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Create_func_make_set Create_func_make_set::s_singleton;

Item* Create_func_make_set::create_native(Session *session_arg, str_ref name, List<Item> *item_list)
{
  if (not item_list || item_list->size() < 2)
  {
    my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
    return NULL;
  }

  Item *param_1= item_list->pop();
  return new (session_arg->mem) Item_func_make_set(*session_arg, param_1, *item_list);
}

Create_func_oct Create_func_oct::s_singleton;

Item* Create_func_oct::create(Session *session, Item *arg1)
{
  Item *i10= new (session->mem) Item_int((int32_t) 10,2);
  Item *i8= new (session->mem) Item_int((int32_t) 8,1);
  return new (session->mem) Item_func_conv(arg1, i10, i8);
}

Create_func_period_add Create_func_period_add::s_singleton;
Create_func_period_diff Create_func_period_diff::s_singleton;
Create_func_pi Create_func_pi::s_singleton;
Create_func_radians Create_func_radians::s_singleton;
Create_func_round Create_func_round::s_singleton;

Item* Create_func_round::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  switch (item_list ? item_list->size() : 0) 
  {
  case 1:
    {
      Item *param_1= item_list->pop();
      Item *i0 = new (session->mem) Item_int("0", 0, 1);
      return new (session->mem) Item_func_round(param_1, i0, 0);
    }
  case 2:
    {
      Item *param_1= item_list->pop();
      Item *param_2= item_list->pop();
      return new (session->mem) Item_func_round(param_1, param_2, 0);
    }
  }
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}

Create_func_space Create_func_space::s_singleton;

Item* Create_func_space::create(Session *session, Item *arg1)
{
  /**
    TODO: Fix Bug#23637
    The parsed item tree should not depend on
    <code>session->variables.collation_connection</code>.
  */
  const charset_info_st * const cs= session->variables.getCollation();
  Item *sp;

  if (cs->mbminlen > 1)
  {
    sp= new (session->mem) Item_string(str_ref(""), cs, DERIVATION_COERCIBLE);
    sp->str_value.copy(" ", 1, cs);
  }
  else
  {
    sp= new (session->mem) Item_string(str_ref(" "), cs, DERIVATION_COERCIBLE);
  }

  return new (session->mem) Item_func_repeat(*session, sp, arg1);
}

Create_func_unix_timestamp Create_func_unix_timestamp::s_singleton;

Item* Create_func_unix_timestamp::create_native(Session *session, str_ref name, List<Item> *item_list)
{
  switch (item_list ? item_list->size() : 0) 
  {
  case 0:
    return new (session->mem) Item_func_unix_timestamp();
  case 1:
    return new (session->mem) Item_func_unix_timestamp(item_list->pop());
  }
  my_error(ER_WRONG_PARAMCOUNT_TO_FUNCTION, MYF(0), name.data());
  return NULL;
}


struct Native_func_registry
{
  const char* name;
  Create_func *builder;
};

#define BUILDER(F) & F::s_singleton

/*
  MySQL native functions.
  MAINTAINER:
  - Keep sorted for human lookup. At runtime, a hash table is used.
  - keep 1 line per entry, it makes grep | sort easier
*/

static Native_func_registry func_array[] =
{
  { "BIN", BUILDER(Create_func_bin)},
  { "CONCAT", BUILDER(Create_func_concat)},
  { "CONCAT_WS", BUILDER(Create_func_concat_ws)},
  { "CONV", BUILDER(Create_func_conv)},
  { "COT", BUILDER(Create_func_cot)},
  { "DATEDIFF", BUILDER(Create_func_datediff)},
  { "DATE_FORMAT", BUILDER(Create_func_date_format)},
  { "DAYNAME", BUILDER(Create_func_dayname)},
  { "DAYOFMONTH", BUILDER(Create_func_dayofmonth)},
  { "DAYOFWEEK", BUILDER(Create_func_dayofweek)},
  { "DAYOFYEAR", BUILDER(Create_func_dayofyear)},
  { "DEGREES", BUILDER(Create_func_degrees)},
  { "EXPORT_SET", BUILDER(Create_func_export_set)},
  { "FIELD", BUILDER(Create_func_field)},
  { "FIND_IN_SET", BUILDER(Create_func_find_in_set)},
  { "FOUND_ROWS", BUILDER(Create_func_found_rows)},
  { "FROM_DAYS", BUILDER(Create_func_from_days)},
  { "FROM_UNIXTIME", BUILDER(Create_func_from_unixtime)},
  { "GREATEST", BUILDER(Create_func_greatest)},
  { "IFNULL", BUILDER(Create_func_ifnull)},
  { "INSTR", BUILDER(Create_func_instr)},
  { "ISNULL", BUILDER(Create_func_isnull)},
  { "LAST_DAY", BUILDER(Create_func_last_day)},
  { "LAST_INSERT_ID", BUILDER(Create_func_last_insert_id)},
  { "LCASE", BUILDER(Create_func_lcase)},
  { "LEAST", BUILDER(Create_func_least)},
  { "LOAD_FILE", BUILDER(Create_func_load_file)},
  { "LOCATE", BUILDER(Create_func_locate)},
  { "LOWER", BUILDER(Create_func_lcase)},
  { "LPAD", BUILDER(Create_func_lpad)},
  { "LTRIM", BUILDER(Create_func_ltrim)},
  { "MAKEDATE", BUILDER(Create_func_makedate)},
  { "MAKE_SET", BUILDER(Create_func_make_set)},
  { "MONTHNAME", BUILDER(Create_func_monthname)},
  { "NULLIF", BUILDER(Create_func_nullif)},
  { "OCT", BUILDER(Create_func_oct)},
  { "PERIOD_ADD", BUILDER(Create_func_period_add)},
  { "PERIOD_DIFF", BUILDER(Create_func_period_diff)},
  { "PI", BUILDER(Create_func_pi)},
  { "RADIANS", BUILDER(Create_func_radians)},
  { "ROUND", BUILDER(Create_func_round)},
  { "ROW_COUNT", BUILDER(Create_func_row_count)},
  { "RPAD", BUILDER(Create_func_rpad)},
  { "RTRIM", BUILDER(Create_func_rtrim)},
  { "SIGN", BUILDER(Create_func_sign)},
  { "SPACE", BUILDER(Create_func_space)},
  { "STRCMP", BUILDER(Create_func_strcmp)},
  { "TAN", BUILDER(Create_func_tan)},
  { "TIME_FORMAT", BUILDER(Create_func_time_format)},
  { "TO_DAYS", BUILDER(Create_func_to_days)},
  { "UCASE", BUILDER(Create_func_ucase)},
  { "UNIX_TIMESTAMP", BUILDER(Create_func_unix_timestamp)},
  { "UPPER", BUILDER(Create_func_ucase)},
  { "WEEKDAY", BUILDER(Create_func_weekday)},
  { NULL, NULL}
};

/*
  Load the hash table for native functions.
  Note: this code is not thread safe, and is intended to be used at server
  startup only (before going multi-threaded)
*/

void item_create_init()
{
  for (Native_func_registry* func= func_array; func->builder; func++)
    FunctionContainer::getMutableMap()[func->name]= func->builder;
}

Create_func* find_native_function_builder(str_ref name)
{
  return find_ptr2(FunctionContainer::getMap(), name.data());
}

Item* create_func_char_cast(Session *session, Item *a, int len, const charset_info_st* cs)
{
  return new (session->mem) Item_char_typecast(a, len, cs ? cs : session->variables.getCollation());
}

Item* create_func_cast(Session *session, Item *a, Cast_target cast_type, const char *c_len, const char *c_dec, const charset_info_st * const cs)
{
  switch (cast_type) 
  {
  case ITEM_CAST_SIGNED:
    return new (session->mem) function::cast::Signed(a);
  case ITEM_CAST_UNSIGNED:
    return new (session->mem) function::cast::Unsigned(a);
  case ITEM_CAST_BINARY:
    return new (session->mem) Item_func_binary(a);
  case ITEM_CAST_BOOLEAN:
    return new (session->mem) function::cast::Boolean(a);
  case ITEM_CAST_TIME:
    return new (session->mem) function::cast::Time(a);
  case ITEM_CAST_DATE:
    return new (session->mem) Item_date_typecast(a);
  case ITEM_CAST_DATETIME:
    return new (session->mem) Item_datetime_typecast(a);
  case ITEM_CAST_DECIMAL:
    {
      uint32_t len= c_len ? atoi(c_len) : 0;
      uint32_t dec= c_dec ? atoi(c_dec) : 0;
      class_decimal_trim(&len, &dec);
      if (len < dec)
      {
        my_error(ER_M_BIGGER_THAN_D, MYF(0), "");
        return 0;
      }
      if (len > DECIMAL_MAX_PRECISION)
      {
        my_error(ER_TOO_BIG_PRECISION, MYF(0), len, a->name, DECIMAL_MAX_PRECISION);
        return 0;
      }
      if (dec > DECIMAL_MAX_SCALE)
      {
        my_error(ER_TOO_BIG_SCALE, MYF(0), dec, a->name, DECIMAL_MAX_SCALE);
        return 0;
      }
      return new (session->mem) Item_decimal_typecast(a, len, dec);
    }
  case ITEM_CAST_CHAR:
    return create_func_char_cast(session, a, c_len ? atoi(c_len) : -1, cs);
  }
  return NULL;
}

} /* namespace drizzled */
