summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <konstantin@mysql.com>2004-08-03 03:33:51 -0700
committerunknown <konstantin@mysql.com>2004-08-03 03:33:51 -0700
commita5debaea43e5164c55b899fdd7f7b20c3ef99d2d (patch)
tree1f890f453c5684d24a0e9457e58bcab87cda60f2 /sql
parentefec129351986ac882f2ef9935fc21eebebba89f (diff)
parenteaf34dd8e3383b92f3d6e2600fc6f6f9365c63fc (diff)
downloadmariadb-git-a5debaea43e5164c55b899fdd7f7b20c3ef99d2d.tar.gz
Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/kostja/mysql/mysql-5.0-sap-new sql/sp.cc: Auto merged sql/sp_head.cc: Auto merged
Diffstat (limited to 'sql')
-rw-r--r--sql/ha_innodb.cc3
-rw-r--r--sql/mysql_priv.h1
-rw-r--r--sql/protocol.cc58
-rw-r--r--sql/protocol.h9
-rw-r--r--sql/protocol_cursor.cc4
-rw-r--r--sql/repl_failsafe.cc6
-rw-r--r--sql/slave.cc3
-rw-r--r--sql/sp.cc3
-rw-r--r--sql/sp_head.cc6
-rw-r--r--sql/sql_acl.cc3
-rw-r--r--sql/sql_class.cc14
-rw-r--r--sql/sql_class.h26
-rw-r--r--sql/sql_error.cc3
-rw-r--r--sql/sql_handler.cc2
-rw-r--r--sql/sql_help.cc6
-rw-r--r--sql/sql_parse.cc9
-rw-r--r--sql/sql_prepare.cc154
-rw-r--r--sql/sql_repl.cc9
-rw-r--r--sql/sql_select.cc380
-rw-r--r--sql/sql_select.h57
-rw-r--r--sql/sql_show.cc50
-rw-r--r--sql/sql_table.cc6
-rw-r--r--sql/sql_union.cc8
23 files changed, 684 insertions, 136 deletions
diff --git a/sql/ha_innodb.cc b/sql/ha_innodb.cc
index fdaf5198b7f..700b8fafe19 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -4891,7 +4891,8 @@ innodb_show_status(
field_list.push_back(new Item_empty_string("Status", flen));
- if (protocol->send_fields(&field_list, 1)) {
+ if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF)) {
my_free(str, MYF(0));
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index b0acf64c4ff..bae1dcf5ecf 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -694,6 +694,7 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
LEX_STRING *name=NULL);
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name);
+void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length);
void mysql_stmt_free(THD *thd, char *packet);
void mysql_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 065fcd3d4af..75383001014 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -487,6 +487,7 @@ void Protocol::init(THD *thd_arg)
flag Bit mask with the following functions:
1 send number of rows
2 send default values
+ 4 don't write eof packet
DESCRIPTION
Sum fields has table name empty and field_name.
@@ -497,7 +498,7 @@ void Protocol::init(THD *thd_arg)
*/
#ifndef EMBEDDED_LIBRARY
-bool Protocol::send_fields(List<Item> *list, uint flag)
+bool Protocol::send_fields(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
@@ -508,7 +509,7 @@ bool Protocol::send_fields(List<Item> *list, uint flag)
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
DBUG_ENTER("send_fields");
- if (flag & 1)
+ if (flags & SEND_NUM_ROWS)
{ // Packet with number of elements
char *pos=net_store_length(buff, (uint) list->elements);
(void) my_net_write(&thd->net, buff,(uint) (pos-buff));
@@ -594,7 +595,7 @@ bool Protocol::send_fields(List<Item> *list, uint flag)
}
}
local_packet->length((uint) (pos - local_packet->ptr()));
- if (flag & 2)
+ if (flags & SEND_DEFAULTS)
item->send(&prot, &tmp); // Send default value
if (prot.write())
break; /* purecov: inspected */
@@ -603,7 +604,8 @@ bool Protocol::send_fields(List<Item> *list, uint flag)
#endif
}
- my_net_write(&thd->net, eof_buff, 1);
+ if (flags & SEND_EOF)
+ my_net_write(&thd->net, eof_buff, 1);
DBUG_RETURN(prepare_for_send(list));
err:
@@ -962,12 +964,6 @@ void Protocol_prep::prepare_for_resend()
bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs)
{
CHARSET_INFO *tocs= thd->variables.character_set_results;
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
- (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
- field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
-#endif
field_pos++;
return store_string_aux(from, length, fromcs, tocs);
}
@@ -975,12 +971,6 @@ bool Protocol_prep::store(const char *from, uint length, CHARSET_INFO *fromcs)
bool Protocol_prep::store(const char *from,uint length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DECIMAL ||
- (field_types[field_pos] >= MYSQL_TYPE_ENUM &&
- field_types[field_pos] <= MYSQL_TYPE_GEOMETRY));
-#endif
field_pos++;
return store_string_aux(from, length, fromcs, tocs);
}
@@ -998,10 +988,6 @@ bool Protocol_prep::store_null()
bool Protocol_prep::store_tiny(longlong from)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_TINY);
-#endif
char buff[1];
field_pos++;
buff[0]= (uchar) from;
@@ -1011,11 +997,6 @@ bool Protocol_prep::store_tiny(longlong from)
bool Protocol_prep::store_short(longlong from)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_SHORT ||
- field_types[field_pos] == MYSQL_TYPE_YEAR);
-#endif
field_pos++;
char *to= packet->prep_append(2, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1027,11 +1008,6 @@ bool Protocol_prep::store_short(longlong from)
bool Protocol_prep::store_long(longlong from)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_INT24 ||
- field_types[field_pos] == MYSQL_TYPE_LONG);
-#endif
field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1043,10 +1019,6 @@ bool Protocol_prep::store_long(longlong from)
bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_LONGLONG);
-#endif
field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1058,10 +1030,6 @@ bool Protocol_prep::store_longlong(longlong from, bool unsigned_flag)
bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_FLOAT);
-#endif
field_pos++;
char *to= packet->prep_append(4, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1073,10 +1041,6 @@ bool Protocol_prep::store(float from, uint32 decimals, String *buffer)
bool Protocol_prep::store(double from, uint32 decimals, String *buffer)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DOUBLE);
-#endif
field_pos++;
char *to= packet->prep_append(8, PACKET_BUFFER_EXTRA_ALLOC);
if (!to)
@@ -1100,12 +1064,6 @@ bool Protocol_prep::store(Field *field)
bool Protocol_prep::store(TIME *tm)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_DATETIME ||
- field_types[field_pos] == MYSQL_TYPE_DATE ||
- field_types[field_pos] == MYSQL_TYPE_TIMESTAMP);
-#endif
char buff[12],*pos;
uint length;
field_pos++;
@@ -1140,10 +1098,6 @@ bool Protocol_prep::store_date(TIME *tm)
bool Protocol_prep::store_time(TIME *tm)
{
-#ifndef DEBUG_OFF
- DBUG_ASSERT(field_types == 0 ||
- field_types[field_pos] == MYSQL_TYPE_TIME);
-#endif
char buff[13], *pos;
uint length;
field_pos++;
diff --git a/sql/protocol.h b/sql/protocol.h
index 079c06ae155..8dc2f506c6c 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -50,7 +50,12 @@ public:
Protocol(THD *thd_arg) { init(thd_arg); }
virtual ~Protocol() {}
void init(THD* thd_arg);
- virtual bool send_fields(List<Item> *list, uint flag);
+
+ static const uint SEND_NUM_ROWS= 1;
+ static const uint SEND_DEFAULTS= 2;
+ static const uint SEND_EOF= 4;
+ virtual bool send_fields(List<Item> *list, uint flags);
+
bool send_records_num(List<Item> *list, ulonglong records);
bool store(I_List<i_string> *str_list);
bool store(const char *from, CHARSET_INFO *cs);
@@ -163,7 +168,7 @@ public:
prev_record= &data;
return Protocol_simple::prepare_for_send(item_list);
}
- bool send_fields(List<Item> *list, uint flag);
+ bool send_fields(List<Item> *list, uint flags);
bool write();
uint get_field_count() { return field_count; }
};
diff --git a/sql/protocol_cursor.cc b/sql/protocol_cursor.cc
index 749b66785d4..d2c99dcaebc 100644
--- a/sql/protocol_cursor.cc
+++ b/sql/protocol_cursor.cc
@@ -26,7 +26,7 @@
#include "mysql_priv.h"
#include <mysql.h>
-bool Protocol_cursor::send_fields(List<Item> *list, uint flag)
+bool Protocol_cursor::send_fields(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
@@ -67,7 +67,7 @@ bool Protocol_cursor::send_fields(List<Item> *list, uint flag)
if (INTERNAL_NUM_FIELD(client_field))
client_field->flags|= NUM_FLAG;
- if (flag & 2)
+ if (flags & Protocol::SEND_DEFAULTS)
{
char buff[80];
String tmp(buff, sizeof(buff), default_charset_info), *res;
diff --git a/sql/repl_failsafe.cc b/sql/repl_failsafe.cc
index 4feb24f06b2..bb70b793d3b 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -461,7 +461,8 @@ int show_new_master(THD* thd)
field_list.push_back(new Item_empty_string("Log_name", 20));
field_list.push_back(new Item_return_int("Log_pos", 10,
MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
protocol->prepare_for_resend();
protocol->store(lex_mi->log_file_name, &my_charset_bin);
@@ -651,7 +652,8 @@ int show_slave_hosts(THD* thd)
field_list.push_back(new Item_return_int("Master_id", 10,
MYSQL_TYPE_LONG));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
pthread_mutex_lock(&LOCK_slave_list);
diff --git a/sql/slave.cc b/sql/slave.cc
index 8843e1561ec..0defbe35163 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -2335,7 +2335,8 @@ int show_master_info(THD* thd, MASTER_INFO* mi)
field_list.push_back(new Item_return_int("Seconds_Behind_Master", 10,
MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
if (mi->host[0])
diff --git a/sql/sp.cc b/sql/sp.cc
index 9d574c81545..238b28edfe3 100644
--- a/sql/sp.cc
+++ b/sql/sp.cc
@@ -564,7 +564,8 @@ db_show_routine_status(THD *thd, int type, const char *wild)
}
}
/* Print header */
- if (thd->protocol->send_fields(&field_list,1))
+ if (thd->protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
{
res= SP_INTERNAL_ERROR;
goto err_case;
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 5054b69cbd8..667c0f15d92 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -902,7 +902,8 @@ sp_head::show_create_procedure(THD *thd)
// 1024 is for not to confuse old clients
field_list.push_back(new Item_empty_string("Create Procedure",
max(buffer.length(), 1024)));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF))
{
res= 1;
goto done;
@@ -968,7 +969,8 @@ sp_head::show_create_function(THD *thd)
field_list.push_back(new Item_empty_string("sql_mode", sql_mode_len));
field_list.push_back(new Item_empty_string("Create Function",
max(buffer.length(),1024)));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
res= 1;
goto done;
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 41c629e5c12..07c3e59fd81 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -3163,7 +3163,8 @@ int mysql_show_grants(THD *thd,LEX_USER *lex_user)
strxmov(buff,"Grants for ",lex_user->user.str,"@",
lex_user->host.str,NullS);
field_list.push_back(field);
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
rw_wrlock(&LOCK_grant);
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 103a5080caf..da64479abf2 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -675,7 +675,8 @@ int THD::send_explain_fields(select_result *result)
item->maybe_null=1;
field_list.push_back(new Item_return_int("rows",10, MYSQL_TYPE_LONGLONG));
field_list.push_back(new Item_empty_string("Extra",255));
- return (result->send_fields(field_list,1));
+ return (result->send_fields(field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
#ifdef SIGNAL_WITH_VIO_CLOSE
@@ -722,9 +723,9 @@ sql_exchange::sql_exchange(char *name,bool flag)
escaped= &default_escaped;
}
-bool select_send::send_fields(List<Item> &list,uint flag)
+bool select_send::send_fields(List<Item> &list, uint flags)
{
- return thd->protocol->send_fields(&list,flag);
+ return thd->protocol->send_fields(&list, flags);
}
/* Send data to client. Returns 0 if ok */
@@ -1354,7 +1355,8 @@ Statement::Statement(THD *thd)
allow_sum_func(0),
lex(&main_lex),
query(0),
- query_length(0)
+ query_length(0),
+ cursor(0)
{
name.str= NULL;
}
@@ -1372,7 +1374,8 @@ Statement::Statement()
allow_sum_func(0), /* initialized later */
lex(&main_lex),
query(0), /* these two are set */
- query_length(0) /* in alloc_query() */
+ query_length(0), /* in alloc_query() */
+ cursor(0)
{
}
@@ -1391,6 +1394,7 @@ void Statement::set_statement(Statement *stmt)
lex= stmt->lex;
query= stmt->query;
query_length= stmt->query_length;
+ cursor= stmt->cursor;
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 00d32543da0..eccaf072008 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -487,6 +487,9 @@ public:
void set_item_arena(Item_arena *set);
};
+
+class Cursor;
+
/*
State of a single command executed against this connection.
One connection can contain a lot of simultaneously running statements,
@@ -543,6 +546,7 @@ public:
*/
char *query;
uint32 query_length; // current query length
+ Cursor *cursor;
public:
/* We build without RTTI, so dynamic_cast can't be used. */
@@ -1054,6 +1058,7 @@ public:
{
DBUG_ASSERT(current_arena!=0);
cleanup_items(current_arena->free_list);
+ /* no need to reset free_list as it won't be used anymore */
free_items(free_list);
close_thread_tables(this); // to close derived tables
free_root(&mem_root, MYF(0));
@@ -1108,7 +1113,7 @@ public:
unit= u;
return 0;
}
- virtual bool send_fields(List<Item> &list,uint flag)=0;
+ virtual bool send_fields(List<Item> &list, uint flags)=0;
virtual bool send_data(List<Item> &items)=0;
virtual bool initialize_tables (JOIN *join=0) { return 0; }
virtual void send_error(uint errcode,const char *err);
@@ -1120,7 +1125,7 @@ public:
class select_send :public select_result {
public:
select_send() {}
- bool send_fields(List<Item> &list,uint flag);
+ bool send_fields(List<Item> &list, uint flags);
bool send_data(List<Item> &items);
bool send_eof();
};
@@ -1138,7 +1143,7 @@ public:
select_to_file(sql_exchange *ex) :exchange(ex), file(-1),row_count(0L)
{ path[0]=0; }
~select_to_file();
- bool send_fields(List<Item> &list, uint flag) { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
void send_error(uint errcode,const char *err);
};
@@ -1185,8 +1190,7 @@ class select_insert :public select_result {
}
~select_insert();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list, uint flag)
- { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
void send_error(uint errcode,const char *err);
bool send_eof();
@@ -1273,8 +1277,7 @@ class select_union :public select_result {
select_union(TABLE *table_par);
~select_union();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list, uint flag)
- { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
bool send_eof();
bool flush();
@@ -1288,7 +1291,7 @@ protected:
Item_subselect *item;
public:
select_subselect(Item_subselect *item);
- bool send_fields(List<Item> &list, uint flag) { return 0; };
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items)=0;
bool send_eof() { return 0; };
@@ -1457,8 +1460,7 @@ public:
multi_delete(THD *thd, TABLE_LIST *dt, uint num_of_tables);
~multi_delete();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list,
- uint flag) { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
void send_error(uint errcode,const char *err);
@@ -1486,7 +1488,7 @@ public:
List<Item> *values, enum_duplicates handle_duplicates);
~multi_update();
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list, uint flag) { return 0; }
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
bool initialize_tables (JOIN *join);
void send_error(uint errcode,const char *err);
@@ -1515,7 +1517,7 @@ public:
select_dumpvar(void) { var_list.empty(); local_vars.empty(); vars.empty(); row_count=0;}
~select_dumpvar() {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
- bool send_fields(List<Item> &list, uint flag) {return 0;}
+ bool send_fields(List<Item> &list, uint flags) { return 0; }
bool send_data(List<Item> &items);
bool send_eof();
};
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 8aa7bdf9a7f..d68d62a8820 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -185,7 +185,8 @@ my_bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE));
- if (thd->protocol->send_fields(&field_list,1))
+ if (thd->protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
MYSQL_ERROR *err;
diff --git a/sql/sql_handler.cc b/sql/sql_handler.cc
index 8d06d3f0017..0df3d617d7f 100644
--- a/sql/sql_handler.cc
+++ b/sql/sql_handler.cc
@@ -252,7 +252,7 @@ int mysql_ha_read(THD *thd, TABLE_LIST *tables,
insert_fields(thd, tables, tables->db, tables->alias, &it, 0);
select_limit+=offset_limit;
- protocol->send_fields(&list,1);
+ protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
HANDLER_TABLES_HACK(thd);
MYSQL_LOCK *lock=mysql_lock_tables(thd,&tables->table,1);
diff --git a/sql/sql_help.cc b/sql/sql_help.cc
index 08a8fc626cc..85d5271d4c3 100644
--- a/sql/sql_help.cc
+++ b/sql/sql_help.cc
@@ -426,7 +426,8 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3)
field_list.push_back(new Item_empty_string("description",1000));
field_list.push_back(new Item_empty_string("example",1000));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
protocol->prepare_for_resend();
@@ -468,7 +469,8 @@ int send_header_2(Protocol *protocol, bool for_category)
field_list.push_back(new Item_empty_string("source_category_name",64));
field_list.push_back(new Item_empty_string("name",64));
field_list.push_back(new Item_empty_string("is_it_category",1));
- DBUG_RETURN(protocol->send_fields(&field_list,1));
+ DBUG_RETURN(protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ Protocol::SEND_EOF));
}
/*
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 7f42d6052d9..21d2a51bfeb 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -1452,6 +1452,11 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
mysql_stmt_execute(thd, packet, packet_length);
break;
}
+ case COM_FETCH:
+ {
+ mysql_stmt_fetch(thd, packet, packet_length);
+ break;
+ }
case COM_LONG_DATA:
{
mysql_stmt_get_longdata(thd, packet, packet_length);
@@ -1545,7 +1550,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
send_error(thd,ER_NO_DB_ERROR);
break;
}
- thd->free_list=0;
pend= strend(packet);
thd->convert_string(&conv_name, system_charset_info,
packet, (uint) (pend-packet), thd->charset());
@@ -1567,6 +1571,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
break;
mysqld_list_fields(thd,&table_list,fields);
free_items(thd->free_list);
+ thd->free_list=0; /* free_list should never point to garbage */
break;
}
#endif
@@ -4443,6 +4448,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length)
}
thd->proc_info="freeing items";
free_items(thd->free_list); /* Free strings used by items */
+ thd->free_list= 0; /* free_list should never point to garbage */
lex_end(lex);
}
DBUG_VOID_RETURN;
@@ -4470,6 +4476,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length)
all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first))
error= 1; /* Ignore question */
free_items(thd->free_list); /* Free strings used by items */
+ thd->free_list= 0; /* free_list should never point to garbage */
lex_end(lex);
DBUG_RETURN(error);
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index ed5e75a3622..9d9a0dd73e2 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -75,6 +75,8 @@ Long data handling:
#ifdef EMBEDDED_LIBRARY
/* include MYSQL_BIND headers */
#include <mysql.h>
+#else
+#include <mysql_com.h>
#endif
/******************************************************************************
@@ -107,7 +109,7 @@ public:
};
static void execute_stmt(THD *thd, Prepared_statement *stmt,
- String *expanded_query, bool set_context);
+ String *expanded_query);
/******************************************************************************
Implementation
@@ -166,7 +168,8 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
return my_net_write(net, buff, sizeof(buff)) ||
(stmt->param_count &&
stmt->thd->protocol_simple.send_fields((List<Item> *)
- &stmt->lex->param_list, 0)) ||
+ &stmt->lex->param_list,
+ Protocol::SEND_EOF)) ||
net_flush(net);
return 0;
}
@@ -1098,7 +1101,8 @@ static int mysql_test_select(Prepared_statement *stmt,
if (!text_protocol)
{
if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) ||
- thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0)
+ thd->protocol_simple.send_fields(&lex->select_lex.item_list,
+ Protocol::SEND_EOF)
#ifndef EMBEDDED_LIBRARY
|| net_flush(&thd->net)
#endif
@@ -1476,6 +1480,12 @@ static int send_prepare_results(Prepared_statement *stmt, bool text_protocol)
case SQLCOM_SHOW_GRANTS:
case SQLCOM_DROP_TABLE:
case SQLCOM_RENAME_TABLE:
+ case SQLCOM_ALTER_TABLE:
+ case SQLCOM_COMMIT:
+ case SQLCOM_CREATE_INDEX:
+ case SQLCOM_DROP_INDEX:
+ case SQLCOM_ROLLBACK:
+ case SQLCOM_TRUNCATE:
break;
default:
@@ -1756,6 +1766,7 @@ static void reset_stmt_params(Prepared_statement *stmt)
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
{
ulong stmt_id= uint4korr(packet);
+ ulong flags= (ulong) ((uchar) packet[4]);
/*
Query text for binary log, or empty string if the query is not put into
binary log.
@@ -1782,6 +1793,28 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
DBUG_VOID_RETURN;
}
+ if (flags & (ulong) CURSOR_TYPE_READ_ONLY)
+ {
+ if (stmt->lex->result)
+ {
+ /*
+ If lex->result is set in the parser, this is not a SELECT
+ statement: we can't open a cursor for it.
+ */
+ flags= 0;
+ }
+ else
+ {
+ if (!stmt->cursor &&
+ !(stmt->cursor= new (&stmt->mem_root) Cursor()))
+ {
+ send_error(thd, ER_OUT_OF_RESOURCES);
+ DBUG_VOID_RETURN;
+ }
+ /* If lex->result is set, mysql_execute_command will use it */
+ stmt->lex->result= &stmt->cursor->result;
+ }
+ }
#ifndef EMBEDDED_LIBRARY
if (stmt->param_count)
{
@@ -1800,16 +1833,55 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query))
goto set_params_data_err;
#endif
+ thd->stmt_backup.set_statement(thd);
+ thd->set_statement(stmt);
thd->current_arena= stmt;
+ reset_stmt_for_execute(thd, stmt->lex);
+ /* From now cursors assume that thd->mem_root is clean */
+ if (expanded_query.length() &&
+ alloc_query(thd, (char *)expanded_query.ptr(),
+ expanded_query.length()+1))
+ {
+ my_error(ER_OUTOFMEMORY, 0, expanded_query.length());
+ goto err;
+ }
+
thd->protocol= &thd->protocol_prep; // Switch to binary protocol
- execute_stmt(thd, stmt, &expanded_query, true);
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(),QUERY_PRIOR);
+ mysql_execute_command(thd);
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(), WAIT_PRIOR);
thd->protocol= &thd->protocol_simple; // Use normal protocol
+
+ if (flags & (ulong) CURSOR_TYPE_READ_ONLY)
+ {
+ if (stmt->cursor->is_open())
+ stmt->cursor->init_from_thd(thd);
+ thd->set_item_arena(&thd->stmt_backup);
+ }
+ else
+ {
+ thd->lex->unit.cleanup();
+ cleanup_items(stmt->free_list);
+ reset_stmt_params(stmt);
+ close_thread_tables(thd); /* to close derived tables */
+ /*
+ Free items that were created during this execution of the PS by
+ query optimizer.
+ */
+ free_items(thd->free_list);
+ thd->free_list= 0;
+ }
+
+ thd->set_statement(&thd->stmt_backup);
thd->current_arena= 0;
DBUG_VOID_RETURN;
set_params_data_err:
reset_stmt_params(stmt);
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_stmt_execute");
+err:
send_error(thd);
DBUG_VOID_RETURN;
}
@@ -1845,7 +1917,6 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
DBUG_VOID_RETURN;
}
- thd->free_list= NULL;
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
if (stmt->set_params_from_vars(stmt,
@@ -1856,7 +1927,7 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
send_error(thd);
}
thd->current_arena= stmt;
- execute_stmt(thd, stmt, &expanded_query, false);
+ execute_stmt(thd, stmt, &expanded_query);
thd->current_arena= 0;
DBUG_VOID_RETURN;
}
@@ -1872,20 +1943,13 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
placeholders replaced with actual values. Otherwise empty
string.
NOTES
- Caller must set parameter values and thd::protocol.
- thd->free_list is assumed to be garbage.
+ Caller must set parameter values and thd::protocol.
*/
static void execute_stmt(THD *thd, Prepared_statement *stmt,
- String *expanded_query, bool set_context)
+ String *expanded_query)
{
DBUG_ENTER("execute_stmt");
- if (set_context)
- {
- thd->free_list= NULL;
- thd->stmt_backup.set_statement(thd);
- thd->set_statement(stmt);
- }
reset_stmt_for_execute(thd, stmt->lex);
if (expanded_query->length() &&
@@ -1899,22 +1963,77 @@ static void execute_stmt(THD *thd, Prepared_statement *stmt,
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
mysql_execute_command(thd);
- thd->lex->unit.cleanup();
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
+ thd->lex->unit.cleanup();
cleanup_items(stmt->free_list);
reset_stmt_params(stmt);
close_thread_tables(thd); // to close derived tables
thd->set_statement(&thd->stmt_backup);
/* Free Items that were created during this execution of the PS. */
free_items(thd->free_list);
+ /*
+ In the rest of prepared statements code we assume that free_list
+ never points to garbage: keep this predicate true.
+ */
thd->free_list= 0;
DBUG_VOID_RETURN;
}
/*
+ COM_FETCH handler: fetches requested amount of rows from cursor
+ SYNOPSIS
+*/
+
+void mysql_stmt_fetch(THD *thd, char *packet, uint packet_length)
+{
+ /* assume there is always place for 8-16 bytes */
+ ulong stmt_id= uint4korr(packet);
+ ulong num_rows= uint4korr(packet+=4);
+ Statement *stmt;
+ int error;
+
+ DBUG_ENTER("mysql_stmt_fetch");
+
+ if (!(stmt= thd->stmt_map.find(stmt_id)) ||
+ !stmt->cursor ||
+ !stmt->cursor->is_open())
+ {
+ my_error(ER_UNKNOWN_STMT_HANDLER, MYF(0), stmt_id, "fetch");
+ send_error(thd);
+ DBUG_VOID_RETURN;
+ }
+
+ thd->stmt_backup.set_statement(thd);
+ thd->stmt_backup.set_item_arena(thd);
+ thd->set_statement(stmt);
+ stmt->cursor->init_thd(thd);
+
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(), QUERY_PRIOR);
+
+ thd->protocol= &thd->protocol_prep; // Switch to binary protocol
+ error= stmt->cursor->fetch(num_rows);
+ thd->protocol= &thd->protocol_simple; // Use normal protocol
+
+ if (!(specialflag & SPECIAL_NO_PRIOR))
+ my_pthread_setprio(pthread_self(), WAIT_PRIOR);
+
+ /* Restore THD state */
+ stmt->cursor->reset_thd(thd);
+ thd->set_statement(&thd->stmt_backup);
+ thd->set_item_arena(&thd->stmt_backup);
+
+ if (error && error != -4)
+ send_error(thd, ER_OUT_OF_RESOURCES);
+
+ DBUG_VOID_RETURN;
+}
+
+
+/*
Reset a prepared statement in case there was a recoverable error.
SYNOPSIS
mysql_stmt_reset()
@@ -2084,8 +2203,11 @@ void Prepared_statement::setup_set_params()
}
}
+
Prepared_statement::~Prepared_statement()
{
+ if (cursor)
+ cursor->Cursor::~Cursor();
free_items(free_list);
}
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 98bf4e86aba..9e38a65d412 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -1297,7 +1297,8 @@ int show_binlog_events(THD* thd)
Format_description_log_event(3); /* MySQL 4.0 by default */
Log_event::init_show_field_list(&field_list);
- if (protocol-> send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
if (mysql_bin_log.is_open())
@@ -1426,7 +1427,8 @@ int show_binlog_info(THD* thd)
field_list.push_back(new Item_empty_string("Binlog_Do_DB",255));
field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
protocol->prepare_for_resend();
@@ -1476,7 +1478,8 @@ int show_binlogs(THD* thd)
}
field_list.push_back(new Item_empty_string("Log_name", 255));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
mysql_bin_log.lock_index();
index_file=mysql_bin_log.get_index_file();
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 756b5f3c017..fce77969bcf 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -1105,7 +1105,8 @@ JOIN::exec()
(zero_result_cause?zero_result_cause:"No tables used"));
else
{
- result->send_fields(fields_list,1);
+ result->send_fields(fields_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
if (!having || having->val_int())
{
if (do_send_rows && (procedure ? (procedure->send_row(fields_list) ||
@@ -1512,12 +1513,45 @@ JOIN::exec()
DBUG_VOID_RETURN;
}
}
+ /* XXX: When can we have here thd->net.report_error not zero? */
+ if (thd->net.report_error)
+ {
+ error= thd->net.report_error;
+ DBUG_VOID_RETURN;
+ }
curr_join->having= curr_join->tmp_having;
- thd->proc_info="Sending data";
- error= thd->net.report_error ||
- do_select(curr_join, curr_fields_list, NULL, procedure);
- thd->limit_found_rows= curr_join->send_records;
- thd->examined_row_count= curr_join->examined_rows;
+ curr_join->fields= curr_fields_list;
+ curr_join->procedure= procedure;
+
+ if (unit == &thd->lex->unit &&
+ (unit->fake_select_lex == 0 || select_lex == unit->fake_select_lex) &&
+ thd->cursor && tables != const_tables)
+ {
+ /*
+ We are here if this is JOIN::exec for the last select of the main unit
+ and the client requested to open a cursor.
+ We check that not all tables are constant because this case is not
+ handled by do_select() separately, and this case is not implemented
+ for cursors yet.
+ */
+ DBUG_ASSERT(error == 0);
+ /*
+ curr_join is used only for reusable joins - that is,
+ to perform SELECT for each outer row (like in subselects).
+ This join is main, so we know for sure that curr_join == join.
+ */
+ DBUG_ASSERT(curr_join == this);
+ /* Open cursor for the last join sweep */
+ error= thd->cursor->open(this);
+ }
+ else
+ {
+ thd->proc_info="Sending data";
+ error= do_select(curr_join, curr_fields_list, NULL, procedure);
+ thd->limit_found_rows= curr_join->send_records;
+ thd->examined_row_count= curr_join->examined_rows;
+ }
+
DBUG_VOID_RETURN;
}
@@ -1566,6 +1600,306 @@ JOIN::cleanup()
}
+/************************* Cursor ******************************************/
+
+void
+Cursor::init_from_thd(THD *thd)
+{
+ /*
+ We need to save and reset thd->mem_root, otherwise it'll be freed
+ later in mysql_parse.
+ */
+ mem_root= thd->mem_root;
+ init_sql_alloc(&thd->mem_root,
+ thd->variables.query_alloc_block_size,
+ thd->variables.query_prealloc_size);
+
+ /*
+ The same is true for open tables and lock: save tables and zero THD
+ pointers to prevent table close in close_thread_tables (This is a part
+ of the temporary solution to make cursors work with minimal changes to
+ the current source base).
+ */
+ derived_tables= thd->derived_tables;
+ open_tables= thd->open_tables;
+ lock= thd->lock;
+ query_id= thd->query_id;
+ free_list= thd->free_list;
+ reset_thd(thd);
+ /*
+ XXX: thd->locked_tables is not changed.
+ What problems can we have with it if cursor is open?
+ */
+ /*
+ TODO: grab thd->free_list here?
+ */
+}
+
+
+void
+Cursor::init_thd(THD *thd)
+{
+ thd->mem_root= mem_root;
+
+ DBUG_ASSERT(thd->derived_tables == 0);
+ thd->derived_tables= derived_tables;
+
+ DBUG_ASSERT(thd->open_tables == 0);
+ thd->open_tables= open_tables;
+
+ DBUG_ASSERT(thd->lock== 0);
+ thd->lock= lock;
+ thd->query_id= query_id;
+ thd->free_list= free_list;
+}
+
+
+void
+Cursor::reset_thd(THD *thd)
+{
+ thd->derived_tables= 0;
+ thd->open_tables= 0;
+ thd->lock= 0;
+ thd->free_list= 0;
+}
+
+
+int
+Cursor::open(JOIN *join_arg)
+{
+ join= join_arg;
+
+ THD *thd= join->thd;
+
+ /* First non-constant table */
+ JOIN_TAB *join_tab= join->join_tab + join->const_tables;
+
+ /*
+ Send fields description to the client; server_status is sent
+ in 'EOF' packet, which ends send_fields().
+ */
+ thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
+ join->result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS);
+ ::send_eof(thd);
+ thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
+
+ /* Prepare JOIN for reading rows. */
+
+ Next_select_func end_select= join->sort_and_group || join->procedure &&
+ join->procedure->flags & PROC_GROUP ?
+ end_send_group : end_send;
+
+ join->join_tab[join->tables-1].next_select= end_select;
+ join->send_records= 0;
+ join->fetch_limit= join->unit->offset_limit_cnt;
+
+ /* Disable JOIN CACHE as it is not working with cursors yet */
+ for (JOIN_TAB *tab= join_tab; tab != join->join_tab + join->tables - 1; ++tab)
+ {
+ if (tab->next_select == sub_select_cache)
+ tab->next_select= sub_select;
+ }
+
+ DBUG_ASSERT(join_tab->table->reginfo.not_exists_optimize == 0);
+ DBUG_ASSERT(join_tab->not_used_in_distinct == 0);
+ /*
+ null_row is set only if row not found and it's outer join: should never
+ happen for the first table in join_tab list
+ */
+ DBUG_ASSERT(join_tab->table->null_row == 0);
+
+ return join_tab->read_first_record(join_tab);
+}
+
+
+/*
+ DESCRIPTION
+ Fetch next num_rows rows from the cursor and sent them to the client
+ PRECONDITION:
+ Cursor is open
+ RETURN VALUES:
+ -4 there are more rows, send_eof sent to the client
+ 0 no more rows, send_eof was sent to the client, cursor is closed
+ other fatal fetch error, cursor is closed (error is not reported)
+*/
+
+int
+Cursor::fetch(ulong num_rows)
+{
+ THD *thd= join->thd;
+ JOIN_TAB *join_tab= join->join_tab + join->const_tables;;
+ COND *on_expr= join_tab->on_expr;
+ COND *select_cond= join_tab->select_cond;
+ READ_RECORD *info= &join_tab->read_record;
+
+ int error= 0;
+
+ join->fetch_limit+= num_rows;
+
+ /*
+ Run while there are new rows in the first table;
+ For each row, satisfying ON and WHERE clauses (those parts of them which
+ can be evaluated early), call next_select.
+ */
+ do
+ {
+ int no_more_rows;
+
+ join->examined_rows++;
+
+ if (thd->killed) /* Aborted by user */
+ {
+ my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ return -1;
+ }
+
+ if (on_expr == 0 || on_expr->val_int())
+ {
+ if (select_cond == 0 || select_cond->val_int())
+ {
+ /*
+ TODO: call table->unlock_row() to unlock row failed selection,
+ when this feature will be used.
+ */
+ error= join_tab->next_select(join, join_tab + 1, 0);
+ DBUG_ASSERT(error <= 0);
+ if (error)
+ {
+ /* real error or LIMIT/FETCH LIMIT worked */
+ if (error == -4)
+ {
+ /*
+ FETCH LIMIT, read ahead one row, and close cursor
+ if there is no more rows XXX: to be fixed to support
+ non-equi-joins!
+ */
+ if ((no_more_rows= info->read_record(info)))
+ error= no_more_rows > 0 ? -1: 0;
+ }
+ break;
+ }
+ }
+ }
+ /* read next row; break loop if there was an error */
+ if ((no_more_rows= info->read_record(info)))
+ {
+ if (no_more_rows > 0)
+ error= -1;
+ else
+ {
+ enum { END_OF_RECORDS= 1 };
+ error= join_tab->next_select(join, join_tab+1, (int) END_OF_RECORDS);
+ }
+ break;
+ }
+ }
+ while (thd->net.report_error == 0);
+
+ if (thd->net.report_error)
+ error= -1;
+
+ switch (error) {
+ /* Fetch limit worked, possibly more rows are there */
+ case -4:
+ if (thd->transaction.all.innobase_tid)
+ ha_release_temporary_latches(thd);
+ thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
+ ::send_eof(thd);
+ thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
+ /* save references to memory, allocated during fetch */
+ mem_root= thd->mem_root;
+ free_list= thd->free_list;
+ break;
+ /* Limit clause worked: this is the same as 'no more rows' */
+ case -3: /* LIMIT clause worked */
+ error= 0;
+ /* fallthrough */
+ case 0: /* No more rows */
+ if (thd->transaction.all.innobase_tid)
+ ha_release_temporary_latches(thd);
+ close();
+ thd->server_status|= SERVER_STATUS_LAST_ROW_SENT;
+ ::send_eof(thd);
+ thd->server_status&= ~SERVER_STATUS_LAST_ROW_SENT;
+ join= 0;
+ unit= 0;
+ free_items(thd->free_list);
+ thd->free_list= free_list= 0;
+ /*
+ Must be last, as some memory might be allocated for free purposes,
+ like in free_tmp_table() (TODO: fix this issue)
+ */
+ mem_root= thd->mem_root;
+ free_root(&mem_root, MYF(0));
+ break;
+ default:
+ close();
+ join= 0;
+ unit= 0;
+ free_items(thd->free_list);
+ thd->free_list= free_list= 0;
+ /*
+ Must be last, as some memory might be allocated for free purposes,
+ like in free_tmp_table() (TODO: fix this issue)
+ */
+ mem_root= thd->mem_root;
+ free_root(&mem_root, MYF(0));
+ break;
+ }
+ return error;
+}
+
+
+void
+Cursor::close()
+{
+ THD *thd= join->thd;
+ join->join_free(0);
+ if (unit)
+ {
+ /* In case of UNIONs JOIN is freed inside unit->cleanup() */
+ unit->cleanup();
+ }
+ else
+ {
+ join->cleanup();
+ delete join;
+ }
+ /* XXX: Another hack: closing tables used in the cursor */
+ {
+ DBUG_ASSERT(lock || open_tables || derived_tables);
+
+ TABLE *tmp_open_tables= thd->open_tables;
+ TABLE *tmp_derived_tables= thd->derived_tables;
+ MYSQL_LOCK *tmp_lock= thd->lock;
+
+ thd->open_tables= open_tables;
+ thd->derived_tables= derived_tables;
+ thd->lock= lock;
+ close_thread_tables(thd);
+
+ thd->open_tables= tmp_derived_tables;
+ thd->derived_tables= tmp_derived_tables;
+ thd->lock= tmp_lock;
+ }
+}
+
+
+Cursor::~Cursor()
+{
+ if (is_open())
+ close();
+ free_items(free_list);
+ /*
+ Must be last, as some memory might be allocated for free purposes,
+ like in free_tmp_table() (TODO: fix this issue)
+ */
+ free_root(&mem_root, MYF(0));
+}
+
+/*********************************************************************/
+
+
int
mysql_select(THD *thd, Item ***rref_pointer_array,
TABLE_LIST *tables, uint wild_num, List<Item> &fields,
@@ -1637,6 +1971,16 @@ mysql_select(THD *thd, Item ***rref_pointer_array,
join->exec();
+ if (thd->cursor && thd->cursor->is_open())
+ {
+ /*
+ A cursor was opened for the last sweep in exec().
+ We are here only if this is mysql_select for top-level SELECT_LEX_UNIT
+ and there were no error.
+ */
+ free_join= 0;
+ }
+
if (thd->lex->describe & DESCRIBE_EXTENDED)
{
select_lex->where= join->conds_history;
@@ -5310,7 +5654,8 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
if (having && having->val_int() == 0)
send_row=0;
}
- if (!(result->send_fields(fields,1)))
+ if (!(result->send_fields(fields,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)))
{
if (send_row)
{
@@ -7035,7 +7380,7 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
{
int error= 0;
JOIN_TAB *join_tab;
- int (*end_select)(JOIN *, struct st_join_table *,bool);
+ Next_select_func end_select;
DBUG_ENTER("do_select");
join->procedure=procedure;
@@ -7043,7 +7388,8 @@ do_select(JOIN *join,List<Item> *fields,TABLE *table,Procedure *procedure)
Tell the client how many fields there are in a row
*/
if (!table)
- join->result->send_fields(*fields,1);
+ join->result->send_fields(*fields,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
else
{
VOID(table->file->extra(HA_EXTRA_WRITE_CACHE));
@@ -8076,6 +8422,14 @@ end_send(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
}
DBUG_RETURN(-3); // Abort nicely
}
+ else if (join->send_records >= join->fetch_limit)
+ {
+ /*
+ There is a server side cursor and all rows for
+ this fetch request are sent.
+ */
+ DBUG_RETURN(-4);
+ }
}
else
{
@@ -8150,6 +8504,14 @@ end_send_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
join->do_send_rows=0;
join->unit->select_limit_cnt = HA_POS_ERROR;
}
+ else if (join->send_records >= join->fetch_limit)
+ {
+ /*
+ There is a server side cursor and all rows
+ for this fetch request are sent.
+ */
+ DBUG_RETURN(-4);
+ }
}
}
else
diff --git a/sql/sql_select.h b/sql/sql_select.h
index 8ffe50e6db2..284e4315917 100644
--- a/sql/sql_select.h
+++ b/sql/sql_select.h
@@ -81,6 +81,10 @@ enum join_type { JT_UNKNOWN,JT_SYSTEM,JT_CONST,JT_EQ_REF,JT_REF,JT_MAYBE_REF,
class JOIN;
+typedef int (*Next_select_func)(JOIN *,struct st_join_table *,bool);
+typedef int (*Read_record_func)(struct st_join_table *tab);
+
+
typedef struct st_join_table {
TABLE *table;
KEYUSE *keyuse; /* pointer to first used key */
@@ -95,8 +99,8 @@ typedef struct st_join_table {
st_join_table *first_upper; /* first inner table for embedding outer join */
st_join_table *first_unmatched; /* used for optimization purposes only */
const char *info;
- int (*read_first_record)(struct st_join_table *tab);
- int (*next_select)(JOIN *,struct st_join_table *,bool);
+ Read_record_func read_first_record;
+ Next_select_func next_select;
READ_RECORD read_record;
double worst_seeks;
key_map const_keys; /* Keys with constant part */
@@ -149,6 +153,16 @@ class JOIN :public Sql_alloc
bool do_send_rows;
table_map const_table_map,found_const_table_map,outer_join;
ha_rows send_records,found_records,examined_rows,row_limit, select_limit;
+ /*
+ Used to fetch no more than given amount of rows per one
+ fetch operation of server side cursor.
+ The value is checked in end_send and end_send_group in fashion, similar
+ to offset_limit_cnt:
+ - fetch_limit= HA_POS_ERROR if there is no cursor.
+ - when we open a cursor, we set fetch_limit to 0,
+ - on each fetch iteration we add num_rows to fetch to fetch_limit
+ */
+ ha_rows fetch_limit;
POSITION positions[MAX_TABLES+1],best_positions[MAX_TABLES+1];
double best_read;
List<Item> *fields;
@@ -239,6 +253,7 @@ class JOIN :public Sql_alloc
do_send_rows= 1;
send_records= 0;
found_records= 0;
+ fetch_limit= HA_POS_ERROR;
examined_rows= 0;
exec_tmp_table1= 0;
exec_tmp_table2= 0;
@@ -319,6 +334,44 @@ class JOIN :public Sql_alloc
};
+/*
+ Server-side cursor (now stands only for basic read-only cursor)
+ See class implementation in sql_select.cc
+*/
+
+class Cursor: public Sql_alloc, public Item_arena
+{
+ JOIN *join;
+ SELECT_LEX_UNIT *unit;
+
+ TABLE *open_tables;
+ MYSQL_LOCK *lock;
+ TABLE *derived_tables;
+ /* List of items created during execution */
+ ulong query_id;
+public:
+ select_send result;
+
+ /* Temporary implementation as now we replace THD state by value */
+ /* Save THD state into cursor */
+ void init_from_thd(THD *thd);
+ /* Restore THD from cursor to continue cursor execution */
+ void init_thd(THD *thd);
+ /* bzero cursor state in THD */
+ void reset_thd(THD *thd);
+
+ int open(JOIN *join);
+ int fetch(ulong num_rows);
+ void reset() { join= 0; }
+ bool is_open() const { return join != 0; }
+ void close();
+
+ void set_unit(SELECT_LEX_UNIT *unit_arg) { unit= unit_arg; }
+ Cursor() :join(0), unit(0) {}
+ ~Cursor();
+};
+
+
typedef struct st_select_check {
uint const_ref,reg_ref;
} SELECT_CHECK;
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 5064a95d468..c11207eac24 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -66,7 +66,8 @@ mysqld_show_dbs(THD *thd,const char *wild)
strxmov(end," (",wild,")",NullS);
field_list.push_back(field);
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
if (mysql_find_files(thd,&files,NullS,mysql_data_home,wild,1))
DBUG_RETURN(1);
@@ -107,7 +108,8 @@ int mysqld_show_open_tables(THD *thd,const char *wild)
field_list.push_back(new Item_return_int("In_use", 1, MYSQL_TYPE_TINY));
field_list.push_back(new Item_return_int("Name_locked", 4, MYSQL_TYPE_TINY));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
if (!(open_list=list_open_tables(thd,wild)) && thd->is_fatal_error)
@@ -160,7 +162,8 @@ int mysqld_show_tables(THD *thd,const char *db,const char *wild)
field_list.push_back(field);
if (show_type)
field_list.push_back(new Item_empty_string("table_type", 10));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
if (mysql_find_files(thd,&files,db,path,wild,0))
DBUG_RETURN(-1);
@@ -208,7 +211,8 @@ int mysqld_show_storage_engines(THD *thd)
field_list.push_back(new Item_empty_string("Support",10));
field_list.push_back(new Item_empty_string("Comment",80));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
const char *default_type_name=
@@ -282,7 +286,8 @@ int mysqld_show_privileges(THD *thd)
field_list.push_back(new Item_empty_string("Context",15));
field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
show_privileges_st *privilege= sys_privileges;
@@ -357,7 +362,8 @@ int mysqld_show_column_types(THD *thd)
field_list.push_back(new Item_empty_string("Default",NAME_LEN));
field_list.push_back(new Item_empty_string("Comment",NAME_LEN));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
/* TODO: Change the loop to not use 'i' */
@@ -530,7 +536,8 @@ int mysqld_extend_show_tables(THD *thd,const char *db,const char *wild)
item->maybe_null=1;
field_list.push_back(item=new Item_empty_string("Comment",80));
item->maybe_null=1;
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
if (mysql_find_files(thd,&files,db,path,wild,0))
@@ -721,7 +728,7 @@ mysqld_show_fields(THD *thd, TABLE_LIST *table_list,const char *wild,
}
// Send first number of fields and records
if (protocol->send_records_num(&field_list, (ulonglong)file->records) ||
- protocol->send_fields(&field_list,0))
+ protocol->send_fields(&field_list, Protocol::SEND_EOF))
DBUG_RETURN(1);
restore_record(table,default_values); // Get empty record
@@ -859,7 +866,8 @@ mysqld_show_create(THD *thd, TABLE_LIST *table_list)
field_list.push_back(new Item_empty_string("Create Table",
max(buffer.length(),1024)));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
protocol->prepare_for_resend();
buffer.length(0);
@@ -943,7 +951,8 @@ int mysqld_show_create_db(THD *thd, char *dbname,
field_list.push_back(new Item_empty_string("Database",NAME_LEN));
field_list.push_back(new Item_empty_string("Create Database",1024));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
protocol->prepare_for_resend();
@@ -985,7 +994,8 @@ mysqld_show_logs(THD *thd)
field_list.push_back(new Item_empty_string("Type",10));
field_list.push_back(new Item_empty_string("Status",10));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
#ifdef HAVE_BERKELEY_DB
@@ -1034,7 +1044,8 @@ mysqld_show_keys(THD *thd, TABLE_LIST *table_list)
field_list.push_back(new Item_empty_string("Comment",255));
item->maybe_null=1;
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
KEY *key_info=table->key_info;
@@ -1121,7 +1132,8 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
field_list.push_back(new Item_field(field));
}
restore_record(table,default_values); // Get empty record
- if (thd->protocol->send_fields(&field_list,2))
+ if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS |
+ Protocol::SEND_EOF))
DBUG_VOID_RETURN;
net_flush(&thd->net);
DBUG_VOID_RETURN;
@@ -1615,7 +1627,8 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
field->maybe_null=1;
field_list.push_back(field=new Item_empty_string("Info",max_query_length));
field->maybe_null=1;
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_VOID_RETURN;
VOID(pthread_mutex_lock(&LOCK_thread_count)); // For unlink from list
@@ -1751,7 +1764,8 @@ int mysqld_show_collations(THD *thd, const char *wild)
field_list.push_back(new Item_empty_string("Compiled",30));
field_list.push_back(new Item_return_int("Sortlen",3, FIELD_TYPE_SHORT));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ )
@@ -1804,7 +1818,8 @@ int mysqld_show_charsets(THD *thd, const char *wild)
field_list.push_back(new Item_empty_string("Default collation",60));
field_list.push_back(new Item_return_int("Maxlen",3, FIELD_TYPE_SHORT));
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
for ( cs= all_charsets ; cs < all_charsets+255 ; cs++ )
@@ -1838,7 +1853,8 @@ int mysqld_show(THD *thd, const char *wild, show_var_st *variables,
field_list.push_back(new Item_empty_string("Variable_name",30));
field_list.push_back(new Item_empty_string("Value",256));
- if (protocol->send_fields(&field_list,1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1); /* purecov: inspected */
null_lex_str.str= 0; // For sys_var->value_ptr()
null_lex_str.length= 0;
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index ea57536d7c1..93fb7930da7 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -1738,7 +1738,8 @@ static int mysql_admin_table(THD* thd, TABLE_LIST* tables,
item->maybe_null = 1;
field_list.push_back(item = new Item_empty_string("Msg_text", 255));
item->maybe_null = 1;
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
mysql_ha_close(thd, tables, /*dont_send_ok*/ 1, /*dont_lock*/ 1);
@@ -3449,7 +3450,8 @@ int mysql_checksum_table(THD *thd, TABLE_LIST *tables, HA_CHECK_OPT *check_opt)
item->maybe_null= 1;
field_list.push_back(item=new Item_int("Checksum",(longlong) 1,21));
item->maybe_null= 1;
- if (protocol->send_fields(&field_list, 1))
+ if (protocol->send_fields(&field_list,
+ Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(-1);
for (table= tables; table; table= table->next_local)
diff --git a/sql/sql_union.cc b/sql/sql_union.cc
index 35f8a390308..d6b776571f2 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -31,7 +31,13 @@ int mysql_union(THD *thd, LEX *lex, select_result *result,
int res, res_cln;
if (!(res= unit->prepare(thd, result, SELECT_NO_UNLOCK)))
res= unit->exec();
- res_cln= unit->cleanup();
+ if (res == 0 && thd->cursor && thd->cursor->is_open())
+ {
+ thd->cursor->set_unit(unit);
+ res_cln= 0;
+ }
+ else
+ res_cln= unit->cleanup();
DBUG_RETURN(res?res:res_cln);
}