summaryrefslogtreecommitdiff
path: root/sql/sql_prepare.cc
diff options
context:
space:
mode:
authorAlexey Botchkov <holyfoot@askmonty.org>2020-08-14 21:04:25 +0400
committerAlexey Botchkov <holyfoot@askmonty.org>2020-08-14 21:04:25 +0400
commitb01c426146710f1c24ab75e2ffcd953feac08882 (patch)
treeae4d31f7391458b8b66fbef5472b337a5ab4636e /sql/sql_prepare.cc
parent68cba09173a49185c02d5b57a5a241f102257fea (diff)
downloadmariadb-git-b01c426146710f1c24ab75e2ffcd953feac08882.tar.gz
MDEV-19275 Provide SQL service to plugins.
Protocol_local fixed so it can be used now. Some Protocol:: methods made virtual so they can adapt. as well as net_ok and net_send_error functions. execute_sql_string function is exported to the plugins. To be changed with the mysql_use_result.
Diffstat (limited to 'sql/sql_prepare.cc')
-rw-r--r--sql/sql_prepare.cc896
1 files changed, 621 insertions, 275 deletions
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index ade531339bb..fee65ed4e22 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -243,61 +243,6 @@ private:
class Ed_connection;
-/**
- Protocol_local: a helper class to intercept the result
- of the data written to the network.
-*/
-
-class Protocol_local :public Protocol
-{
-public:
- Protocol_local(THD *thd, Ed_connection *ed_connection);
- ~Protocol_local() { free_root(&m_rset_root, MYF(0)); }
-protected:
- virtual void prepare_for_resend();
- virtual bool write();
- virtual bool store_null();
- virtual bool store_tiny(longlong from);
- virtual bool store_short(longlong from);
- virtual bool store_long(longlong from);
- virtual bool store_longlong(longlong from, bool unsigned_flag);
- virtual bool store_decimal(const my_decimal *);
- virtual bool store_str(const char *from, size_t length,
- CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
- virtual bool store(MYSQL_TIME *time, int decimals);
- virtual bool store_date(MYSQL_TIME *time);
- virtual bool store_time(MYSQL_TIME *time, int decimals);
- virtual bool store_float(float value, uint32 decimals);
- virtual bool store_double(double value, uint32 decimals);
- virtual bool store(Field *field);
-
- virtual bool send_result_set_metadata(List<Item> *list, uint flags);
- virtual bool send_out_parameters(List<Item_param> *sp_params);
-#ifdef EMBEDDED_LIBRARY
- void remove_last_row();
-#endif
- virtual enum enum_protocol_type type() { return PROTOCOL_LOCAL; };
-
- virtual bool send_ok(uint server_status, uint statement_warn_count,
- ulonglong affected_rows, ulonglong last_insert_id,
- const char *message, bool skip_flush);
-
- virtual bool send_eof(uint server_status, uint statement_warn_count);
- virtual bool send_error(uint sql_errno, const char *err_msg, const char* sqlstate);
-private:
- bool store_string(const char *str, size_t length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs);
-
- bool store_column(const void *data, size_t length);
- void opt_add_row_to_rset();
-private:
- Ed_connection *m_connection;
- MEM_ROOT m_rset_root;
- List<Ed_row> *m_rset;
- size_t m_column_count;
- Ed_column *m_current_row;
- Ed_column *m_current_column;
-};
/******************************************************************************
Implementation
@@ -3792,6 +3737,7 @@ Execute_sql_statement::execute_server_code(THD *thd)
end:
thd->lex->restore_set_statement_var();
+ delete_explain_query(thd->lex);
lex_end(thd->lex);
return error;
@@ -5035,12 +4981,12 @@ Ed_connection::free_old_result()
*/
bool
-Ed_connection::execute_direct(LEX_STRING sql_text)
+Ed_connection::execute_direct(Protocol *p, LEX_STRING sql_text)
{
Execute_sql_statement execute_sql_statement(sql_text);
DBUG_PRINT("ed_query", ("%s", sql_text.str));
- return execute_direct(&execute_sql_statement);
+ return execute_direct(p, &execute_sql_statement);
}
@@ -5057,10 +5003,9 @@ Ed_connection::execute_direct(LEX_STRING sql_text)
@param server_runnable A code fragment to execute.
*/
-bool Ed_connection::execute_direct(Server_runnable *server_runnable)
+bool Ed_connection::execute_direct(Protocol *p, Server_runnable *server_runnable)
{
bool rc= FALSE;
- Protocol_local protocol_local(m_thd, this);
Prepared_statement stmt(m_thd);
Protocol *save_protocol= m_thd->protocol;
Diagnostics_area *save_diagnostics_area= m_thd->get_stmt_da();
@@ -5069,7 +5014,7 @@ bool Ed_connection::execute_direct(Server_runnable *server_runnable)
free_old_result(); /* Delete all data from previous execution, if any */
- m_thd->protocol= &protocol_local;
+ m_thd->protocol= p;
m_thd->set_stmt_da(&m_diagnostics_area);
rc= stmt.execute_server_runnable(server_runnable);
@@ -5156,340 +5101,741 @@ Ed_connection::store_result_set()
return ed_result_set;
}
-/*************************************************************************
-* Protocol_local
-**************************************************************************/
-
-Protocol_local::Protocol_local(THD *thd, Ed_connection *ed_connection)
- :Protocol(thd),
- m_connection(ed_connection),
- m_rset(NULL),
- m_column_count(0),
- m_current_row(NULL),
- m_current_column(NULL)
-{
- clear_alloc_root(&m_rset_root);
-}
+/*
+ MENT-56
+ Protocol_local and service_sql for plugins to enable 'local' SQL query execution.
+*/
-/**
- Called between two result set rows.
+#ifndef EMBEDDED_LIBRARY
+// This part is mostly copied from libmysqld/lib_sql.cc
+// TODO: get rid of code duplications
- Prepare structures to fill result set rows.
- Unfortunately, we can't return an error here. If memory allocation
- fails, we'll have to return an error later. And so is done
- in methods such as @sa store_column().
-*/
+#include <mysql.h>
+#include "../libmysqld/embedded_priv.h"
-void Protocol_local::prepare_for_resend()
+class Protocol_local : public Protocol_text
{
- DBUG_ASSERT(alloc_root_inited(&m_rset_root));
+public:
+ struct st_mysql_data *cur_data;
+ struct st_mysql_data *first_data;
+ struct st_mysql_data **data_tail;
+ void clear_data_list();
+ struct st_mysql_data *alloc_new_dataset();
+ char **next_field;
+ MYSQL_FIELD *next_mysql_field;
+ MEM_ROOT *alloc;
+
+ Protocol_local(THD *thd_arg, ulong prealloc= 0) :
+ Protocol_text(thd_arg, prealloc),
+ cur_data(0), first_data(0), data_tail(&first_data)
+ {}
+
+protected:
+ bool net_store_data(const uchar *from, size_t length);
+ bool net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+ bool net_send_eof(THD *thd, uint server_status, uint statement_warn_count);
+ bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *,
+ bool, bool);
+ bool net_send_error_packet(THD *, uint, const char *, const char *);
+ bool begin_dataset();
+ bool begin_dataset(THD *thd, uint numfields);
+
+ bool write();
+ bool flush();
+
+ bool store_field_metadata(const THD *thd, const Send_field &field,
+ CHARSET_INFO *charset_for_protocol,
+ uint pos);
+ bool send_result_set_metadata(List<Item> *list, uint flags);
+ void remove_last_row();
+ bool store_null();
+ void prepare_for_resend();
+ bool send_list_fields(List<Field> *list, const TABLE_LIST *table_list);
+
+ enum enum_protocol_type type() { return PROTOCOL_LOCAL; };
+};
- opt_add_row_to_rset();
- /* Start a new row. */
- m_current_row= (Ed_column *) alloc_root(&m_rset_root,
- sizeof(Ed_column) * m_column_count);
- m_current_column= m_current_row;
+static
+bool
+write_eof_packet_local(THD *thd,
+ Protocol_local *p, uint server_status, uint statement_warn_count)
+{
+// if (!thd->mysql) // bootstrap file handling
+// return FALSE;
+ /*
+ The following test should never be true, but it's better to do it
+ because if 'is_fatal_error' is set the server is not going to execute
+ other queries (see the if test in dispatch_command / COM_QUERY)
+ */
+ if (thd->is_fatal_error)
+ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
+ p->cur_data->embedded_info->server_status= server_status;
+ /*
+ Don't send warn count during SP execution, as the warn_list
+ is cleared between substatements, and mysqltest gets confused
+ */
+ p->cur_data->embedded_info->warning_count=
+ (thd->spcont ? 0 : MY_MIN(statement_warn_count, 65535));
+ return FALSE;
}
-/**
- In "real" protocols this is called to finish a result set row.
- Unused in the local implementation.
-*/
-
-bool Protocol_local::write()
+MYSQL_DATA *Protocol_local::alloc_new_dataset()
{
- return FALSE;
+ MYSQL_DATA *data;
+ struct embedded_query_result *emb_data;
+ if (!my_multi_malloc(PSI_INSTRUMENT_ME, MYF(MY_WME | MY_ZEROFILL),
+ &data, sizeof(*data),
+ &emb_data, sizeof(*emb_data),
+ NULL))
+ return NULL;
+
+ emb_data->prev_ptr= &data->data;
+ cur_data= data;
+ *data_tail= data;
+ data_tail= &emb_data->next;
+ data->embedded_info= emb_data;
+ return data;
}
-/**
- A helper function to add the current row to the current result
- set. Called in @sa prepare_for_resend(), when a new row is started,
- and in send_eof(), when the result set is finished.
-*/
-void Protocol_local::opt_add_row_to_rset()
+static char *dup_str_aux(MEM_ROOT *root, const char *from, uint length,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
- if (m_current_row)
+ uint32 dummy32;
+ uint dummy_err;
+ char *result;
+
+ /* 'tocs' is set 0 when client issues SET character_set_results=NULL */
+ if (tocs && String::needs_conversion(0, fromcs, tocs, &dummy32))
{
- /* Add the old row to the result set */
- Ed_row *ed_row= new (&m_rset_root) Ed_row(m_current_row, m_column_count);
- if (ed_row)
- m_rset->push_back(ed_row, &m_rset_root);
+ uint new_len= (tocs->mbmaxlen * length) / fromcs->mbminlen + 1;
+ result= (char *)alloc_root(root, new_len);
+ length= copy_and_convert(result, new_len,
+ tocs, from, length, fromcs, &dummy_err);
}
+ else
+ {
+ result= (char *)alloc_root(root, length + 1);
+ memcpy(result, from, length);
+ }
+
+ result[length]= 0;
+ return result;
}
-/**
- Add a NULL column to the current row.
-*/
+static char *dup_str_aux(MEM_ROOT *root, const LEX_CSTRING &from,
+ CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
+{
+ return dup_str_aux(root, from.str, (uint) from.length, fromcs, tocs);
+}
-bool Protocol_local::store_null()
+
+bool Protocol_local::net_store_data(const uchar *from, size_t length)
{
- if (m_current_column == NULL)
- return TRUE; /* prepare_for_resend() failed to allocate memory. */
+ char *field_buf;
+// if (!thd->mysql) // bootstrap file handling
+// return FALSE;
- bzero(m_current_column, sizeof(*m_current_column));
- ++m_current_column;
+ if (!(field_buf= (char*) alloc_root(alloc, length + sizeof(uint) + 1)))
+ return TRUE;
+ *(uint *)field_buf= (uint) length;
+ *next_field= field_buf + sizeof(uint);
+ memcpy((uchar*) *next_field, from, length);
+ (*next_field)[length]= 0;
+ if (next_mysql_field->max_length < length)
+ next_mysql_field->max_length= (unsigned long) length;
+ ++next_field;
+ ++next_mysql_field;
return FALSE;
}
+bool Protocol_local::net_store_data_cs(const uchar *from, size_t length,
+ CHARSET_INFO *from_cs, CHARSET_INFO *to_cs)
+{
+ uint conv_length= (uint) (to_cs->mbmaxlen * length / from_cs->mbminlen);
+ uint dummy_error;
+ char *field_buf;
+// if (!thd->mysql) // bootstrap file handling
+// return false;
+
+ if (!(field_buf= (char*) alloc_root(alloc, conv_length + sizeof(uint) + 1)))
+ return true;
+ *next_field= field_buf + sizeof(uint);
+ length= copy_and_convert(*next_field, conv_length, to_cs,
+ (const char*) from, length, from_cs, &dummy_error);
+ *(uint *) field_buf= (uint) length;
+ (*next_field)[length]= 0;
+ if (next_mysql_field->max_length < length)
+ next_mysql_field->max_length= (unsigned long) length;
+ ++next_field;
+ ++next_mysql_field;
+ return false;
+}
+
+
/**
- A helper method to add any column to the current row
- in its binary form.
+ Embedded library implementation of OK response.
+
+ This function is used by the server to write 'OK' packet to
+ the "network" when the server is compiled as an embedded library.
+ Since there is no network in the embedded configuration,
+ a different implementation is necessary.
+ Instead of marshalling response parameters to a network representation
+ and then writing it to the socket, here we simply copy the data to the
+ corresponding client-side connection structures.
- Allocates memory for the data in the result set memory root.
+ @sa Server implementation of net_send_ok in protocol.cc for
+ description of the arguments.
+
+ @return
+ @retval TRUE An error occurred
+ @retval FALSE Success
*/
-bool Protocol_local::store_column(const void *data, size_t length)
+bool
+Protocol_local::net_send_ok(THD *thd,
+ uint server_status, uint statement_warn_count,
+ ulonglong affected_rows, ulonglong id, const char *message, bool, bool)
{
- if (m_current_column == NULL)
- return TRUE; /* prepare_for_resend() failed to allocate memory. */
- /*
- alloc_root() automatically aligns memory, so we don't need to
- do any extra alignment if we're pointing to, say, an integer.
- */
- m_current_column->str= (char*) memdup_root(&m_rset_root,
- data,
- length + 1 /* Safety */);
- if (! m_current_column->str)
- return TRUE;
- m_current_column->str[length]= '\0'; /* Safety */
- m_current_column->length= length;
- ++m_current_column;
- return FALSE;
+ DBUG_ENTER("emb_net_send_ok");
+ MYSQL_DATA *data;
+// MYSQL *mysql= thd->mysql;
+
+// if (!mysql) // bootstrap file handling
+// DBUG_RETURN(FALSE);
+ if (!(data= alloc_new_dataset()))
+ DBUG_RETURN(TRUE);
+ data->embedded_info->affected_rows= affected_rows;
+ data->embedded_info->insert_id= id;
+ if (message)
+ strmake_buf(data->embedded_info->info, message);
+
+ bool error= write_eof_packet_local(thd, this,
+ server_status, statement_warn_count);
+ cur_data= 0;
+ DBUG_RETURN(error);
}
/**
- Store a string value in a result set column, optionally
- having converted it to character_set_results.
+ Embedded library implementation of EOF response.
+
+ @sa net_send_ok
+
+ @return
+ @retval TRUE An error occurred
+ @retval FALSE Success
*/
bool
-Protocol_local::store_string(const char *str, size_t length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
+Protocol_local::net_send_eof(THD *thd, uint server_status,
+ uint statement_warn_count)
{
- /* Store with conversion */
- uint error_unused;
-
- if (needs_conversion(src_cs, dst_cs))
- {
- if (unlikely(convert->copy(str, length, src_cs, dst_cs, &error_unused)))
- return TRUE;
- str= convert->ptr();
- length= convert->length();
- }
- return store_column(str, length);
+ bool error= write_eof_packet_local(thd, this, server_status,
+ statement_warn_count);
+ cur_data= 0;
+ return error;
}
-/** Store a tiny int as is (1 byte) in a result set column. */
+bool Protocol_local::net_send_error_packet(THD *thd, uint sql_errno,
+ const char *err, const char *sqlstate)
+{
+ uint error;
+ char converted_err[MYSQL_ERRMSG_SIZE];
+ MYSQL_DATA *data= cur_data;
+ struct embedded_query_result *ei;
+
+// if (!thd->mysql) // bootstrap file handling
+// {
+// fprintf(stderr, "ERROR: %d %s\n", sql_errno, err);
+// return TRUE;
+// }
+ if (!data)
+ data= alloc_new_dataset();
+
+ ei= data->embedded_info;
+ ei->last_errno= sql_errno;
+ convert_error_message(converted_err, sizeof(converted_err),
+ thd->variables.character_set_results,
+ err, strlen(err),
+ system_charset_info, &error);
+ /* Converted error message is always null-terminated. */
+ strmake_buf(ei->info, converted_err);
+ strmov(ei->sqlstate, sqlstate);
+ ei->server_status= thd->server_status;
+ cur_data= 0;
+ return FALSE;
+}
+
-bool Protocol_local::store_tiny(longlong value)
+bool Protocol_local::begin_dataset()
{
- char v= (char) value;
- return store_column(&v, 1);
+ MYSQL_DATA *data= alloc_new_dataset();
+ if (!data)
+ return 1;
+ alloc= &data->alloc;
+ /* Assume rowlength < 8192 */
+ init_alloc_root(PSI_INSTRUMENT_ME, alloc, 8192, 0, MYF(0));
+ alloc->min_malloc= sizeof(MYSQL_ROWS);
+ return 0;
}
-/** Store a short as is (2 bytes, host order) in a result set column. */
-
-bool Protocol_local::store_short(longlong value)
+bool Protocol_local::begin_dataset(THD *thd, uint numfields)
{
- int16 v= (int16) value;
- return store_column(&v, 2);
+ if (begin_dataset())
+ return true;
+ MYSQL_DATA *data= cur_data;
+ data->fields= field_count= numfields;
+ if (!(data->embedded_info->fields_list=
+ (MYSQL_FIELD*)alloc_root(&data->alloc, sizeof(MYSQL_FIELD)*field_count)))
+ return true;
+ return false;
}
-/** Store a "long" as is (4 bytes, host order) in a result set column. */
-
-bool Protocol_local::store_long(longlong value)
+bool Protocol_local::write()
{
- int32 v= (int32) value;
- return store_column(&v, 4);
-}
+// if (!thd->mysql) // bootstrap file handling
+// return false;
+ *next_field= 0;
+ return false;
+}
-/** Store a "longlong" as is (8 bytes, host order) in a result set column. */
-bool Protocol_local::store_longlong(longlong value, bool unsigned_flag)
+bool Protocol_local::flush()
{
- int64 v= (int64) value;
- return store_column(&v, 8);
+ return 0;
}
-/** Store a decimal in string format in a result set column */
+bool Protocol_local::store_field_metadata(const THD * thd,
+ const Send_field &server_field,
+ CHARSET_INFO *charset_for_protocol,
+ uint pos)
+{
+ CHARSET_INFO *cs= system_charset_info;
+ CHARSET_INFO *thd_cs= thd->variables.character_set_results;
+ MYSQL_DATA *data= cur_data;
+ MEM_ROOT *field_alloc= &data->alloc;
+ MYSQL_FIELD *client_field= &cur_data->embedded_info->fields_list[pos];
+ DBUG_ASSERT(server_field.is_sane());
+
+ client_field->db= dup_str_aux(field_alloc, server_field.db_name,
+ cs, thd_cs);
+ client_field->table= dup_str_aux(field_alloc, server_field.table_name,
+ cs, thd_cs);
+ client_field->name= dup_str_aux(field_alloc, server_field.col_name,
+ cs, thd_cs);
+ client_field->org_table= dup_str_aux(field_alloc, server_field.org_table_name,
+ cs, thd_cs);
+ client_field->org_name= dup_str_aux(field_alloc, server_field.org_col_name,
+ cs, thd_cs);
+ if (charset_for_protocol == &my_charset_bin || thd_cs == NULL)
+ {
+ /* No conversion */
+ client_field->charsetnr= charset_for_protocol->number;
+ client_field->length= server_field.length;
+ }
+ else
+ {
+ /* With conversion */
+ client_field->charsetnr= thd_cs->number;
+ client_field->length= server_field.max_octet_length(charset_for_protocol,
+ thd_cs);
+ }
+ client_field->type= server_field.type_handler()->type_code_for_protocol();
+ client_field->flags= (uint16) server_field.flags;
+ client_field->decimals= server_field.decimals;
-bool Protocol_local::store_decimal(const my_decimal *value)
-{
- DBUG_ASSERT(0); // This method is not used yet
- StringBuffer<DECIMAL_MAX_STR_LENGTH> str;
- return value->to_string(&str) ? store_column(str.ptr(), str.length()) : true;
-}
+ client_field->db_length= (unsigned int) strlen(client_field->db);
+ client_field->table_length= (unsigned int) strlen(client_field->table);
+ client_field->name_length= (unsigned int) strlen(client_field->name);
+ client_field->org_name_length= (unsigned int) strlen(client_field->org_name);
+ client_field->org_table_length= (unsigned int) strlen(client_field->org_table);
+ client_field->catalog= dup_str_aux(field_alloc, "def", 3, cs, thd_cs);
+ client_field->catalog_length= 3;
-/** Store a string. */
+ if (IS_NUM(client_field->type))
+ client_field->flags|= NUM_FLAG;
-bool Protocol_local::store_str(const char *str, size_t length,
- CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
-{
- return store_string(str, length, src_cs, dst_cs);
+ client_field->max_length= 0;
+ client_field->def= 0;
+ return false;
}
-/* Store MYSQL_TIME (in binary format) */
-
-bool Protocol_local::store(MYSQL_TIME *time, int decimals)
+void Protocol_local::remove_last_row()
{
- if (decimals != AUTO_SEC_PART_DIGITS)
- my_datetime_trunc(time, decimals);
- return store_column(time, sizeof(MYSQL_TIME));
-}
+ MYSQL_DATA *data= cur_data;
+ MYSQL_ROWS **last_row_hook= &data->data;
+ my_ulonglong count= data->rows;
+ DBUG_ENTER("Protocol_text::remove_last_row");
+ while (--count)
+ last_row_hook= &(*last_row_hook)->next;
+ *last_row_hook= 0;
+ data->embedded_info->prev_ptr= last_row_hook;
+ data->rows--;
-/** Store MYSQL_TIME (in binary format) */
+ DBUG_VOID_RETURN;
+}
-bool Protocol_local::store_date(MYSQL_TIME *time)
+
+bool Protocol_local::send_result_set_metadata(List<Item> *list, uint flags)
{
- return store_column(time, sizeof(MYSQL_TIME));
-}
+ List_iterator_fast<Item> it(*list);
+ Item *item;
+// Protocol_local prot(thd);
+ DBUG_ENTER("send_result_set_metadata");
+// if (!thd->mysql) // bootstrap file handling
+// DBUG_RETURN(0);
-/** Store MYSQL_TIME (in binary format) */
+ if (begin_dataset(thd, list->elements))
+ goto err;
-bool Protocol_local::store_time(MYSQL_TIME *time, int decimals)
-{
- if (decimals != AUTO_SEC_PART_DIGITS)
- my_time_trunc(time, decimals);
- return store_column(time, sizeof(MYSQL_TIME));
+ for (uint pos= 0 ; (item= it++); pos++)
+ {
+ if (/*prot.*/store_item_metadata(thd, item, pos))
+ goto err;
+ }
+
+ if (flags & SEND_EOF)
+ write_eof_packet_local(thd, this, thd->server_status,
+ thd->get_stmt_da()->current_statement_warn_count());
+
+ DBUG_RETURN(prepare_for_send(list->elements));
+ err:
+ my_error(ER_OUT_OF_RESOURCES, MYF(0)); /* purecov: inspected */
+ DBUG_RETURN(1); /* purecov: inspected */
}
+static void
+list_fields_send_default(THD *thd, Protocol_local *p, Field *fld, uint pos)
+{
+ char buff[80];
+ String tmp(buff, sizeof(buff), default_charset_info), *res;
+ MYSQL_FIELD *client_field= &p->cur_data->embedded_info->fields_list[pos];
+
+ if (fld->is_null() || !(res= fld->val_str(&tmp)))
+ {
+ client_field->def_length= 0;
+ client_field->def= strmake_root(&p->cur_data->alloc, "", 0);
+ }
+ else
+ {
+ client_field->def_length= res->length();
+ client_field->def= strmake_root(&p->cur_data->alloc, res->ptr(),
+ client_field->def_length);
+ }
+}
-/* Store a floating point number, as is. */
-bool Protocol_local::store_float(float value, uint32 decimals)
+bool Protocol_local::send_list_fields(List<Field> *list, const TABLE_LIST *table_list)
{
- return store_column(&value, sizeof(float));
+ DBUG_ENTER("send_result_set_metadata");
+ Protocol_text prot(thd);
+ List_iterator_fast<Field> it(*list);
+ Field *fld;
+
+// if (!thd->mysql) // bootstrap file handling
+// DBUG_RETURN(0);
+
+ if (begin_dataset(thd, list->elements))
+ goto err;
+
+ for (uint pos= 0 ; (fld= it++); pos++)
+ {
+ if (prot.store_field_metadata_for_list_fields(thd, fld, table_list, pos))
+ goto err;
+ list_fields_send_default(thd, this, fld, pos);
+ }
+
+ DBUG_RETURN(prepare_for_send(list->elements));
+err:
+ my_error(ER_OUT_OF_RESOURCES, MYF(0));
+ DBUG_RETURN(1);
}
-/* Store a double precision number, as is. */
+void Protocol_local::prepare_for_resend()
+{
+ MYSQL_ROWS *cur;
+ MYSQL_DATA *data= cur_data;
+ DBUG_ENTER("send_data");
+
+// if (!thd->mysql) // bootstrap file handling
+// DBUG_VOID_RETURN;
-bool Protocol_local::store_double(double value, uint32 decimals)
+ data->rows++;
+ if (!(cur= (MYSQL_ROWS *)alloc_root(alloc, sizeof(MYSQL_ROWS)+(field_count + 1) * sizeof(char *))))
+ {
+ my_error(ER_OUT_OF_RESOURCES,MYF(0));
+ DBUG_VOID_RETURN;
+ }
+ cur->data= (MYSQL_ROW)(((char *)cur) + sizeof(MYSQL_ROWS));
+
+ *data->embedded_info->prev_ptr= cur;
+ data->embedded_info->prev_ptr= &cur->next;
+ next_field=cur->data;
+ next_mysql_field= data->embedded_info->fields_list;
+#ifndef DBUG_OFF
+ field_pos= 0;
+#endif
+
+ DBUG_VOID_RETURN;
+}
+
+bool Protocol_local::store_null()
{
- return store_column(&value, sizeof (double));
+ *(next_field++)= NULL;
+ ++next_mysql_field;
+ return false;
}
-/* Store a Field. */
+#include <sql_common.h>
+#include <errmsg.h>
-bool Protocol_local::store(Field *field)
+struct local_results
{
- if (field->is_null())
- return store_null();
- return field->send(this);
-}
+ struct st_mysql_data *cur_data;
+ struct st_mysql_data *first_data;
+ struct st_mysql_data **data_tail;
+ void clear_data_list();
+ struct st_mysql_data *alloc_new_dataset();
+ char **next_field;
+ MYSQL_FIELD *next_mysql_field;
+ MEM_ROOT *alloc;
+};
-/** Called to start a new result set. */
+static void embedded_get_error(MYSQL *mysql, MYSQL_DATA *data)
+{
+ NET *net= &mysql->net;
+ struct embedded_query_result *ei= data->embedded_info;
+ net->last_errno= ei->last_errno;
+ strmake_buf(net->last_error, ei->info);
+ memcpy(net->sqlstate, ei->sqlstate, sizeof(net->sqlstate));
+ mysql->server_status= ei->server_status;
+ my_free(data);
+}
+
-bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint)
+static my_bool loc_read_query_result(MYSQL *mysql)
{
- DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root));
+ local_results *thd= (local_results *) mysql->thd;
- init_sql_alloc(PSI_INSTRUMENT_ME, &m_rset_root, MEM_ROOT_BLOCK_SIZE, 0,
- MYF(MY_THREAD_SPECIFIC));
+ MYSQL_DATA *res= thd->first_data;
+ DBUG_ASSERT(!thd->cur_data);
+ thd->first_data= res->embedded_info->next;
+ if (res->embedded_info->last_errno &&
+ !res->embedded_info->fields_list)
+ {
+ embedded_get_error(mysql, res);
+ return 1;
+ }
- if (! (m_rset= new (&m_rset_root) List<Ed_row>))
- return TRUE;
+ mysql->warning_count= res->embedded_info->warning_count;
+ mysql->server_status= res->embedded_info->server_status;
+ mysql->field_count= res->fields;
+ if (!(mysql->fields= res->embedded_info->fields_list))
+ {
+ mysql->affected_rows= res->embedded_info->affected_rows;
+ mysql->insert_id= res->embedded_info->insert_id;
+ }
+ net_clear_error(&mysql->net);
+ mysql->info= 0;
+
+ if (res->embedded_info->info[0])
+ {
+ strmake(mysql->info_buffer, res->embedded_info->info, MYSQL_ERRMSG_SIZE-1);
+ mysql->info= mysql->info_buffer;
+ }
- m_column_count= columns->elements;
+ if (res->embedded_info->fields_list)
+ {
+ mysql->status=MYSQL_STATUS_GET_RESULT;
+ thd->cur_data= res;
+ }
+ else
+ my_free(res);
- return FALSE;
+ return 0;
}
-/**
- Normally this is a separate result set with OUT parameters
- of stored procedures. Currently unsupported for the local
- version.
-*/
+static MYSQL_METHODS local_methods=
+{
+ loc_read_query_result, /* read_query_result */
+ NULL/*loc_advanced_command*/, /* advanced_command */
+ NULL/*loc_read_rows*/, /* read_rows */
+ NULL/*loc_use_result*/, /* use_result */
+ NULL/*loc_fetch_lengths*/, /* fetch_lengths */
+ NULL/*loc_flush_use_result*/, /* flush_use_result */
+ NULL/*loc_read_change_user_result*/ /* read_change_user_result */
+};
+
-bool Protocol_local::send_out_parameters(List<Item_param> *sp_params)
+extern "C" MYSQL *mysql_real_connect_local(MYSQL *mysql,
+ const char *host, const char *user, const char *passwd, const char *db)
{
- return FALSE;
-}
+ //char name_buff[USERNAME_LENGTH];
+ DBUG_ENTER("mysql_real_connect_local");
-/** Called for statements that don't have a result set, at statement end. */
+ /* Test whether we're already connected */
+ if (mysql->server_version)
+ {
+ set_mysql_error(mysql, CR_ALREADY_CONNECTED, unknown_sqlstate);
+ DBUG_RETURN(0);
+ }
-bool
-Protocol_local::send_ok(uint server_status, uint statement_warn_count,
- ulonglong affected_rows, ulonglong last_insert_id,
- const char *message, bool skip_flush)
-{
- /*
- Just make sure nothing is sent to the client, we have grabbed
- the status information in the connection diagnostics area.
- */
- return FALSE;
-}
+ if (!host || !host[0])
+ host= mysql->options.host;
+ mysql->methods= &local_methods;
-/**
- Called at the end of a result set. Append a complete
- result set to the list in Ed_connection.
+ if (!db || !db[0])
+ db=mysql->options.db;
- Don't send anything to the client, but instead finish
- building of the result set at hand.
-*/
+ if (!user || !user[0])
+ user=mysql->options.user;
-bool Protocol_local::send_eof(uint server_status, uint statement_warn_count)
-{
- Ed_result_set *ed_result_set;
+ mysql->user= my_strdup(PSI_INSTRUMENT_ME, user, MYF(0));
- DBUG_ASSERT(m_rset);
- opt_add_row_to_rset();
- m_current_row= 0;
+ mysql->info_buffer= (char *) my_malloc(PSI_INSTRUMENT_ME,
+ MYSQL_ERRMSG_SIZE, MYF(0));
+ //mysql->thd= create_embedded_thd(client_flag);
- ed_result_set= new (&m_rset_root) Ed_result_set(m_rset, m_column_count,
- &m_rset_root);
+ //init_embedded_mysql(mysql, client_flag);
- m_rset= NULL;
+ //if (mysql_init_character_set(mysql))
+ // goto error;
- if (! ed_result_set)
- return TRUE;
+ //if (check_embedded_connection(mysql, db))
+ // goto error;
- /* In case of successful allocation memory ownership was transferred. */
- DBUG_ASSERT(!alloc_root_inited(&m_rset_root));
+ mysql->server_status= SERVER_STATUS_AUTOCOMMIT;
- /*
- Link the created Ed_result_set instance into the list of connection
- result sets. Never fails.
- */
- m_connection->add_result_set(ed_result_set);
- return FALSE;
-}
+ //if (mysql->options.init_commands)
+ //{
+ // DYNAMIC_ARRAY *init_commands= mysql->options.init_commands;
+ // char **ptr= (char**)init_commands->buffer;
+ // char **end= ptr + init_commands->elements;
+//
+ // for (; ptr<end; ptr++)
+ // {
+ // MYSQL_RES *res;
+ // if (mysql_query(mysql,*ptr))
+ // goto error;
+ // if (mysql->fields)
+ // {
+ // if (!(res= (*mysql->methods->use_result)(mysql)))
+ // goto error;
+ // mysql_free_result(res);
+ // }
+ // }
+ //}
+ DBUG_PRINT("exit",("Mysql handler: %p", mysql));
+ DBUG_RETURN(mysql);
-/** Called to send an error to the client at the end of a statement. */
+//error:
+ DBUG_PRINT("error",("message: %u (%s)",
+ mysql->net.last_errno,
+ mysql->net.last_error));
+ {
+ /* Free alloced memory */
+ my_bool free_me=mysql->free_me;
+ free_old_query(mysql);
+ mysql->free_me=0;
+ mysql_close(mysql);
+ mysql->free_me=free_me;
+ }
+ DBUG_RETURN(0);
+}
-bool
-Protocol_local::send_error(uint sql_errno, const char *err_msg, const char*)
+
+extern "C" int execute_sql_command(const char *command,
+ char *hosts, char *names, char *filters)
{
- /*
- Just make sure that nothing is sent to the client (default
- implementation).
- */
- return FALSE;
+ MYSQL_LEX_STRING sql_text;
+ THD *thd= current_thd;
+ THD *new_thd= 0;
+ int result;
+ my_bool qc_save= 0;
+
+ if (!thd)
+ {
+ new_thd= new THD(0);
+ new_thd->thread_stack= (char*) &sql_text;
+ new_thd->store_globals();
+ new_thd->security_ctx->skip_grants();
+ new_thd->query_cache_is_applicable= 0;
+ bzero((char*) &new_thd->net, sizeof(new_thd->net));
+ thd= new_thd;
+ }
+ else
+ {
+ if (thd->lock)
+ /* Doesn't work if the thread opened/locked tables already. */
+ return 2;
+
+ qc_save= thd->query_cache_is_applicable;
+ thd->query_cache_is_applicable= 0;
+ }
+ sql_text.str= (char *) command;
+ sql_text.length= strlen(command);
+ {
+ Protocol_local p(thd);
+ Ed_connection con(thd);
+ result= con.execute_direct(&p, sql_text);
+ if (!result && p.first_data)
+ {
+ int nr= (int) p.first_data->rows;
+ MYSQL_ROWS *rows= p.first_data->data;
+
+ while (nr--)
+ {
+ strcpy(hosts, rows->data[0]);
+ hosts+= strlen(hosts) + 1;
+ strcpy(names, rows->data[1]);
+ names+= strlen(names) + 1;
+ if (filters)
+ {
+ strcpy(filters, rows->data[2]);
+ filters+= strlen(filters) + 1;
+ }
+ rows= rows->next;
+ }
+ }
+ if (p.first_data)
+ {
+ if (p.alloc)
+ free_root(p.alloc, MYF(0));
+ my_free(p.first_data);
+ }
+ }
+
+ if (new_thd)
+ delete new_thd;
+ else
+ thd->query_cache_is_applicable= qc_save;
+
+ *hosts= 0;
+ return result;
}
+#endif /*!EMBEDDED_LIBRARY*/
+
-#ifdef EMBEDDED_LIBRARY
-void Protocol_local::remove_last_row()
-{ }
-#endif