Merge lp:~vlad-lesin/percona-server/5.6-binary-answer into lp:percona-server/5.6
- 5.6-binary-answer
- Merge into 5.6
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 |
Related bugs: | |
Related blueprints: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Laurynas Biveinis (community) | Needs Fixing | ||
Review via email: mp+225987@code.launchpad.net |
Commit message
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://
Laurynas Biveinis (laurynas-biveinis) wrote : | # |
Vlad Lesin (vlad-lesin) wrote : | # |
> - This appears to implement
> http://
> in the blueprint. Is it a good idea to take their names
> COM_STMT_
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_
> - 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://
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/
> test/r/
> +++ /Users/
> test/var/
> @@ -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...
Vlad Lesin (vlad-lesin) wrote : | # |
The new results of testing are here:
with --ps-protocol
http://
with --binary-protocol
http://
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_
main.mysql_
main.query_
perfschema.
perfschema.
perfschema.
perfschema.
perfschema.
perfschema.
perfschema.
perfschema.
perfschema.nesting
Vlad Lesin (vlad-lesin) wrote : | # |
The "reconnect" bug.
If MYSQL::reconnect flag is set reconnect does not happen on mysql_stmt_
If MYSQL::reconnect flag is set and mysql_stmt_
(gdb) bt
#0 __libc_send (fd=7, buf=0x9f9ae0, n=13, flags=0)
at ../sysdeps/
#1 0x0000000000438c6c in inline_
src_
at ./include/
#2 0x0000000000439597 in vio_write (vio=0x9f58d0, buf=0x9f9ae0 "\t", size=13)
at ./vio/viosocket
#3 0x000000000041ea38 in net_write_raw_loop (net=0x7fffffff
buf=0x9f9ae0 "\t", count=13)
at ./sql/net_
#4 0x000000000041ed0d in net_write_packet (net=0x7fffffff
packet=0x9f9ae0 "\t", length=13)
at ./sql/net_
#5 0x000000000041e31f in net_flush (net=0x7fffffff
at ./sql/net_
#6 0x000000000041e7d9 in net_write_command (net=0x7fffffff
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_
#7 0x0000000000413a08 in cli_advanced_
command=
arg=0x4c3eaf "SELECT 1", arg_length=8, skip_check=1 '\001', stmt=0x9fbb80)
at ./sql-common/
#8 0x000000000040914d in cli_stmt_
query=0x4c3eaf "SELECT 1", len=8)
at ./libmysql/
#9 0x0000000000409ba4 in mysql_stmt_
q=0x4c3eaf "SELECT 1")
at ./libmysql/
#10 0x0000000000404cfd in main (argc=2, argv=0x7fffffff
at ./tests/
And the fact the connection was closed by peer is determined during the step of receiving data from server in cli_read_
Why does the reconnection happen if mysql_stmt_
(gdb) bt
#0 __libc_send (fd=7, buf=0x9f9ae0, n=9, flags=0)
at ../sysdeps/
#1 0x0000000000438c99 in inline_
src_
at ./include/
#2 0x00000000004395c4 in vio_write (vio=0x9f58d0, buf=0x9f9ae0 "\005", siz...
- 619. By Vlad Lesin
-
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 protocolSo 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_
const uchar *header, ulong header_length,
const uchar *arg, ulong arg_length, my_bool skip_check,
{
...
my_bool stmt_skip= stmt ? stmt->state != MYSQL_STMT_
...
if (mysql->net.vio == 0)
{ /* Do reconnect if possible */
if (mysql_
DBUG_
}
...
}
As stmt->state was set to MYSQL_STMT_
The fix is skipping stmt argument of cli_advanced_
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_
After fixing these two bugs the number of failed tests ran with '--binary-protocol' option decreased dramatically. Here is the list:
main.
main.parser
main.
perfschema.
rpl.
(http://
Unmerged revisions
- 619. By Vlad Lesin
-
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 protocolSo 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
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 | +} |
- This appears to implement dev.mysql. com/worklog/ task/?id= 4627. Let's reference it STMT_EXECUTE_ IMMEDIATE and mysql_stmt_ execute_ immediate( )? dev.mysql. com/doc/ internals/ en/binary- protocol- value.html execute in binary protocol, by this feature, and run
http://
in the blueprint. Is it a good idea to take their names
COM_
- 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://
- Another way to have good test coverage for the feature would be
to patch run_query in mysqltest.cc, which happens to do
prepare+
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 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
+++ /Users/
@@ -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/tww0v3g14tnc qd9lsc81vqj8000 0gn/T/kL5oE2Hwj x/ is case insensitive
- Not sure if I like COM_STMT_QUERY replacement with COM_QUERY in command. On one hand, COM_STMT_QUERY is like COM_QUERY, STMT_QUERY as necessary.
dispatch_
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_
Minor comments: query_bind_ result declaration (diff stmt_query, mysql_stmt_query need header comments bind_result was required?
- COM_STMT_QUERY in mysql_com.h should go above the "don't forget
to update ... " comment.
- args aligment in mysql_stmt_
lines 16+ and 46+)
- init_stmt_fields, cli_stmt_query, reinit_stmt_fields,
mysql_
- spurious diff line 312
- Was adding of emb_stmt_query necessary?
- Why patching of mysql_stmt_
- diff line 472: bool x = (y == z) ? true : false is simply x = (y
== z)