Merge lp:~vlad-lesin/percona-server/5.6-binary-answer into lp:percona-server/5.6

Proposed by Vlad Lesin on 2014-07-08
Status: Work in progress
Proposed branch: lp:~vlad-lesin/percona-server/5.6-binary-answer
Merge into: lp:percona-server/5.6
Diff against target: 1108 lines (+668/-36)
17 files modified
client/mysqltest.cc (+19/-6)
include/mysql.h (+4/-0)
include/mysql.h.pp (+5/-0)
include/mysql_com.h (+1/-0)
include/sql_common.h (+1/-0)
libmysql/CMakeLists.txt (+2/-0)
libmysql/client_settings.h (+1/-0)
libmysql/libmysql.c (+266/-30)
libmysql/libmysql.def (+2/-0)
libmysqld/lib_sql.cc (+8/-0)
mysql-test/mysql-test-run.pl (+15/-0)
mysql-test/r/stmt_execute_immediate.result (+64/-0)
mysql-test/t/stmt_execute_immediate.test (+7/-0)
sql-common/client.c (+1/-0)
sql/sql_parse.cc (+9/-0)
tests/CMakeLists.txt (+4/-0)
tests/stmt_execute_immediate.cc (+259/-0)
To merge this branch: bzr merge lp:~vlad-lesin/percona-server/5.6-binary-answer
Reviewer Review Type Date Requested Status
Laurynas Biveinis (community) 2014-07-08 Needs Fixing on 2014-07-15
Review via email: mp+225987@code.launchpad.net

Description of the change

This branch is not supposed to be merged to PS5.6, all changes should be applied to the custom tree. But the fixes where ported to PS5.6 for comfortable testing and code review.

Testing on 5.6: http://jenkins.percona.com/view/PS%205.6/job/percona-server-5.6-param/636

To post a comment you must log in.

    - This appears to implement
      http://dev.mysql.com/worklog/task/?id=4627. Let's reference it
      in the blueprint. Is it a good idea to take their names
      COM_STMT_EXECUTE_IMMEDIATE and mysql_stmt_execute_immediate()?
    - The feature breaks forward client/server compatibility with
      Oracle. Is that why we are not merging this to PS 5.6 trunk?
    - The test program should exercise the binary protocol itself
      more, it's possible some corner case might fall out. Looking at
      the protocol description that entails testing the following:
      NULLs in result, and types listed in
      http://dev.mysql.com/doc/internals/en/binary-protocol-value.html
    - Another way to have good test coverage for the feature would be
      to patch run_query in mysqltest.cc, which happens to do
      prepare+execute in binary protocol, by this feature, and run
      testsuite with --ps-protocol, and watch for fallout. I am not
      sure whether this should be done as a one-time thing for
      testing, whether --ps-protocol should always do the binary query
      answer, or a a new option like --ps-protocol should be added.
    - On Mac OS X, the testcase fails with

--- /Users/laurynas/percona/lp-mysql-server/5.6-binary-answer/mysql-test/r/stmt_query.result 2014-07-15 14:41:41.000000000 +0300
+++ /Users/laurynas/percona/lp-mysql-server/5.6-binary-answer/obj-debug/mysql-test/var/log/stmt_query.reject 2014-07-15 15:12:00.000000000 +0300
@@ -24,5 +24,5 @@
 binary: 3, Volkswagen, 21600

 Test for error:
-Errno: 1146, error text: Table 'test.Bars' doesn't exist
+Errno: 1146, error text: Table 'test.bars' doesn't exist
 Errno: 1064, error text: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELEC * FROM Bars' at line 1

    This is probably because

2014-07-15 15:11:56 62141 [Warning] Setting lower_case_table_names=2 because file system for /var/folders/sj/tww0v3g14tncqd9lsc81vqj80000gn/T/kL5oE2Hwjx/ is case insensitive

    - Not sure if I like COM_STMT_QUERY replacement with COM_QUERY in
      dispatch_command. On one hand, COM_STMT_QUERY is like COM_QUERY,
      onn another, like COM_EXECUTE, so it's not trivially obivous
      that all COM_QUERY checks automatically apply to COM_STMT_QUERY
      too. I'd rather leave the original command and add || ==
      COM_STMT_QUERY as necessary.

    Minor comments:
    - COM_STMT_QUERY in mysql_com.h should go above the "don't forget
      to update ... " comment.
    - args aligment in mysql_stmt_query_bind_result declaration (diff
      lines 16+ and 46+)
    - init_stmt_fields, cli_stmt_query, reinit_stmt_fields,
      mysql_stmt_query, mysql_stmt_query need header comments
    - spurious diff line 312
    - Was adding of emb_stmt_query necessary?
    - Why patching of mysql_stmt_bind_result was required?
    - diff line 472: bool x = (y == z) ? true : false is simply x = (y
      == z)

review: Needs Fixing
Vlad Lesin (vlad-lesin) wrote :
Download full text (5.4 KiB)

> - This appears to implement
> http://dev.mysql.com/worklog/task/?id=4627. Let's reference it
> in the blueprint. Is it a good idea to take their names
> COM_STMT_EXECUTE_IMMEDIATE and mysql_stmt_execute_immediate()?
Yes, this is a good naming. I will do renaming. This WL is only for one new api function but one more function is necessary for binding results as standard mysql_stmt_bind_result() requires some fields of MYSQL_STMT object to be initialized, and on normal workflow they are initialized inside of mysql_stmt_prepare().

> - The feature breaks forward client/server compatibility with
> Oracle. Is that why we are not merging this to PS 5.6 trunk?
If you mean that new command will not be supported in new mysql versions then yes, you are right. But as we discussed there are many such things which lead to forward incompatibility PS5.6 with Oracle Mysql.

> - The test program should exercise the binary protocol itself
> more, it's possible some corner case might fall out. Looking at
> the protocol description that entails testing the following:
> NULLs in result, and types listed in
> http://dev.mysql.com/doc/internals/en/binary-protocol-value.html
I don't think this is really necessary as there is no new code which would parse the protocol from client-side, absolutely the same functions are used for parsing answer from both "prepared" and "immediate" queries. And the same is true for server-side. And if there are any errors in binary protocol they will show itself during "prepare/execute" testing.

> - Another way to have good test coverage for the feature would be
> to patch run_query in mysqltest.cc, which happens to do
> prepare+execute in binary protocol, by this feature, and run
> testsuite with --ps-protocol, and watch for fallout. I am not
> sure whether this should be done as a one-time thing for
> testing, whether --ps-protocol should always do the binary query
> answer, or a a new option like --ps-protocol should be added.
Hey, thanks, great idea. I have implemented new --binary-protocol option. I will launch two jenkins jobs. One will be with --ps-protocol and another with --binary-protocol and then we will compare results. But again, this approach just tests some new sequence of C api calls for parsing binary answer. The rest of test is absolutely the same as for "prepare-execute" pair.

> - On Mac OS X, the testcase fails with
>
> --- /Users/laurynas/percona/lp-mysql-server/5.6-binary-answer/mysql-
> test/r/stmt_query.result 2014-07-15 14:41:41.000000000 +0300
> +++ /Users/laurynas/percona/lp-mysql-server/5.6-binary-answer/obj-debug/mysql-
> test/var/log/stmt_query.reject 2014-07-15 15:12:00.000000000 +0300
> @@ -24,5 +24,5 @@
> binary: 3, Volkswagen, 21600
>
> Test for error:
> -Errno: 1146, error text: Table 'test.Bars' doesn't exist
> +Errno: 1146, error text: Table 'test.bars' doesn't exist
> Errno: 1064, error text: You have an error in your SQL syntax; check the
> manual that corresponds to your MySQL server version for the right syntax to
> use near 'SELEC * FROM Bars' at line 1
>
> This is probab...

Read more...

Vlad Lesin (vlad-lesin) wrote :

The new results of testing are here:
with --ps-protocol
http://jenkins.percona.com/view/PS 5.6/job/percona-server-5.6-param/685
with --binary-protocol
http://jenkins.percona.com/view/PS 5.6/job/percona-server-5.6-param/686

The tests are passed for --ps-protocol and not passed for --binary-protocol are the following:

main.kill
main.wait_timeout
main.parser
main.failed_auth_3909
main.mysql_binary_mode
main.query_cache_ps_no_prot
perfschema.event_aggregate
perfschema.event_aggregate_no_a
perfschema.event_aggregate_no_a_no_h
perfschema.event_aggregate_no_a_no_u
perfschema.event_aggregate_no_a_no_u_no_h
perfschema.event_aggregate_no_h
perfschema.event_aggregate_no_u
perfschema.event_aggregate_no_u_no_h
perfschema.nesting

Vlad Lesin (vlad-lesin) wrote :
Download full text (5.7 KiB)

The "reconnect" bug.

If MYSQL::reconnect flag is set reconnect does not happen on mysql_stmt_execute_immediate() call. There are several failed test connected with this bug. For example: main.kill, main.wait_timeout.

If MYSQL::reconnect flag is set and mysql_stmt_execute_immediate() is invoked the reconnection should be inside of cli_advanced_command() after net_write_command() returns error. But net_write_command() does not return error as send() function does not return error in inline_mysql_socket_send() too in the case if connection was closed by peer. As the data size to send is quite small send() just buffers the data. Here is the stacktrace:

(gdb) bt
#0 __libc_send (fd=7, buf=0x9f9ae0, n=13, flags=0)
    at ../sysdeps/unix/sysv/linux/x86_64/send.c:26
#1 0x0000000000438c6c in inline_mysql_socket_send (
    src_file=0x4c9e10 "./vio/viosocket.c", src_line=224, mysql_socket=..., buf=0x9f9ae0, n=13, flags=0)
    at ./include/mysql/psi/mysql_socket.h:759
#2 0x0000000000439597 in vio_write (vio=0x9f58d0, buf=0x9f9ae0 "\t", size=13)
    at ./vio/viosocket.c:224
#3 0x000000000041ea38 in net_write_raw_loop (net=0x7fffffff98c8,
    buf=0x9f9ae0 "\t", count=13)
    at ./sql/net_serv.cc:500
#4 0x000000000041ed0d in net_write_packet (net=0x7fffffff98c8,
    packet=0x9f9ae0 "\t", length=13)
    at ./sql/net_serv.cc:636
#5 0x000000000041e31f in net_flush (net=0x7fffffff98c8)
    at ./sql/net_serv.cc:220
#6 0x000000000041e7d9 in net_write_command (net=0x7fffffff98c8,
    command=31 '\037', header=0x0, head_len=0, packet=0x4c3eaf "SELECT 1",
---Type <return> to continue, or q <return> to quit---
    len=8)
    at ./sql/net_serv.cc:399
#7 0x0000000000413a08 in cli_advanced_command (mysql=0x7fffffff98c8,
    command=COM_STMT_EXECUTE_IMMEDIATE, header=0x0, header_length=0,
    arg=0x4c3eaf "SELECT 1", arg_length=8, skip_check=1 '\001', stmt=0x9fbb80)
    at ./sql-common/client.c:727
#8 0x000000000040914d in cli_stmt_execute_immediate (stmt=0x9fbb80,
    query=0x4c3eaf "SELECT 1", len=8)
    at ./libmysql/libmysql.c:2224
#9 0x0000000000409ba4 in mysql_stmt_execute_immediate (stmt=0x9fbb80,
    q=0x4c3eaf "SELECT 1")
    at ./libmysql/libmysql.c:2716
#10 0x0000000000404cfd in main (argc=2, argv=0x7fffffffa038)
    at ./tests/stmt_execute_immediate.cc:276

And the fact the connection was closed by peer is determined during the step of receiving data from server in cli_read_query_result(). But in this case reconnection does not happen.

Why does the reconnection happen if mysql_stmt_prepare() is used? Because send() is invoked twice in this case. The first time is for COM_STMT_CLEAR command and the second time is for COM_STMT_PREPARE. The first send() buffers the data and does not return error while the second call returns error. Here are the stacktraces:

(gdb) bt
#0 __libc_send (fd=7, buf=0x9f9ae0, n=9, flags=0)
    at ../sysdeps/unix/sysv/linux/x86_64/send.c:26
#1 0x0000000000438c99 in inline_mysql_socket_send (
    src_file=0x4c9e30 "./vio/viosocket.c", src_line=224, mysql_socket=..., buf=0x9f9ae0, n=9, flags=0)
    at ./include/mysql/psi/mysql_socket.h:759
#2 0x00000000004395c4 in vio_write (vio=0x9f58d0, buf=0x9f9ae0 "\005", siz...

Read more...

619. By Vlad Lesin on 2014-09-09

Implement this
https://blueprints.launchpad.net/percona-server/+spec/binary-answer-5.6
blueprint.

The task can be splitted on server and client parts.

Server-side.

If we look into mysqld_stmt_prepare() function we can see the code like this:

Protocol *save_protocol= thd->protocol;
thd->protocol= &thd->protocol_binary;
stmt->prepare(...);
thd->protocol= save_protocol;

The THD class contains this members:

Protocol *protocol; // Current protocol
Protocol_text protocol_text; // Normal protocol
Protocol_binary protocol_binary; // Binary protocol

So for server-side is enough to add new command COM_STMT_EXECUTE_IMMEDIATE which
would do absolutely the same things as COM_QUERY does but with replacing
thd->protocol with &thd->protocol_binary before processing and restore protocol
pointer after processing.

Client side.

Standard C API has already have all necessary infrastructure to parse binary
protocol. So the task is to use this infrastructure. The idea is to add new
function mysql_stmt_execute_immediate() which would send
COM_STMT_EXECUTE_IMMEDIATE to server, initialize MYSQL_STMT object to use it
for parsing result with standard functions for parsing the result of
"execute prepared statement". As well mysql_stmt_bind_immediate() is necessary
to initialize certain MYSQL_STMT fields which are usually initialized inside of
mysql_stmt_prepare() and to invoke standard mysql_stmt_bind_result() function
to describe the result data.

There are two functions to execute prepared and non-prepared queries
correspondingly: mysql_stmt_execute() and mysql_real_query(). Both functions
use the same function cli_read_query_result() to get information about fields
count, their names. So this part of result parsing is the same. But rows parsing
is absolutely different.

For non-prepared query mysql_fetch_row()->read_one_row() calls just set pointers
in array of rows to the certain values in read packet. As all values are in the
text format we have the array of pointers to null-terminated strings as the
output of the function.

But when binary data are parsed in mysql_stmt_fetch()->stmt_fetch_row() the
functions use information about field types from MYSQL_STMT::bind array.
For each element of this array
mysql_stmt_bind_result()->setup_one_fetch_function() sets the pointer to the
function which parses binary data of corresponding type to the buffer. For
example fetch_result_int32() is set up for MYSQL_TYPE_LONG data type.

So we have to develop new function mysql_stmt_execute_immediate() which would
mix mysql_real_query() and mysql_stmt_execute().

Use tests/stmt_execute_immediate.cc as the source of information about the
sequence of C API calls to send query and receive and parse binary answer.

stmt_execute_immediate.cc could be the part of mysql_client_test.c but the
customer disabled this test in his tree.

The new parameter --binary-protocol is added to mysqltest to allow communication
between client and server with binary protocol without "prepare" using
mysql_stmt_execute_immedate() function to send query.

Vlad Lesin (vlad-lesin) wrote :

> The "reconnect" bug.

The issue was in this code:

my_bool
cli_advanced_command(MYSQL *mysql, enum enum_server_command command,
       const uchar *header, ulong header_length,
       const uchar *arg, ulong arg_length, my_bool skip_check,
                     MYSQL_STMT *stmt)
{
...
  my_bool stmt_skip= stmt ? stmt->state != MYSQL_STMT_INIT_DONE : FALSE;
...
  if (mysql->net.vio == 0)
  { /* Do reconnect if possible */
    if (mysql_reconnect(mysql) || stmt_skip)
      DBUG_RETURN(1);
  }
...
}

As stmt->state was set to MYSQL_STMT_PREPARE_DONE value in mysql_stmt_execute_immediate(), the code did reconnect but did not send commend after the reconnect.

The fix is skipping stmt argument of cli_advanced_command() when it is invoked from cli_stmt_execute_immediate.

As well there was error in mysql_stmt_close(). The function used to send COM_STMT_SEND command to server, and as there is no valid statement ID for queries sent with mysql_stmt_execute_immediate(), the server returned error. The bug was fixed with checking statement ID for "undefined" value which is 0 as statement ID's begin with 1 (see THD::statement_id_counter, THD and Prepared_statement constructors). The "udefined" statement ID is set in mysql_stmt_execute_immediate().

After fixing these two bugs the number of failed tests ran with '--binary-protocol' option decreased dramatically. Here is the list:

    main.mysql_binary_mode
    main.parser
    main.query_cache_ps_no_prot
    perfschema.nesting
    rpl.rpl_err_ignoredtable 'row'

(http://jenkins.percona.com/view/PS%205.6/job/percona-server-5.6-param/709)

Unmerged revisions

619. By Vlad Lesin on 2014-09-09

Implement this
https://blueprints.launchpad.net/percona-server/+spec/binary-answer-5.6
blueprint.

The task can be splitted on server and client parts.

Server-side.

If we look into mysqld_stmt_prepare() function we can see the code like this:

Protocol *save_protocol= thd->protocol;
thd->protocol= &thd->protocol_binary;
stmt->prepare(...);
thd->protocol= save_protocol;

The THD class contains this members:

Protocol *protocol; // Current protocol
Protocol_text protocol_text; // Normal protocol
Protocol_binary protocol_binary; // Binary protocol

So for server-side is enough to add new command COM_STMT_EXECUTE_IMMEDIATE which
would do absolutely the same things as COM_QUERY does but with replacing
thd->protocol with &thd->protocol_binary before processing and restore protocol
pointer after processing.

Client side.

Standard C API has already have all necessary infrastructure to parse binary
protocol. So the task is to use this infrastructure. The idea is to add new
function mysql_stmt_execute_immediate() which would send
COM_STMT_EXECUTE_IMMEDIATE to server, initialize MYSQL_STMT object to use it
for parsing result with standard functions for parsing the result of
"execute prepared statement". As well mysql_stmt_bind_immediate() is necessary
to initialize certain MYSQL_STMT fields which are usually initialized inside of
mysql_stmt_prepare() and to invoke standard mysql_stmt_bind_result() function
to describe the result data.

There are two functions to execute prepared and non-prepared queries
correspondingly: mysql_stmt_execute() and mysql_real_query(). Both functions
use the same function cli_read_query_result() to get information about fields
count, their names. So this part of result parsing is the same. But rows parsing
is absolutely different.

For non-prepared query mysql_fetch_row()->read_one_row() calls just set pointers
in array of rows to the certain values in read packet. As all values are in the
text format we have the array of pointers to null-terminated strings as the
output of the function.

But when binary data are parsed in mysql_stmt_fetch()->stmt_fetch_row() the
functions use information about field types from MYSQL_STMT::bind array.
For each element of this array
mysql_stmt_bind_result()->setup_one_fetch_function() sets the pointer to the
function which parses binary data of corresponding type to the buffer. For
example fetch_result_int32() is set up for MYSQL_TYPE_LONG data type.

So we have to develop new function mysql_stmt_execute_immediate() which would
mix mysql_real_query() and mysql_stmt_execute().

Use tests/stmt_execute_immediate.cc as the source of information about the
sequence of C API calls to send query and receive and parse binary answer.

stmt_execute_immediate.cc could be the part of mysql_client_test.c but the
customer disabled this test in his tree.

The new parameter --binary-protocol is added to mysqltest to allow communication
between client and server with binary protocol without "prepare" using
mysql_stmt_execute_immedate() function to send query.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'client/mysqltest.cc'
2--- client/mysqltest.cc 2013-10-15 11:37:57 +0000
3+++ client/mysqltest.cc 2014-09-09 12:25:45 +0000
4@@ -87,7 +87,8 @@
5
6 enum {
7 OPT_PS_PROTOCOL=OPT_MAX_CLIENT_OPTION, OPT_SP_PROTOCOL,
8- OPT_CURSOR_PROTOCOL, OPT_VIEW_PROTOCOL, OPT_MAX_CONNECT_RETRIES,
9+ OPT_CURSOR_PROTOCOL, OPT_BINARY_PROTOCOL,
10+ OPT_VIEW_PROTOCOL, OPT_MAX_CONNECT_RETRIES,
11 OPT_MAX_CONNECTIONS, OPT_MARK_PROGRESS, OPT_LOG_DIR,
12 OPT_TAIL_LINES, OPT_RESULT_FORMAT_VERSION, OPT_TRACE_PROTOCOL,
13 OPT_EXPLAIN_PROTOCOL, OPT_JSON_EXPLAIN_PROTOCOL
14@@ -114,6 +115,7 @@
15 static my_bool explain_protocol= 0, explain_protocol_enabled= 0;
16 static my_bool json_explain_protocol= 0, json_explain_protocol_enabled= 0;
17 static my_bool cursor_protocol= 0, cursor_protocol_enabled= 0;
18+static my_bool binary_protocol= 0, binary_protocol_enabled= 0;;
19 static my_bool parsing_disabled= 0;
20 static my_bool display_result_vertically= FALSE, display_result_lower= FALSE,
21 display_metadata= FALSE, display_result_sorted= FALSE;
22@@ -6551,6 +6553,9 @@
23 {"cursor-protocol", OPT_CURSOR_PROTOCOL, "Use cursors for prepared statements.",
24 &cursor_protocol, &cursor_protocol, 0,
25 GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
26+ {"binary-protocol", OPT_BINARY_PROTOCOL, "Use binary protocol without prepare query.",
27+ &binary_protocol, &binary_protocol, 0,
28+ GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
29 {"database", 'D', "Database to use.", &opt_db, &opt_db, 0,
30 GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
31 #ifdef DBUG_OFF
32@@ -7717,7 +7722,8 @@
33 /*
34 Prepare the query
35 */
36- if (mysql_stmt_prepare(stmt, query, query_len))
37+
38+ if (!binary_protocol_enabled && mysql_stmt_prepare(stmt, query, query_len))
39 {
40 handle_error(command, mysql_stmt_errno(stmt),
41 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
42@@ -7728,7 +7734,7 @@
43 Get the warnings from mysql_stmt_prepare and keep them in a
44 separate string
45 */
46- if (!disable_warnings)
47+ if (!binary_protocol && !disable_warnings)
48 append_warnings(&ds_prepare_warnings, mysql);
49
50 /*
51@@ -7752,7 +7758,8 @@
52 /*
53 Execute the query
54 */
55- if (mysql_stmt_execute(stmt))
56+ if (binary_protocol_enabled ?
57+ mysql_stmt_execute_immediate(stmt, query) : mysql_stmt_execute(stmt))
58 {
59 handle_error(command, mysql_stmt_errno(stmt),
60 mysql_stmt_error(stmt), mysql_stmt_sqlstate(stmt), ds);
61@@ -8660,6 +8667,7 @@
62 var_set_int("$EXPLAIN_PROTOCOL", explain_protocol);
63 var_set_int("$JSON_EXPLAIN_PROTOCOL", json_explain_protocol);
64 var_set_int("$CURSOR_PROTOCOL", cursor_protocol);
65+ var_set_int("$BINARY_PROTOCOL", binary_protocol);
66
67 var_set_int("$ENABLED_QUERY_LOG", 1);
68 var_set_int("$ENABLED_ABORT_ON_ERROR", 1);
69@@ -8687,8 +8695,12 @@
70 var_set_string("MYSQLTEST_FILE", cur_file->file_name);
71 init_re();
72
73- /* Cursor protcol implies ps protocol */
74- if (cursor_protocol)
75+ if (cursor_protocol && binary_protocol)
76+ die("Currently binary protocol without prepare query does not support "
77+ "work with cursors, --cursor-protocol and --binary-protocol "
78+ "options are mutually exclusive.");
79+ /* Cursor and binary protcols implies ps protocol */
80+ if (cursor_protocol || binary_protocol)
81 ps_protocol= 1;
82
83 ps_protocol_enabled= ps_protocol;
84@@ -8698,6 +8710,7 @@
85 explain_protocol_enabled= explain_protocol;
86 json_explain_protocol_enabled= json_explain_protocol;
87 cursor_protocol_enabled= cursor_protocol;
88+ binary_protocol_enabled= binary_protocol;
89
90 st_connection *con= connections;
91 #ifdef EMBEDDED_LIBRARY
92
93=== modified file 'include/mysql.h'
94--- include/mysql.h 2013-05-12 06:24:46 +0000
95+++ include/mysql.h 2014-09-09 12:25:45 +0000
96@@ -674,6 +674,7 @@
97 int STDCALL mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
98 unsigned long length);
99 int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt);
100+int STDCALL mysql_stmt_execute_immediate(MYSQL_STMT *stmt, const char *q);
101 int STDCALL mysql_stmt_fetch(MYSQL_STMT *stmt);
102 int STDCALL mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind_arg,
103 unsigned int column,
104@@ -687,6 +688,9 @@
105 enum enum_stmt_attr_type attr_type,
106 void *attr);
107 my_bool STDCALL mysql_stmt_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bnd);
108+my_bool STDCALL mysql_stmt_bind_immediate(MYSQL_STMT * stmt,
109+ MYSQL_BIND * bnd,
110+ size_t fields_count);
111 my_bool STDCALL mysql_stmt_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bnd);
112 my_bool STDCALL mysql_stmt_close(MYSQL_STMT * stmt);
113 my_bool STDCALL mysql_stmt_reset(MYSQL_STMT * stmt);
114
115=== modified file 'include/mysql.h.pp'
116--- include/mysql.h.pp 2013-02-12 07:47:19 +0000
117+++ include/mysql.h.pp 2014-09-09 12:25:45 +0000
118@@ -12,6 +12,7 @@
119 COM_STMT_PREPARE, COM_STMT_EXECUTE, COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE,
120 COM_STMT_RESET, COM_SET_OPTION, COM_STMT_FETCH, COM_DAEMON,
121 COM_BINLOG_DUMP_GTID,
122+ COM_STMT_EXECUTE_IMMEDIATE,
123 COM_END
124 };
125 struct st_vio;
126@@ -569,6 +570,7 @@
127 int mysql_stmt_prepare(MYSQL_STMT *stmt, const char *query,
128 unsigned long length);
129 int mysql_stmt_execute(MYSQL_STMT *stmt);
130+int mysql_stmt_execute_immediate(MYSQL_STMT *stmt, const char *q);
131 int mysql_stmt_fetch(MYSQL_STMT *stmt);
132 int mysql_stmt_fetch_column(MYSQL_STMT *stmt, MYSQL_BIND *bind_arg,
133 unsigned int column,
134@@ -582,6 +584,9 @@
135 enum enum_stmt_attr_type attr_type,
136 void *attr);
137 my_bool mysql_stmt_bind_param(MYSQL_STMT * stmt, MYSQL_BIND * bnd);
138+my_bool mysql_stmt_bind_immediate(MYSQL_STMT * stmt,
139+ MYSQL_BIND * bnd,
140+ size_t fields_count);
141 my_bool mysql_stmt_bind_result(MYSQL_STMT * stmt, MYSQL_BIND * bnd);
142 my_bool mysql_stmt_close(MYSQL_STMT * stmt);
143 my_bool mysql_stmt_reset(MYSQL_STMT * stmt);
144
145=== modified file 'include/mysql_com.h'
146--- include/mysql_com.h 2014-03-26 10:11:16 +0000
147+++ include/mysql_com.h 2014-09-09 12:25:45 +0000
148@@ -74,6 +74,7 @@
149 COM_STMT_PREPARE, COM_STMT_EXECUTE, COM_STMT_SEND_LONG_DATA, COM_STMT_CLOSE,
150 COM_STMT_RESET, COM_SET_OPTION, COM_STMT_FETCH, COM_DAEMON,
151 COM_BINLOG_DUMP_GTID,
152+ COM_STMT_EXECUTE_IMMEDIATE,
153 /* don't forget to update const char *command_name[] in sql_parse.cc */
154
155 /* Must be last */
156
157=== modified file 'include/sql_common.h'
158--- include/sql_common.h 2013-08-14 03:57:21 +0000
159+++ include/sql_common.h 2014-09-09 12:25:45 +0000
160@@ -62,6 +62,7 @@
161 MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
162 my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
163 int (*stmt_execute)(MYSQL_STMT *stmt);
164+ int (*stmt_execute_immediate)(MYSQL_STMT *stmt, const char *query, size_t len);
165 int (*read_binary_rows)(MYSQL_STMT *stmt);
166 int (*unbuffered_fetch)(MYSQL *mysql, char **row);
167 void (*free_embedded_thd)(MYSQL *mysql);
168
169=== modified file 'libmysql/CMakeLists.txt'
170--- libmysql/CMakeLists.txt 2014-08-22 10:02:24 +0000
171+++ libmysql/CMakeLists.txt 2014-09-09 12:25:45 +0000
172@@ -34,6 +34,7 @@
173 mysql_affected_rows
174 mysql_autocommit
175 mysql_stmt_bind_param
176+mysql_stmt_bind_immediate
177 mysql_stmt_bind_result
178 mysql_change_user
179 mysql_character_set_name
180@@ -48,6 +49,7 @@
181 mysql_escape_string
182 mysql_hex_string
183 mysql_stmt_execute
184+mysql_stmt_execute_immediate
185 mysql_stmt_fetch
186 mysql_stmt_fetch_column
187 mysql_fetch_field
188
189=== modified file 'libmysql/client_settings.h'
190--- libmysql/client_settings.h 2013-06-20 15:16:00 +0000
191+++ libmysql/client_settings.h 2014-09-09 12:25:45 +0000
192@@ -57,6 +57,7 @@
193 MYSQL_DATA * cli_read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
194 uint fields);
195 int cli_stmt_execute(MYSQL_STMT *stmt);
196+int cli_stmt_execute_immediate(MYSQL_STMT *stmt, const char *query, size_t len);
197 int cli_read_binary_rows(MYSQL_STMT *stmt);
198 int cli_unbuffered_fetch(MYSQL *mysql, char **row);
199 const char * cli_read_statistics(MYSQL *mysql);
200
201=== modified file 'libmysql/libmysql.c'
202--- libmysql/libmysql.c 2014-08-22 13:31:49 +0000
203+++ libmysql/libmysql.c 2014-09-09 12:25:45 +0000
204@@ -75,6 +75,12 @@
205 #endif /* __WIN__ */
206
207 /*
208+ Statemet ids begin with 1
209+ (see THD::statement_id_counter, THD and Prepared_statement constructors)
210+*/
211+#define STMT_ID_UNDEFINED 0
212+
213+/*
214 If allowed through some configuration, then this needs to
215 be changed
216 */
217@@ -1658,37 +1664,15 @@
218 }
219
220 /*
221- Get result set metadata from reply to mysql_stmt_execute.
222- This is used mainly for SHOW commands, as metadata for these
223- commands is sent only with result set.
224- To be removed when all commands will fully support prepared mode.
225+ Initialize stmt->fields with data from stmt->mysql->fields.
226 */
227
228-static void alloc_stmt_fields(MYSQL_STMT *stmt)
229+static void init_stmt_fields(MYSQL_STMT *stmt)
230 {
231 MYSQL_FIELD *fields, *field, *end;
232 MEM_ROOT *fields_mem_root= &stmt->extension->fields_mem_root;
233 MYSQL *mysql= stmt->mysql;
234
235- DBUG_ASSERT(stmt->field_count);
236-
237- free_root(fields_mem_root, MYF(0));
238-
239- /*
240- Get the field information for non-select statements
241- like SHOW and DESCRIBE commands
242- */
243- if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(fields_mem_root,
244- sizeof(MYSQL_FIELD) *
245- stmt->field_count)) ||
246- !(stmt->bind= (MYSQL_BIND *) alloc_root(fields_mem_root,
247- sizeof(MYSQL_BIND) *
248- stmt->field_count)))
249- {
250- set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL);
251- return;
252- }
253-
254 for (fields= mysql->fields, end= fields+stmt->field_count,
255 field= stmt->fields;
256 field && fields < end; fields++, field++)
257@@ -1729,6 +1713,38 @@
258 }
259 }
260
261+/*
262+ Get result set metadata from reply to mysql_stmt_execute.
263+ This is used mainly for SHOW commands, as metadata for these
264+ commands is sent only with result set.
265+ To be removed when all commands will fully support prepared mode.
266+*/
267+
268+static void alloc_stmt_fields(MYSQL_STMT *stmt)
269+{
270+ MEM_ROOT *fields_mem_root= &stmt->extension->fields_mem_root;
271+
272+ DBUG_ASSERT(stmt->field_count);
273+
274+ free_root(fields_mem_root, MYF(0));
275+
276+ /*
277+ Get the field information for non-select statements
278+ like SHOW and DESCRIBE commands
279+ */
280+ if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(fields_mem_root,
281+ sizeof(MYSQL_FIELD) *
282+ stmt->field_count)) ||
283+ !(stmt->bind= (MYSQL_BIND *) alloc_root(fields_mem_root,
284+ sizeof(MYSQL_BIND) *
285+ stmt->field_count)))
286+ {
287+ set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL);
288+ return;
289+ }
290+
291+ init_stmt_fields(stmt);
292+}
293
294 /**
295 Update result set columns metadata if it was sent again in
296@@ -2190,6 +2206,59 @@
297 }
298
299 /*
300+ Send COM_STMT_EXECUTE_IMMEDIATE command with query string to server and
301+ read command execution result.
302+
303+ SYNOPSIS
304+ cli_stmt_execute_immediate()
305+ stmt statement handle
306+ query query string
307+ len query string len
308+
309+ RETURN VALUES
310+ 0 ok
311+ 1 error
312+*/
313+
314+int cli_stmt_execute_immediate(MYSQL_STMT *stmt, const char *query, size_t len) {
315+ MYSQL *mysql;
316+ NET *net;
317+ my_bool res;
318+ DBUG_ENTER("cli_stmt_execute_immediate");
319+ mysql= stmt->mysql;
320+ net= &mysql->net;
321+
322+ /*
323+ The last parameter of advanced_command() should be NULL to enable command
324+ resending after reconnect in cli_advanced_command()
325+ (see stmt_skip local variable).
326+ */
327+ res= MY_TEST((*mysql->methods->advanced_command)(mysql,
328+ COM_STMT_EXECUTE_IMMEDIATE,
329+ 0, 0,
330+ (uchar*)query, len,
331+ 1, NULL) ||
332+ (*mysql->methods->read_query_result)(mysql));
333+
334+ stmt->affected_rows= mysql->affected_rows;
335+ stmt->server_status= mysql->server_status;
336+ stmt->insert_id= mysql->insert_id;
337+ if (res)
338+ {
339+ /*
340+ Don't set stmt error if stmt->mysql is NULL, as the error in this case
341+ has already been set by mysql_prune_stmt_list().
342+ */
343+ if (stmt->mysql)
344+ set_stmt_errmsg(stmt, net);
345+ DBUG_RETURN(1);
346+ }
347+ else if (mysql->status == MYSQL_STATUS_GET_RESULT)
348+ stmt->mysql->status= MYSQL_STATUS_STATEMENT_GET_RESULT;
349+ DBUG_RETURN(0);
350+}
351+
352+/*
353 Read one row from buffered result set. Result set is created by prior
354 call to mysql_stmt_store_result().
355 SYNOPSIS
356@@ -2573,6 +2642,108 @@
357 DBUG_RETURN(MY_TEST(stmt->last_errno));
358 }
359
360+/**
361+ Reallocate and initialize stmt->fields with values from
362+ stmt->mysql->fields.
363+
364+ @arguments stmt - statement header
365+
366+ @retval TRUE error
367+ @retval FALSE success
368+*/
369+
370+static int reinit_stmt_fields(MYSQL_STMT *stmt)
371+{
372+ MEM_ROOT *fields_mem_root;
373+ stmt->field_count= stmt->mysql->field_count;
374+ fields_mem_root= &stmt->extension->fields_mem_root;
375+
376+ DBUG_ASSERT(stmt->field_count);
377+
378+ free_root(fields_mem_root, MYF(0));
379+
380+ if (!(stmt->fields= (MYSQL_FIELD *) alloc_root(fields_mem_root,
381+ sizeof(MYSQL_FIELD) *
382+ stmt->field_count)))
383+ {
384+ set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL);
385+ return 1;
386+ }
387+
388+ init_stmt_fields(stmt);
389+
390+ return 0;
391+}
392+
393+/*
394+ This is the analogue of mysql_stmt_execute() but allows to execute
395+ queries without "prepare" step. The difference with mysql_real_query is
396+ that the ansewer
397+
398+ SYNOPSIS
399+ mysql_stmt_execute_immediate()
400+ stmt statement handle. The handle must be created
401+ with mysql_stmt_init(). The result data can be
402+ bound to variables with mysql_stmt_bind_immediate() function
403+ after mysql_stmt_execute_immediate() call.
404+
405+ DESCRIPTION
406+ This function sends COM_STMT_EXECUTE_IMMEDIATE to server with query
407+ string. The query is executed on server and the result data is sent
408+ with binary protocol which can be then parsed with the same way as
409+ for mysql_stmt_execute() function. To bind result to variables use
410+ mysql_stmt_bind_immediate() instead of mysql_stmt_bind_result().
411+
412+ There is no possibility to use cursors with mysql_stmt_attr_set() as
413+ server does not support such feature.
414+
415+ As for the rest this function is like mysql_stmt_execute().
416+ See description of mysql_stmt_execute() for more details.
417+
418+ RETURN
419+ 0 success
420+ 1 error, message can be retrieved with mysql_stmt_error().
421+*/
422+
423+int STDCALL mysql_stmt_execute_immediate(MYSQL_STMT *stmt, const char *q)
424+{
425+ MYSQL *mysql;
426+ DBUG_ENTER("mysql_stmt_execute_immediate");
427+ mysql= stmt->mysql;
428+
429+ if (!mysql)
430+ {
431+ set_stmt_error(stmt, CR_SERVER_LOST, unknown_sqlstate, NULL);
432+ DBUG_RETURN(1);
433+ }
434+
435+ stmt->bind_param_done= stmt->bind_result_done= FALSE;
436+ stmt->param_count= stmt->field_count= 0;
437+ stmt->bind= stmt->params= NULL;
438+ /*
439+ Set statement id to "undefined" to disable sending COM_STMT_CLOSE
440+ in mysql_stmt_close().
441+ */
442+ stmt->stmt_id= STMT_ID_UNDEFINED;
443+ free_root(&stmt->mem_root, MYF(MY_KEEP_PREALLOC));
444+ free_root(&stmt->extension->fields_mem_root, MYF(0));
445+
446+ stmt->state= MYSQL_STMT_PREPARE_DONE;
447+ if (reset_stmt_handle(stmt, RESET_STORE_RESULT | RESET_CLEAR_ERROR))
448+ DBUG_RETURN(1);
449+
450+ if (mysql->methods->stmt_execute_immediate(stmt, q, strlen(q)))
451+ DBUG_RETURN(1);
452+
453+ stmt->state= MYSQL_STMT_EXECUTE_DONE;
454+ if (mysql->field_count)
455+ {
456+ reinit_stmt_fields(stmt);
457+ update_stmt_fields(stmt);
458+ prepare_to_fetch_result(stmt);
459+ }
460+ DBUG_RETURN(MY_TEST(stmt->last_errno));
461+};
462
463 /*
464 Return total parameters count in the statement
465@@ -2914,6 +3085,57 @@
466 DBUG_RETURN(0);
467 }
468
469+/*
470+ Setup the bind buffers for resultset processing for
471+ mysql_stmt_execute_immediate() call. This function must
472+ be invoken only after mysql_stmt_execute_immediate call.
473+
474+ SYNOPSIS
475+ mysql_stmt_bind_immediate()
476+ stmt statement handle. The handle must be created
477+ with mysql_stmt_init().
478+ my_bind the pointer to the array with bind information.
479+ fields_count the number of elements in my_bind array
480+
481+ DESCRIPTION
482+ This function is used to bind buffers for resultset processing in the
483+ pair with mysql_stmt_execute_immediate(). This is analogue of
484+ mysql_stmt_bind_result(). But mysql_stmt_bind_result() assumes some
485+ fields of MYSQL_BIND object are initialized in mysql_stmt_prepare().
486+ As "prepare" step is skipped for mysql_stmt_execute_immediate() those
487+ fields need to be initialized somewhere. mysql_stmt_bind_immediate()
488+ initializes those fields and invokes mysql_stmt_bind_result(). This
489+ function must be invoked only after mysql_stmt_execute_immediate call.
490+
491+ RETURN
492+ 0 success
493+ 1 error, message can be retrieved with mysql_stmt_error().
494+*/
495+
496+my_bool STDCALL mysql_stmt_bind_immediate(MYSQL_STMT *stmt,
497+ MYSQL_BIND *my_bind,
498+ size_t fields_count)
499+{
500+ DBUG_ENTER("mysql_stmt_bind_immediate");
501+ stmt->field_count = fields_count;
502+ stmt->param_count = 0;
503+ stmt->params = NULL;
504+ /*
505+ alloc_root will return valid address even in case when param_count
506+ and field_count are zero. Thus we should never rely on stmt->bind
507+ or stmt->params when checking for existence of placeholders or
508+ result set.
509+ */
510+ if (!(stmt->bind = (MYSQL_BIND *) alloc_root(&stmt->mem_root,
511+ sizeof(MYSQL_BIND)*
512+ stmt->field_count)))
513+ {
514+ set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL);
515+ DBUG_RETURN(1);
516+ }
517+
518+ DBUG_RETURN(mysql_stmt_bind_result(stmt, my_bind));
519+}
520
521 /********************************************************************
522 Long data implementation
523@@ -3983,6 +4205,8 @@
524 (uint) param->buffer_type));
525 DBUG_RETURN(TRUE);
526 }
527+ if (!field)
528+ DBUG_RETURN(FALSE);
529 if (! is_binary_compatible(param->buffer_type, field->type))
530 param->fetch_result= fetch_result_with_conversion;
531
532@@ -4096,7 +4320,8 @@
533 param++, field++)
534 {
535 DBUG_PRINT("info",("buffer_type: %u field_type: %u",
536- (uint) param->buffer_type, (uint) field->type));
537+ (uint) param->buffer_type,
538+ stmt->fields ? (uint) field->type : 0));
539 /*
540 Set param->is_null to point to a dummy variable if it's not set.
541 This is to make the execute code easier
542@@ -4113,12 +4338,12 @@
543 param->param_number= param_count++;
544 param->offset= 0;
545
546- if (setup_one_fetch_function(param, field))
547+ if (setup_one_fetch_function(param, stmt->fields ? field : 0))
548 {
549 strmov(stmt->sqlstate, unknown_sqlstate);
550 sprintf(stmt->last_error,
551 ER(stmt->last_errno= CR_UNSUPPORTED_PARAM_TYPE),
552- field->type, param_count);
553+ stmt->fields ? field->type : 0, param_count);
554 DBUG_RETURN(1);
555 }
556 }
557@@ -4483,6 +4708,14 @@
558 */
559 MYSQL_BIND *my_bind, *end;
560 MYSQL_FIELD *field;
561+ if (!stmt->bind &&
562+ !(stmt->bind = (MYSQL_BIND *) alloc_root(&stmt->mem_root,
563+ sizeof(MYSQL_BIND)*
564+ stmt->field_count)))
565+ {
566+ set_stmt_error(stmt, CR_OUT_OF_MEMORY, unknown_sqlstate, NULL);
567+ DBUG_RETURN(1);
568+ }
569 memset(stmt->bind, 0, sizeof(*stmt->bind) * stmt->field_count);
570
571 for (my_bind= stmt->bind, end= my_bind + stmt->field_count,
572@@ -4725,10 +4958,13 @@
573 *mysql->unbuffered_fetch_owner= TRUE;
574 mysql->status= MYSQL_STATUS_READY;
575 }
576- int4store(buff, stmt->stmt_id);
577- if ((rc= stmt_command(mysql, COM_STMT_CLOSE, buff, 4, stmt)))
578+ if (stmt->stmt_id != STMT_ID_UNDEFINED)
579 {
580- set_stmt_errmsg(stmt, &mysql->net);
581+ int4store(buff, stmt->stmt_id);
582+ if ((rc= stmt_command(mysql, COM_STMT_CLOSE, buff, 4, stmt)))
583+ {
584+ set_stmt_errmsg(stmt, &mysql->net);
585+ }
586 }
587 }
588 }
589
590=== modified file 'libmysql/libmysql.def'
591--- libmysql/libmysql.def 2013-08-13 21:11:09 +0000
592+++ libmysql/libmysql.def 2014-09-09 12:25:45 +0000
593@@ -9,6 +9,7 @@
594 mysql_autocommit
595 mysql_stmt_bind_param
596 mysql_stmt_bind_result
597+ mysql_stmt_bind_immediate
598 mysql_change_user
599 mysql_character_set_name
600 mysql_close
601@@ -22,6 +23,7 @@
602 mysql_escape_string
603 mysql_hex_string
604 mysql_stmt_execute
605+ mysql_stmt_execute_immediate
606 mysql_stmt_fetch
607 mysql_stmt_fetch_column
608 mysql_fetch_field
609
610=== modified file 'libmysqld/lib_sql.cc'
611--- libmysqld/lib_sql.cc 2014-08-22 10:02:24 +0000
612+++ libmysqld/lib_sql.cc 2014-09-09 12:25:45 +0000
613@@ -354,6 +354,13 @@
614 DBUG_RETURN(0);
615 }
616
617+static int emb_stmt_execute_immediate(MYSQL_STMT *stmt, const char *, size_t)
618+{
619+ DBUG_ENTER("emb_stmt_execute_immediate");
620+ set_stmt_error(stmt, CR_NOT_IMPLEMENTED, unknown_sqlstate, NULL);
621+ DBUG_RETURN(1);
622+}
623+
624 int emb_read_binary_rows(MYSQL_STMT *stmt)
625 {
626 MYSQL_DATA *data;
627@@ -461,6 +468,7 @@
628 emb_list_fields,
629 emb_read_prepare_result,
630 emb_stmt_execute,
631+ emb_stmt_execute_immediate,
632 emb_read_binary_rows,
633 emb_unbuffered_fetch,
634 emb_free_embedded_thd,
635
636=== modified file 'mysql-test/mysql-test-run.pl'
637--- mysql-test/mysql-test-run.pl 2014-08-22 13:31:49 +0000
638+++ mysql-test/mysql-test-run.pl 2014-09-09 12:25:45 +0000
639@@ -192,6 +192,7 @@
640 my $opt_ps_protocol;
641 my $opt_sp_protocol;
642 my $opt_cursor_protocol;
643+my $opt_binary_protocol;
644 my $opt_view_protocol;
645 my $opt_trace_protocol;
646 my $opt_explain_protocol;
647@@ -990,6 +991,7 @@
648 resfile_global("sp-protocol", $opt_sp_protocol ? 1 : 0);
649 resfile_global("view-protocol", $opt_view_protocol ? 1 : 0);
650 resfile_global("cursor-protocol", $opt_cursor_protocol ? 1 : 0);
651+ resfile_global("binary-protocol", $opt_binary_protocol ? 1 : 0);
652 resfile_global("ssl", $opt_ssl ? 1 : 0);
653 resfile_global("compress", $opt_compress ? 1 : 0);
654 resfile_global("parallel", $opt_parallel);
655@@ -1043,6 +1045,7 @@
656 'explain-protocol' => \$opt_explain_protocol,
657 'json-explain-protocol' => \$opt_json_explain_protocol,
658 'cursor-protocol' => \$opt_cursor_protocol,
659+ 'binary-protocol' => \$opt_binary_protocol,
660 'ssl|with-openssl' => \$opt_ssl,
661 'skip-ssl' => \$opt_skip_ssl,
662 'compress' => \$opt_compress,
663@@ -2492,6 +2495,11 @@
664 "$basedir/tests/bug25714");
665 $ENV{'MYSQL_BUG25714'}= native_path($exe_bug25714);
666
667+ my $exe_stmt_execute_immediate=
668+ mtr_exe_maybe_exists(vs_config_dirs('tests', 'stmt_execute_immediate'),
669+ "$basedir/tests/stmt_execute_immediate");
670+ $ENV{'MYSQL_STMT_EXECUTE_IMMEDIATE'}= native_path($exe_stmt_execute_immediate);
671+
672 # ----------------------------------------------------
673 # mysql_fix_privilege_tables.sql
674 # ----------------------------------------------------
675@@ -5927,6 +5935,10 @@
676 mtr_add_arg($args, "--cursor-protocol");
677 }
678
679+ if ( $opt_binary_protocol )
680+ {
681+ mtr_add_arg($args, "--binary-protocol");
682+ }
683
684 mtr_add_arg($args, "--timer-file=%s/log/timer", $opt_vardir);
685
686@@ -6478,6 +6490,9 @@
687 ps-protocol Use the binary protocol between client and server
688 cursor-protocol Use the cursor protocol between client and server
689 (implies --ps-protocol)
690+ binary-protocol Use the binary protocol between client and server
691+ without prepare stage (mysql_stmt_execute_immediate())
692+ (implies --ps-protocol)
693 view-protocol Create a view to execute all non updating queries
694 opt-trace-protocol Print optimizer trace
695 explain-protocol Run 'EXPLAIN EXTENDED' on all SELECT, INSERT,
696
697=== added file 'mysql-test/r/stmt_execute_immediate.result'
698--- mysql-test/r/stmt_execute_immediate.result 1970-01-01 00:00:00 +0000
699+++ mysql-test/r/stmt_execute_immediate.result 2014-09-09 12:25:45 +0000
700@@ -0,0 +1,64 @@
701+Testing drop, create, insert:
702+Insert id: 3, affected rows: 1
703+
704+Test for select with result storing:
705+Iteration number 0
706+Rows count: 0, fields count: 3
707+binary: 1, Audi, 52642
708+Iteration number 1
709+Rows count: 0, fields count: 3
710+binary: 1, Audi, 52642
711+binary: 2, Hummer, 41400
712+Iteration number 2
713+Rows count: 0, fields count: 3
714+binary: 1, Audi, 52642
715+binary: 2, Hummer, 41400
716+binary: 3, Volkswagen, 21600
717+
718+Test for multiple results with result storing:
719+Rows count: 0, fields count: 3
720+binary: 1, Audi, 52642
721+Rows count: 0, fields count: 3
722+binary: 2, Hummer, 41400
723+Rows count: 0, fields count: 3
724+binary: 3, Volkswagen, 21600
725+
726+Test for select without result storing:
727+Iteration number 0
728+Rows count: 1, fields count: 3
729+binary: 1, Audi, 52642
730+Iteration number 1
731+Rows count: 2, fields count: 3
732+binary: 1, Audi, 52642
733+binary: 2, Hummer, 41400
734+Iteration number 2
735+Rows count: 3, fields count: 3
736+binary: 1, Audi, 52642
737+binary: 2, Hummer, 41400
738+binary: 3, Volkswagen, 21600
739+
740+Test for multiple results without result storing:
741+Rows count: 1, fields count: 3
742+binary: 1, Audi, 52642
743+Rows count: 1, fields count: 3
744+binary: 2, Hummer, 41400
745+Rows count: 1, fields count: 3
746+binary: 3, Volkswagen, 21600
747+
748+Test for select if the number and the type of fields are unknown before query execution:
749+bind col[0]: buffer_type: 254, buffer_length: 12
750+bind col[1]: buffer_type: 254, buffer_length: 11
751+bind col[2]: buffer_type: 254, buffer_length: 12
752+fetch col[0]: buffer_type: 254, buffer_length: 12, buffer: 1
753+fetch col[1]: buffer_type: 254, buffer_length: 11, buffer: Audi
754+fetch col[2]: buffer_type: 254, buffer_length: 12, buffer: 52642
755+fetch col[0]: buffer_type: 254, buffer_length: 12, buffer: 2
756+fetch col[1]: buffer_type: 254, buffer_length: 11, buffer: Hummer
757+fetch col[2]: buffer_type: 254, buffer_length: 12, buffer: 41400
758+fetch col[0]: buffer_type: 254, buffer_length: 12, buffer: 3
759+fetch col[1]: buffer_type: 254, buffer_length: 11, buffer: Volkswagen
760+fetch col[2]: buffer_type: 254, buffer_length: 12, buffer: 21600
761+
762+Test for error:
763+Errno: 1146, error text: Table 'test.bars' doesn't exist
764+Errno: 1064, error text: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'SELEC * FROM bars' at line 1
765
766=== added file 'mysql-test/t/stmt_execute_immediate.test'
767--- mysql-test/t/stmt_execute_immediate.test 1970-01-01 00:00:00 +0000
768+++ mysql-test/t/stmt_execute_immediate.test 2014-09-09 12:25:45 +0000
769@@ -0,0 +1,7 @@
770+ # Check that path to the specific test program has been setup
771+if (!$MYSQL_STMT_EXECUTE_IMMEDIATE)
772+{
773+ skip Need stmt_execute_immediate test program;
774+}
775+
776+--exec $MYSQL_STMT_EXECUTE_IMMEDIATE $MASTER_MYPORT
777
778=== modified file 'sql-common/client.c'
779--- sql-common/client.c 2014-08-22 13:31:49 +0000
780+++ sql-common/client.c 2014-09-09 12:25:45 +0000
781@@ -1916,6 +1916,7 @@
782 ,cli_list_fields, /* list_fields */
783 cli_read_prepare_result, /* read_prepare_result */
784 cli_stmt_execute, /* stmt_execute */
785+ cli_stmt_execute_immediate, /* stmt_execute_immediate */
786 cli_read_binary_rows, /* read_binary_rows */
787 cli_unbuffered_fetch, /* unbuffered_fetch */
788 NULL, /* free_embedded_thd */
789
790=== modified file 'sql/sql_parse.cc'
791--- sql/sql_parse.cc 2014-08-22 10:02:24 +0000
792+++ sql/sql_parse.cc 2014-09-09 12:25:45 +0000
793@@ -168,6 +168,7 @@
794 { C_STRING_WITH_LEN("Fetch") },
795 { C_STRING_WITH_LEN("Daemon") },
796 { C_STRING_WITH_LEN("Binlog Dump GTID") },
797+ { C_STRING_WITH_LEN("Query With Binary Answer") },
798 { C_STRING_WITH_LEN("Error") } // Last command number
799 };
800
801@@ -1226,6 +1227,9 @@
802 bool dispatch_command(enum enum_server_command command, THD *thd,
803 char* packet, uint packet_length)
804 {
805+ bool query_statement = (command == COM_STMT_EXECUTE_IMMEDIATE);
806+ if (command == COM_STMT_EXECUTE_IMMEDIATE)
807+ command = COM_QUERY;
808 NET *net= &thd->net;
809 bool error= 0;
810 DBUG_ENTER("dispatch_command");
811@@ -1429,6 +1433,10 @@
812 if (parser_state.init(thd, thd->query(), thd->query_length()))
813 break;
814
815+ Protocol *save_protocol= thd->protocol;
816+ if (query_statement)
817+ thd->protocol= &thd->protocol_binary;
818+
819 mysql_parse(thd, thd->query(), thd->query_length(), &parser_state);
820
821 while (!thd->killed && (parser_state.m_lip.found_semicolon != NULL) &&
822@@ -1507,6 +1515,7 @@
823 mysql_parse(thd, beginning_of_next_stmt, length, &parser_state);
824 }
825
826+ thd->protocol = save_protocol;
827 DBUG_PRINT("info",("query ready"));
828 break;
829 }
830
831=== modified file 'tests/CMakeLists.txt'
832--- tests/CMakeLists.txt 2013-10-08 06:03:07 +0000
833+++ tests/CMakeLists.txt 2014-09-09 12:25:45 +0000
834@@ -25,6 +25,10 @@
835 ADD_EXECUTABLE(bug25714 bug25714.c)
836 TARGET_LINK_LIBRARIES(bug25714 perconaserverclient)
837 SET_TARGET_PROPERTIES(bug25714 PROPERTIES LINKER_LANGUAGE CXX)
838+
839+ ADD_EXECUTABLE(stmt_execute_immediate stmt_execute_immediate.cc)
840+ TARGET_LINK_LIBRARIES(stmt_execute_immediate perconaserverclient)
841+ SET_TARGET_PROPERTIES(stmt_execute_immediate PROPERTIES LINKER_LANGUAGE CXX)
842 ENDIF()
843
844 INSTALL(TARGETS mysql_client_test DESTINATION ${INSTALL_BINDIR} COMPONENT Test)
845
846=== added file 'tests/stmt_execute_immediate.cc'
847--- tests/stmt_execute_immediate.cc 1970-01-01 00:00:00 +0000
848+++ tests/stmt_execute_immediate.cc 2014-09-09 12:25:45 +0000
849@@ -0,0 +1,259 @@
850+#include <my_global.h>
851+#include <my_sys.h>
852+#include <mysql.h>
853+#include <string.h>
854+
855+void finish_with_error(MYSQL *con)
856+{
857+ fprintf(stderr, "%s\n", mysql_error(con));
858+ mysql_close(con);
859+ exit(1);
860+}
861+
862+int main(int argc, char **argv)
863+{
864+ MYSQL con;
865+ MYSQL_BIND stmt_result[3];
866+ int id;
867+ char name[255];
868+ int price;
869+ my_bool is_null[3];
870+ MYSQL_STMT *stmt;
871+ MYSQL_RES *res= NULL;
872+
873+ if (argc != 2 || !strcmp(argv[1], "--help"))
874+ {
875+ fprintf(stderr, "This program is a part of the MySQL test suite. "
876+ "It is not intended to be executed directly by a user.\n");
877+ return -1;
878+ }
879+
880+ MY_INIT(argv[0]);
881+
882+ memset(stmt_result, 0, sizeof(stmt_result));
883+
884+ stmt_result[0].buffer_type = MYSQL_TYPE_LONG;
885+ stmt_result[0].buffer = (void *) &id;
886+ stmt_result[0].is_unsigned = 0;
887+ stmt_result[0].is_null = &is_null[0];
888+ stmt_result[0].length = 0;
889+
890+ stmt_result[1].buffer_type = MYSQL_TYPE_STRING;
891+ stmt_result[1].buffer = (void *) name;
892+ stmt_result[1].is_unsigned = 0;
893+ stmt_result[1].is_null = &is_null[0];
894+ stmt_result[1].buffer_length = sizeof(name) - 1;
895+ stmt_result[1].length = 0;
896+
897+ stmt_result[2].buffer_type = MYSQL_TYPE_LONG;
898+ stmt_result[2].buffer = (void *) &price;
899+ stmt_result[2].is_unsigned = 0;
900+ stmt_result[2].is_null = &is_null[0];
901+ stmt_result[2].length = 0;
902+
903+ mysql_init(&con);
904+
905+ if (mysql_real_connect(&con, "127.0.0.1", "root", "",
906+ "test", atoi(argv[1]), NULL, 0) == NULL)
907+ finish_with_error(&con);
908+
909+ stmt = mysql_stmt_init(&con);
910+ /********************************************************
911+ Test #1: Test for queries which do not contain resultset.
912+ *********************************************************/
913+ printf("Testing drop, create, insert:\n");
914+ if (!stmt)
915+ finish_with_error(&con);
916+
917+ if (mysql_stmt_execute_immediate(stmt, "DROP TABLE IF EXISTS Cars"))
918+ finish_with_error(&con);
919+
920+ if (mysql_stmt_execute_immediate(stmt, "CREATE TABLE Cars(Id INT AUTO_INCREMENT PRIMARY KEY, Name TEXT, Price INT)"))
921+ finish_with_error(&con);
922+
923+ if (mysql_stmt_execute_immediate(stmt, "INSERT INTO Cars (Name, price) VALUES('Audi',52642)"))
924+ finish_with_error(&con);
925+
926+ if (mysql_stmt_execute_immediate(stmt, "INSERT INTO Cars (Name, price) VALUES('Hummer',41400)"))
927+ finish_with_error(&con);
928+
929+ if (mysql_stmt_execute_immediate(stmt, "INSERT INTO Cars (Name, price) VALUES('Volkswagen',21600)"))
930+ finish_with_error(&con);
931+
932+ my_ulonglong insert_id = mysql_stmt_insert_id(stmt);
933+ my_ulonglong affected_rows = mysql_stmt_affected_rows(stmt);
934+
935+ printf("Insert id: %llu, affected rows: %llu\n", insert_id, affected_rows);
936+
937+ if (mysql_stmt_execute_immediate(stmt, "DROP PROCEDURE IF EXISTS simpleproc"))
938+ finish_with_error(&con);
939+ if (mysql_stmt_execute_immediate(stmt, "CREATE PROCEDURE simpleproc () "
940+ "BEGIN "
941+ "SELECT * FROM Cars WHERE Id = 1; "
942+ "SELECT * FROM Cars WHERE Id = 2; "
943+ "SELECT * FROM Cars WHERE Id = 3; "
944+ "END"))
945+ finish_with_error(&con);
946+
947+ for (int need_store_result = 0; need_store_result < 2; ++need_store_result) {
948+ /************************************************************
949+ Test #2: Test for queries which have one resultset. The test
950+ has two variants - with and without storing result on
951+ client-side.
952+ *************************************************************/
953+ printf("\nTest for select %s:\n", need_store_result ?
954+ "without result storing" :
955+ "with result storing");
956+ for (unsigned i = 0; i < 3; ++i) {
957+ char query[100];
958+ snprintf(query,
959+ sizeof(query) - 1,
960+ "SELECT * FROM Cars WHERE Id <= %u", i + 1);
961+ printf("Iteration number %u\n", i);
962+ if (mysql_stmt_execute_immediate(stmt, query))
963+ finish_with_error(&con);
964+ if (mysql_stmt_bind_immediate(stmt,
965+ stmt_result, sizeof(stmt_result)/sizeof(stmt_result[0])) != 0)
966+ finish_with_error(&con);
967+ if (need_store_result && mysql_stmt_store_result(stmt))
968+ finish_with_error(&con);
969+ my_ulonglong num_rows = mysql_stmt_num_rows(stmt);
970+ unsigned int field_count = mysql_stmt_field_count(stmt);
971+ printf("Rows count: %llu, fields count: %d\n", num_rows, field_count);
972+ while(!mysql_stmt_fetch (stmt))
973+ printf("binary: %d, %s, %d\n", id, name, price);
974+ mysql_stmt_free_result(stmt);
975+ }
976+ /*****************************************************************
977+ Test #3: Test for queries which have multiple resultset. The test
978+ has two variants - with and without storing result on
979+ client-side.
980+ *****************************************************************/
981+ printf("\nTest for multiple results %s:\n",
982+ need_store_result ?
983+ "without result storing" : "with result storing");
984+ if (mysql_stmt_execute_immediate(stmt, "CALL simpleproc()"))
985+ finish_with_error(&con);
986+ do {
987+ unsigned int field_count = mysql_stmt_field_count(stmt);
988+ if (field_count) {
989+ if (mysql_stmt_bind_immediate(stmt,
990+ stmt_result, sizeof(stmt_result)/sizeof(stmt_result[0])) != 0)
991+ finish_with_error(&con);
992+ if (need_store_result && mysql_stmt_store_result(stmt))
993+ finish_with_error(&con);
994+ my_ulonglong num_rows = mysql_stmt_num_rows(stmt);
995+ printf("Rows count: %llu, fields count: %d\n", num_rows, field_count);
996+ while(!mysql_stmt_fetch (stmt))
997+ printf("binary: %d, %s, %d\n", id, name, price);
998+ mysql_stmt_free_result(stmt);
999+ }
1000+ } while(mysql_stmt_next_result(stmt) == 0);
1001+ }
1002+ /*************************************************************
1003+ Test #4: The case when the number of fields is unknown before
1004+ query execution.
1005+ **************************************************************/
1006+ printf("\nTest for select if the number and the type of fields "
1007+ "are unknown before query execution:\n");
1008+ if (mysql_stmt_execute_immediate(stmt, "SELECT * FROM Cars"))
1009+ finish_with_error(&con);
1010+ {
1011+ my_bool one= 1;
1012+ if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &one))
1013+ finish_with_error(&con);
1014+ }
1015+ if (mysql_stmt_field_count(stmt)) {
1016+ if (mysql_stmt_store_result(stmt))
1017+ finish_with_error(&con);
1018+ if ((res= mysql_stmt_result_metadata(stmt)) != NULL) {
1019+ MYSQL_BIND *my_bind;
1020+ my_bool *is_null;
1021+ ulong *length;
1022+ uint i;
1023+ MYSQL_FIELD *fields;
1024+ uint num_fields;
1025+
1026+ /* Take the column count from meta info */
1027+ fields= mysql_fetch_fields(res);
1028+ num_fields= mysql_num_fields(res);
1029+
1030+ my_bind= (MYSQL_BIND*) my_malloc(num_fields * sizeof(MYSQL_BIND),
1031+ MYF(MY_WME | MY_FAE | MY_ZEROFILL));
1032+ length= (ulong*) my_malloc(num_fields * sizeof(ulong),
1033+ MYF(MY_WME | MY_FAE));
1034+ is_null= (my_bool*) my_malloc(num_fields * sizeof(my_bool),
1035+ MYF(MY_WME | MY_FAE));
1036+
1037+ /* Allocate data for the result of each field */
1038+ for (i= 0; i < num_fields; i++)
1039+ {
1040+ uint max_length= fields[i].max_length + 1;
1041+ my_bind[i].buffer_type= MYSQL_TYPE_STRING;
1042+ my_bind[i].buffer= my_malloc(max_length, MYF(MY_WME | MY_FAE));
1043+ my_bind[i].buffer_length= max_length;
1044+ my_bind[i].is_null= &is_null[i];
1045+ my_bind[i].length= &length[i];
1046+
1047+ printf("bind col[%d]: buffer_type: %d, buffer_length: %lu\n",
1048+ i, my_bind[i].buffer_type, my_bind[i].buffer_length);
1049+ }
1050+ /*
1051+ It is safe to use mysql_stmt_bind_result() instead of
1052+ mysql_stmt_bind_immediate() here as all neccessary
1053+ data in stmt has been initialized already in
1054+ mysql_stmt_store_result() as STMT_ATTR_UPDATE_MAX_LENGTH
1055+ attribute was set to one
1056+ (see also stmt->update_max_length description).
1057+ */
1058+ if (mysql_stmt_bind_result(stmt, my_bind))
1059+ finish_with_error(&con);
1060+
1061+ while (mysql_stmt_fetch(stmt) == 0)
1062+ for (i= 0; i < num_fields; i++)
1063+ printf("fetch col[%d]: buffer_type: %d, buffer_length: %lu, "
1064+ "buffer: %s\n",
1065+ i, my_bind[i].buffer_type, my_bind[i].buffer_length,
1066+ (char *)my_bind[i].buffer);
1067+
1068+ for (i= 0; i < num_fields; i++)
1069+ {
1070+ /* Free data for output */
1071+ my_free(my_bind[i].buffer);
1072+ }
1073+ /* Free array with bind structs, lengths and NULL flags */
1074+ my_free(my_bind);
1075+ my_free(length);
1076+ my_free(is_null);
1077+ }
1078+ }
1079+ {
1080+ my_bool zero= 0;
1081+ if (mysql_stmt_attr_set(stmt, STMT_ATTR_UPDATE_MAX_LENGTH, (void*) &zero))
1082+ finish_with_error(&con);
1083+ }
1084+ mysql_stmt_free_result(stmt);
1085+
1086+ /************************************************************
1087+ Test #5: Test for errors.
1088+ *************************************************************/
1089+ printf("\nTest for error:\n");
1090+ if (!mysql_stmt_execute_immediate(stmt, "SELECT * FROM bars")) {
1091+ printf("Must be error!\n");
1092+ return 1;
1093+ }
1094+ printf("Errno: %u, error text: %s\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
1095+ if (!mysql_stmt_execute_immediate(stmt, "SELEC * FROM bars")) {
1096+ printf("Must be error!\n");
1097+ return 1;
1098+ }
1099+ printf("Errno: %u, error text: %s\n", mysql_stmt_errno(stmt), mysql_stmt_error(stmt));
1100+
1101+ mysql_stmt_execute_immediate(stmt, "DROP PROCEDURE simpleproc");
1102+ mysql_stmt_execute_immediate(stmt, "DROP TABLE Cars");
1103+
1104+ mysql_stmt_close(stmt);
1105+
1106+ mysql_close(&con);
1107+ return 0;
1108+}

Subscribers

People subscribed via source and target branches