mirror of
https://github.com/MariaDB/server.git
synced 2025-01-17 04:22:27 +01:00
Merge bk-internal.mysql.com:/home/bk/mysql-5.0
into mysql.com:/home/kostja/mysql/mysql-5.0-sap-new
This commit is contained in:
commit
e67584151e
31 changed files with 1091 additions and 150 deletions
|
@ -800,3 +800,4 @@ vio/test-sslclient
|
|||
vio/test-sslserver
|
||||
vio/viotest-ssl
|
||||
libmysqld/sql_view.cc
|
||||
libmysqld/examples/client_test.cc
|
||||
|
|
|
@ -580,6 +580,12 @@ typedef struct st_mysql_stmt
|
|||
int (*read_row_func)(struct st_mysql_stmt *stmt,
|
||||
unsigned char **row);
|
||||
unsigned long stmt_id; /* Id for prepared statement */
|
||||
unsigned long flags; /* i.e. type of cursor to open */
|
||||
/*
|
||||
Copied from mysql->server_status after execute/fetch to know
|
||||
server-side cursor status for this statement.
|
||||
*/
|
||||
unsigned int server_status;
|
||||
unsigned int last_errno; /* error code */
|
||||
unsigned int param_count; /* inpute parameters count */
|
||||
unsigned int field_count; /* number of columns in result set */
|
||||
|
@ -608,7 +614,12 @@ enum enum_stmt_attr_type
|
|||
In the new API we do that only by request because it slows down
|
||||
mysql_stmt_store_result sufficiently.
|
||||
*/
|
||||
STMT_ATTR_UPDATE_MAX_LENGTH
|
||||
STMT_ATTR_UPDATE_MAX_LENGTH,
|
||||
/*
|
||||
unsigned long with combination of cursor flags (read only, for update,
|
||||
etc)
|
||||
*/
|
||||
STMT_ATTR_CURSOR_TYPE
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ enum enum_server_command
|
|||
COM_TIME, COM_DELAYED_INSERT, COM_CHANGE_USER, COM_BINLOG_DUMP,
|
||||
COM_TABLE_DUMP, COM_CONNECT_OUT, COM_REGISTER_SLAVE,
|
||||
COM_PREPARE, COM_EXECUTE, COM_LONG_DATA, COM_CLOSE_STMT,
|
||||
COM_RESET_STMT, COM_SET_OPTION,
|
||||
COM_RESET_STMT, COM_SET_OPTION, COM_FETCH,
|
||||
COM_END /* Must be last */
|
||||
};
|
||||
|
||||
|
@ -132,6 +132,17 @@ enum enum_server_command
|
|||
#define SERVER_MORE_RESULTS_EXISTS 8 /* Multi query - next query exists */
|
||||
#define SERVER_QUERY_NO_GOOD_INDEX_USED 16
|
||||
#define SERVER_QUERY_NO_INDEX_USED 32
|
||||
/*
|
||||
The server was able to fulfill client request and open read-only
|
||||
non-scrollable cursor for the query. This flag comes in server
|
||||
status with reply to COM_EXECUTE and COM_EXECUTE_DIRECT commands.
|
||||
*/
|
||||
#define SERVER_STATUS_CURSOR_EXISTS 64
|
||||
/*
|
||||
This flag is sent with last row of read-only cursor, in reply to
|
||||
COM_FETCH command.
|
||||
*/
|
||||
#define SERVER_STATUS_LAST_ROW_SENT 128
|
||||
|
||||
#define MYSQL_ERRMSG_SIZE 512
|
||||
#define NET_READ_TIMEOUT 30 /* Timeout on read */
|
||||
|
@ -257,6 +268,16 @@ enum enum_shutdown_level {
|
|||
KILL_CONNECTION= 255
|
||||
};
|
||||
|
||||
|
||||
enum enum_cursor_type
|
||||
{
|
||||
CURSOR_TYPE_NO_CURSOR= 0,
|
||||
CURSOR_TYPE_READ_ONLY= 1,
|
||||
CURSOR_TYPE_FOR_UPDATE= 2,
|
||||
CURSOR_TYPE_SCROLLABLE= 4
|
||||
};
|
||||
|
||||
|
||||
/* options for mysql_set_option */
|
||||
enum enum_mysql_set_option
|
||||
{
|
||||
|
|
|
@ -1668,6 +1668,7 @@ myodbc_remove_escape(MYSQL *mysql,char *name)
|
|||
|
||||
static int stmt_read_row_unbuffered(MYSQL_STMT *stmt, unsigned char **row);
|
||||
static int stmt_read_row_buffered(MYSQL_STMT *stmt, unsigned char **row);
|
||||
static int stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row);
|
||||
static int stmt_read_row_no_data(MYSQL_STMT *stmt, unsigned char **row);
|
||||
|
||||
/*
|
||||
|
@ -2387,7 +2388,7 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length)
|
|||
|
||||
mysql->last_used_con= mysql;
|
||||
int4store(buff, stmt->stmt_id); /* Send stmt id to server */
|
||||
buff[4]= (char) 0; /* no flags */
|
||||
buff[4]= (char) stmt->flags;
|
||||
int4store(buff+5, 1); /* iteration count */
|
||||
if (cli_advanced_command(mysql, COM_EXECUTE, buff, sizeof(buff),
|
||||
packet, length, 1) ||
|
||||
|
@ -2397,6 +2398,7 @@ static my_bool execute(MYSQL_STMT *stmt, char *packet, ulong length)
|
|||
DBUG_RETURN(1);
|
||||
}
|
||||
stmt->affected_rows= mysql->affected_rows;
|
||||
stmt->server_status= mysql->server_status;
|
||||
stmt->insert_id= mysql->insert_id;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
@ -2552,6 +2554,59 @@ error:
|
|||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Fetch statement row using server side cursor.
|
||||
|
||||
SYNOPSIS
|
||||
stmt_read_row_from_cursor()
|
||||
|
||||
RETURN VALUE
|
||||
0 success
|
||||
1 error
|
||||
MYSQL_NO_DATA end of data
|
||||
*/
|
||||
|
||||
static int
|
||||
stmt_read_row_from_cursor(MYSQL_STMT *stmt, unsigned char **row)
|
||||
{
|
||||
if (stmt->data_cursor)
|
||||
return stmt_read_row_buffered(stmt, row);
|
||||
if (stmt->server_status & SERVER_STATUS_LAST_ROW_SENT)
|
||||
stmt->server_status &= ~SERVER_STATUS_LAST_ROW_SENT;
|
||||
else
|
||||
{
|
||||
MYSQL *mysql= stmt->mysql;
|
||||
NET *net= &mysql->net;
|
||||
MYSQL_DATA *result= &stmt->result;
|
||||
char buff[4 /* statement id */ +
|
||||
4 /* number of rows to fetch */];
|
||||
|
||||
free_root(&result->alloc, MYF(MY_KEEP_PREALLOC));
|
||||
result->data= NULL;
|
||||
result->rows= 0;
|
||||
/* Send row request to the server */
|
||||
int4store(buff, stmt->stmt_id);
|
||||
int4store(buff + 4, 1); /* number of rows to fetch */
|
||||
if (cli_advanced_command(mysql, COM_FETCH, buff, sizeof(buff),
|
||||
NullS, 0, 1))
|
||||
{
|
||||
set_stmt_errmsg(stmt, net->last_error, net->last_errno, net->sqlstate);
|
||||
return 1;
|
||||
}
|
||||
stmt->server_status= mysql->server_status;
|
||||
if (cli_read_binary_rows(stmt))
|
||||
return 1;
|
||||
stmt->server_status= mysql->server_status;
|
||||
|
||||
stmt->data_cursor= result->data;
|
||||
return stmt_read_row_buffered(stmt, row);
|
||||
}
|
||||
*row= 0;
|
||||
return MYSQL_NO_DATA;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Default read row function to not SIGSEGV in client in
|
||||
case of wrong sequence of API calls.
|
||||
|
@ -2593,6 +2648,9 @@ my_bool STDCALL mysql_stmt_attr_set(MYSQL_STMT *stmt,
|
|||
case STMT_ATTR_UPDATE_MAX_LENGTH:
|
||||
stmt->update_max_length= value ? *(const my_bool*) value : 0;
|
||||
break;
|
||||
case STMT_ATTR_CURSOR_TYPE:
|
||||
stmt->flags= value ? *(const unsigned long *) value : 0;
|
||||
break;
|
||||
default:
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -2608,6 +2666,9 @@ my_bool STDCALL mysql_stmt_attr_get(MYSQL_STMT *stmt,
|
|||
case STMT_ATTR_UPDATE_MAX_LENGTH:
|
||||
*(unsigned long *) value= stmt->update_max_length;
|
||||
break;
|
||||
case STMT_ATTR_CURSOR_TYPE:
|
||||
*(unsigned long *) value= stmt->flags;
|
||||
break;
|
||||
default:
|
||||
return TRUE;
|
||||
}
|
||||
|
@ -2711,9 +2772,17 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
|
|||
stmt->state= MYSQL_STMT_EXECUTE_DONE;
|
||||
if (stmt->field_count)
|
||||
{
|
||||
stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
|
||||
stmt->unbuffered_fetch_cancelled= FALSE;
|
||||
stmt->read_row_func= stmt_read_row_unbuffered;
|
||||
if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
|
||||
{
|
||||
mysql->status= MYSQL_STATUS_READY;
|
||||
stmt->read_row_func= stmt_read_row_from_cursor;
|
||||
}
|
||||
else
|
||||
{
|
||||
stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
|
||||
stmt->unbuffered_fetch_cancelled= FALSE;
|
||||
stmt->read_row_func= stmt_read_row_unbuffered;
|
||||
}
|
||||
}
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ mysql_SOURCES = mysql.cc readline.cc completion_hash.cc \
|
|||
mysql_LDADD = @readline_link@ @TERMCAP_LIB@ $(LDADD)
|
||||
|
||||
client_test_LINK = $(CXXLINK)
|
||||
client_test_SOURCES = client_test.c
|
||||
client_test_SOURCES = client_test.cc
|
||||
|
||||
clean:
|
||||
rm -f $(client_sources)
|
||||
|
|
|
@ -568,7 +568,7 @@ err:
|
|||
|
||||
C_MODE_END
|
||||
|
||||
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;
|
||||
|
@ -615,7 +615,7 @@ bool Protocol::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;
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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])
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,21 +1963,76 @@ 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
|
||||
|
@ -2084,8 +2203,11 @@ void Prepared_statement::setup_set_params()
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
Prepared_statement::~Prepared_statement()
|
||||
{
|
||||
if (cursor)
|
||||
cursor->Cursor::~Cursor();
|
||||
free_items(free_list);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ INCLUDES = -I$(top_srcdir)/include $(openssl_includes)
|
|||
LIBS = @CLIENT_LIBS@
|
||||
LDADD = @CLIENT_EXTRA_LDFLAGS@ ../libmysql/libmysqlclient.la
|
||||
client_test_LDADD= $(LDADD) $(CXXLDFLAGS)
|
||||
client_test_SOURCES= client_test.c
|
||||
client_test_SOURCES= client_test.cc
|
||||
insert_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES)
|
||||
select_test_DEPENDENCIES= $(LIBRARIES) $(pkglib_LTLIBRARIES)
|
||||
|
||||
|
|
|
@ -693,6 +693,240 @@ static void client_use_result()
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
Accepts arbitrary number of queries and runs them against the database.
|
||||
Used to fill tables for each test.
|
||||
*/
|
||||
|
||||
void fill_tables(const char **query_list, unsigned query_count)
|
||||
{
|
||||
int rc;
|
||||
for (const char **query= query_list; query < query_list + query_count;
|
||||
++query)
|
||||
{
|
||||
rc= mysql_query(mysql, *query);
|
||||
if (rc)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"fill_tables failed: query is\n"
|
||||
"%s,\n"
|
||||
"error: %s\n", *query, mysql_error(mysql));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
All state of fetch from one statement: statement handle, out buffers,
|
||||
fetch position.
|
||||
See fetch_n for for the only use case.
|
||||
*/
|
||||
|
||||
struct Stmt_fetch
|
||||
{
|
||||
enum { MAX_COLUMN_LENGTH= 255 };
|
||||
|
||||
Stmt_fetch() {}
|
||||
~Stmt_fetch();
|
||||
|
||||
void init(unsigned stmt_no_arg, const char *query_arg);
|
||||
int fetch_row();
|
||||
|
||||
const char *query;
|
||||
unsigned stmt_no;
|
||||
MYSQL_STMT *stmt;
|
||||
bool is_open;
|
||||
MYSQL_BIND *bind_array;
|
||||
char **out_data;
|
||||
unsigned long *out_data_length;
|
||||
unsigned column_count;
|
||||
unsigned row_count;
|
||||
};
|
||||
|
||||
/*
|
||||
Create statement handle, prepare it with statement, execute and allocate
|
||||
fetch buffers.
|
||||
*/
|
||||
|
||||
void Stmt_fetch::init(unsigned stmt_no_arg, const char *query_arg)
|
||||
{
|
||||
unsigned long type= CURSOR_TYPE_READ_ONLY;
|
||||
int rc;
|
||||
unsigned i;
|
||||
MYSQL_RES *metadata;
|
||||
|
||||
/* Save query and statement number for error messages */
|
||||
stmt_no= stmt_no_arg;
|
||||
query= query_arg;
|
||||
|
||||
stmt= mysql_stmt_init(mysql);
|
||||
|
||||
rc= mysql_stmt_prepare(stmt, query, strlen(query));
|
||||
if (rc)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"mysql_stmt_prepare of stmt %d failed:\n"
|
||||
"query: %s\n"
|
||||
"error: %s\n",
|
||||
stmt_no, query, mysql_stmt_error(stmt));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/*
|
||||
The attribute is sent to server on execute and asks to open read-only
|
||||
for result set
|
||||
*/
|
||||
mysql_stmt_attr_set(stmt, STMT_ATTR_CURSOR_TYPE, (const void *) &type);
|
||||
|
||||
rc= mysql_stmt_execute(stmt);
|
||||
if (rc)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"mysql_stmt_execute of stmt %d failed:\n"
|
||||
"query: %s\n"
|
||||
"error: %s\n",
|
||||
stmt_no, query, mysql_stmt_error(stmt));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* Find out total number of columns in result set */
|
||||
metadata= mysql_stmt_result_metadata(stmt);
|
||||
column_count= mysql_num_fields(metadata);
|
||||
mysql_free_result(metadata);
|
||||
|
||||
/*
|
||||
Now allocate bind handles and buffers for output data:
|
||||
calloc memory to reduce number of MYSQL_BIND members we need to
|
||||
set up.
|
||||
*/
|
||||
|
||||
bind_array= (MYSQL_BIND *) calloc(1, sizeof(MYSQL_BIND) * column_count);
|
||||
out_data= (char **) calloc(1, sizeof(*out_data) * column_count);
|
||||
out_data_length= (unsigned long *) calloc(1,
|
||||
sizeof(*out_data_length) * column_count);
|
||||
|
||||
for (i= 0; i < column_count; ++i)
|
||||
{
|
||||
out_data[i]= (char *) calloc(1, MAX_COLUMN_LENGTH);
|
||||
bind_array[i].buffer_type= MYSQL_TYPE_STRING;
|
||||
bind_array[i].buffer= out_data[i];
|
||||
bind_array[i].buffer_length= MAX_COLUMN_LENGTH;
|
||||
bind_array[i].length= out_data_length + i;
|
||||
}
|
||||
|
||||
mysql_stmt_bind_result(stmt, bind_array);
|
||||
|
||||
row_count= 0;
|
||||
is_open= true;
|
||||
|
||||
/* Ready for reading rows */
|
||||
}
|
||||
|
||||
|
||||
/* Fetch and print one row from cursor */
|
||||
|
||||
int Stmt_fetch::fetch_row()
|
||||
{
|
||||
int rc;
|
||||
unsigned i;
|
||||
|
||||
if ((rc= mysql_stmt_fetch(stmt)) == 0)
|
||||
{
|
||||
++row_count;
|
||||
printf("Stmt %d fetched row %d:\n", stmt_no, row_count);
|
||||
for (i= 0; i < column_count; ++i)
|
||||
{
|
||||
out_data[i][out_data_length[i]]= '\0';
|
||||
printf("column %d: %s\n", i+1, out_data[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
is_open= false;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
Stmt_fetch::~Stmt_fetch()
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
for (i= 0; i < column_count; ++i)
|
||||
free(out_data[i]);
|
||||
free(out_data);
|
||||
free(bind_array);
|
||||
mysql_stmt_close(stmt);
|
||||
}
|
||||
|
||||
/* We need these to compile without libstdc++ */
|
||||
|
||||
void *operator new[] (size_t sz)
|
||||
{
|
||||
return (void *) malloc (sz ? sz : 1);
|
||||
}
|
||||
|
||||
void operator delete[] (void *ptr) throw ()
|
||||
{
|
||||
if (ptr)
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
For given array of queries, open query_count cursors and fetch
|
||||
from them in simultaneous manner.
|
||||
In case there was an error in one of the cursors, continue
|
||||
reading from the rest.
|
||||
*/
|
||||
|
||||
bool fetch_n(const char **query_list, unsigned query_count)
|
||||
{
|
||||
unsigned open_statements= query_count;
|
||||
unsigned i;
|
||||
int rc, error_count= 0;
|
||||
Stmt_fetch *stmt_array= new Stmt_fetch[query_count];
|
||||
Stmt_fetch *stmt;
|
||||
|
||||
for (i= 0; i < query_count; ++i)
|
||||
{
|
||||
/* Init will exit(1) in case of error */
|
||||
stmt_array[i].init(i, query_list[i]);
|
||||
}
|
||||
|
||||
while (open_statements)
|
||||
{
|
||||
for (stmt= stmt_array; stmt < stmt_array + query_count; ++stmt)
|
||||
{
|
||||
if (stmt->is_open && (rc= stmt->fetch_row()))
|
||||
{
|
||||
--open_statements;
|
||||
/*
|
||||
We try to fetch from the rest of the statements in case of
|
||||
error
|
||||
*/
|
||||
if (rc != MYSQL_NO_DATA)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"Got error reading rows from statement %d,\n"
|
||||
"query is: %s,\n"
|
||||
"error message: %s", stmt - stmt_array, stmt->query,
|
||||
mysql_stmt_error(stmt->stmt));
|
||||
++error_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (error_count)
|
||||
fprintf(stderr, "Fetch FAILED");
|
||||
else
|
||||
{
|
||||
unsigned total_row_count= 0;
|
||||
for (stmt= stmt_array; stmt < stmt_array + query_count; ++stmt)
|
||||
total_row_count+= stmt->row_count;
|
||||
printf("Success, total rows fetched: %d\n", total_row_count);
|
||||
}
|
||||
delete [] stmt_array;
|
||||
return error_count != 0;
|
||||
}
|
||||
|
||||
/* Separate thread query to test some cases */
|
||||
|
||||
static my_bool thread_query(char *query)
|
||||
|
@ -10043,7 +10277,7 @@ static void test_view()
|
|||
int rc, i;
|
||||
MYSQL_BIND bind[1];
|
||||
char str_data[50];
|
||||
long length = 0L;
|
||||
ulong length = 0L;
|
||||
long is_null = 0L;
|
||||
const char *query=
|
||||
"SELECT COUNT(*) FROM v1 WHERE `SERVERNAME`=?";
|
||||
|
@ -10141,7 +10375,7 @@ static void test_view_2where()
|
|||
int rc, i;
|
||||
MYSQL_BIND bind[8];
|
||||
char parms[8][100];
|
||||
long length[8];
|
||||
ulong length[8];
|
||||
const char *query= "SELECT `RELID` ,`REPORT` ,`HANDLE` ,`LOG_GROUP` ,`USERNAME` ,`VARIANT` ,`TYPE` ,`VERSION` ,`ERFDAT` ,`ERFTIME` ,`ERFNAME` ,`AEDAT` ,`AETIME` ,`AENAME` ,`DEPENDVARS` ,`INACTIVE` FROM `V_LTDX` WHERE `MANDT` = ? AND `RELID` = ? AND `REPORT` = ? AND `HANDLE` = ? AND `LOG_GROUP` = ? AND `USERNAME` IN ( ? , ? ) AND `TYPE` = ?";
|
||||
|
||||
myheader("test_view_2where");
|
||||
|
@ -10189,7 +10423,7 @@ static void test_view_star()
|
|||
int rc, i;
|
||||
MYSQL_BIND bind[8];
|
||||
char parms[8][100];
|
||||
long length[8];
|
||||
ulong length[8];
|
||||
const char *query= "SELECT * FROM vt1 WHERE a IN (?,?)";
|
||||
|
||||
myheader("test_view_star");
|
||||
|
@ -10338,7 +10572,7 @@ static void test_view_insert_fields()
|
|||
{
|
||||
MYSQL_STMT *stmt;
|
||||
char parm[11][1000];
|
||||
long l[11];
|
||||
ulong l[11];
|
||||
int rc, i;
|
||||
MYSQL_BIND bind[11];
|
||||
const char *query= "INSERT INTO `v1` ( `K1C4` ,`K2C4` ,`K3C4` ,`K4N4` ,`F1C4` ,`F2I4` ,`F3N5` ,`F7F8` ,`F6N4` ,`F5C8` ,`F9D8` ) VALUES( ? , ? , ? , ? , ? , ? , ? , ? , ? , ? , ? )";
|
||||
|
@ -10390,6 +10624,61 @@ static void test_view_insert_fields()
|
|||
|
||||
}
|
||||
|
||||
|
||||
static void test_basic_cursors()
|
||||
{
|
||||
myheader("test_basic_cursors");
|
||||
const char *basic_tables[]=
|
||||
{
|
||||
"DROP TABLE IF EXISTS t1, t2",
|
||||
|
||||
"CREATE TABLE t1 "
|
||||
"(id INTEGER NOT NULL PRIMARY KEY, "
|
||||
" name VARCHAR(20) NOT NULL)",
|
||||
|
||||
"INSERT INTO t1 (id, name) VALUES "
|
||||
" (2, 'Ja'), (3, 'Ede'), "
|
||||
" (4, 'Haag'), (5, 'Kabul'), "
|
||||
" (6, 'Almere'), (7, 'Utrecht'), "
|
||||
" (8, 'Qandahar'), (9, 'Amsterdam'), "
|
||||
" (10, 'Amersfoort'), (11, 'Constantine')",
|
||||
|
||||
"CREATE TABLE t2 "
|
||||
"(id INTEGER NOT NULL PRIMARY KEY, "
|
||||
" name VARCHAR(20) NOT NULL)",
|
||||
|
||||
"INSERT INTO t2 (id, name) VALUES "
|
||||
" (4, 'Guam'), (5, 'Aruba'), "
|
||||
" (6, 'Angola'), (7, 'Albania'), "
|
||||
" (8, 'Anguilla'), (9, 'Argentina'), "
|
||||
" (10, 'Azerbaijan'), (11, 'Afghanistan'), "
|
||||
" (12, 'Burkina Faso'), (13, 'Faroe Islands')"
|
||||
};
|
||||
|
||||
fill_tables(basic_tables, sizeof(basic_tables)/sizeof(*basic_tables));
|
||||
|
||||
const char *queries[]=
|
||||
{
|
||||
"SELECT * FROM t1",
|
||||
"SELECT * FROM t2"
|
||||
};
|
||||
|
||||
fetch_n(queries, sizeof(queries)/sizeof(*queries));
|
||||
}
|
||||
|
||||
|
||||
static void test_cursors_with_union()
|
||||
{
|
||||
myheader("test_cursors_with_union");
|
||||
|
||||
const char *queries[]=
|
||||
{
|
||||
"SELECT t1.name FROM t1 UNION SELECT t2.name FROM t2",
|
||||
"SELECT t1.id FROM t1 WHERE t1.id < 5"
|
||||
};
|
||||
fetch_n(queries, sizeof(queries)/sizeof(*queries));
|
||||
}
|
||||
|
||||
/*
|
||||
Read and parse arguments and MySQL options from my.cnf
|
||||
*/
|
||||
|
@ -10694,6 +10983,8 @@ int main(int argc, char **argv)
|
|||
test_view_insert(); /* inserting in VIEW without field list */
|
||||
test_left_join_view(); /* left join on VIEW with WHERE condition */
|
||||
test_view_insert_fields(); /* insert into VIOEW with fields list */
|
||||
test_basic_cursors();
|
||||
test_cursors_with_union();
|
||||
/*
|
||||
XXX: PLEASE RUN THIS PROGRAM UNDER VALGRIND AND VERIFY THAT YOUR TEST
|
||||
DOESN'T CONTAIN WARNINGS/ERRORS BEFORE YOU PUSH.
|
Loading…
Reference in a new issue