Merge lp:~jan-kneschke/mysql-proxy/prep-stmt-codecs into lp:mysql-proxy/0.8

Proposed by Jan Kneschke
Status: Merged
Merged at revision: 1132
Proposed branch: lp:~jan-kneschke/mysql-proxy/prep-stmt-codecs
Merge into: lp:mysql-proxy/0.8
Diff against target: 5689 lines (+5074/-275)
26 files modified
Makefile.am (+1/-1)
configure.in (+9/-1)
doc/Makefile.am (+18/-0)
doc/book.txt (+17/-0)
doc/chapter/Makefile.am (+4/-0)
doc/chapter/protocol.txt (+1581/-122)
doc/chapter/scripting.txt (+326/-96)
doc/protocol.txt (+8/-0)
doc/scripting.txt (+8/-0)
examples/Makefile.am (+1/-0)
examples/tutorial-prep-stmts.lua (+74/-0)
lib/mysql-proto.c (+270/-0)
src/CMakeLists.txt (+4/-0)
src/Makefile.am (+4/-0)
src/network-mysqld-packet.c (+506/-50)
src/network-mysqld-packet.h (+54/-0)
src/network-mysqld-proto.h (+7/-4)
src/network_mysqld_proto_binary.c (+436/-0)
src/network_mysqld_proto_binary.h (+12/-0)
src/network_mysqld_type.c (+656/-0)
src/network_mysqld_type.h (+147/-0)
tests/unit/CMakeLists.txt (+2/-0)
tests/unit/Makefile.am (+18/-0)
tests/unit/lua/mysql-proto.lua (+70/-1)
tests/unit/t_network_mysqld_packet.c (+636/-0)
tests/unit/t_network_mysqld_type.c (+205/-0)
To merge this branch: bzr merge lp:~jan-kneschke/mysql-proxy/prep-stmt-codecs
Reviewer Review Type Date Requested Status
Registry Administrators Pending
Review via email: mp+36325@code.launchpad.net

This proposal supersedes a proposal from 2010-09-22.

Description of the change

* added a network_mysqld_type_t infrastructure that can store and convert types that are known to the binary row type (network_mysqld_type.[ch])
* added encoders and decoders for the binary row format (network_mysqld_proto_binary.[ch])
* added encoders and decoders for COM_STMT_PREPARE, COM_STMT_EXECUTE, COM_STMT_CLOSE and its special response packets
* added a example on how to use them examples/tutorial-prep-stmt.lua

To post a comment you must log in.
1167. By <email address hidden>

restructed the file into a valid reST file and added infos about MYSQL_TYPE_* and COM_*

  * see http://docutils.sourceforge.net/

1168. By <email address hidden>

streamlined the payload description

  * described the possible types of the payload
  * adjusted the payload descriptions of the packets to follow it strictly

1169. By <email address hidden>

allow COM_STMT_PREPAREs to fail and linked its OK packet

1170. By <email address hidden>

document COM_SLEEP, COM_INIT_DB, _CREATE_DB and _DROP_DB

1171. By <email address hidden>

moved OK and ERR into the Generic Packets section and EOF into the text resultset

1172. By <email address hidden>

added 'COM_STMT_EXECUTE Response' and added a few more 'response:' fields to the packet definitions

1173. By <email address hidden>

added LOCAL INFILE handling

1174. By <email address hidden>

added links to the external documents describing the protocol

1175. By <email address hidden>

documented the packets of the auth phase

1176. By <email address hidden>

added COM_STMT_RESET and _STMT_SEND_LONG_DATA

1177. By <email address hidden>

added the pre-4.1 column definition

1178. By <email address hidden>

COM_STMT_RESET has a response: OK or ERR

1179. By <email address hidden>

_SEND_LONG_DATA is 18

1180. By <email address hidden>

added COM_FIELD_LIST

1181. By <email address hidden>

added COM_PROCESS_INFO, _PROCESS_KILL and _PING

1182. By <email address hidden>

documented the commands that are not handled by the server

1183. By <email address hidden>

added COM_SET_OPTION

1184. By <email address hidden>

added COM_DEBUG, _STATISTICS and _SHUTDOWN

1185. By <email address hidden>

TABLE_DUMP and CONNECT_OUT are undefined in 5.x

1186. By <email address hidden>

added COM_CHANGE_USER

1187. By <email address hidden>

documented multi-resultsets and all the server-status flags

1188. By <email address hidden>

moved the status flags to the OK packet

1189. By <email address hidden>

added the capability flags

1190. By <email address hidden>

added some more cross-references

1191. By <email address hidden>

fixed section indention to put the COM_* responses as sub-section of the command

1192. By <email address hidden>

added the COM_STMT_EXECUTE flags and did some cosmetic changes and typo fixes

1193. By <email address hidden>

replaced doxygen version of scripting.txt by a reST version

  * added the basic structure
  * documented the mysql.proto module

1194. By <email address hidden>

removed the document headings

1195. By <email address hidden>

moved the protocol.txt and scripting.txt into the chapter/ dir

  * allow to include them into standalone documents and into the
    full book

1196. By <email address hidden>

added rules to generate a full book and standalone pieces of protocol and scripting

1197. By <email address hidden>

removed the use of the gnu-make extension

1198. By <email address hidden>

use AC_MISSING() to let "make html" fail nicely if rst2html.py isn't installed

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'Makefile.am'
--- Makefile.am 2010-04-06 14:46:45 +0000
+++ Makefile.am 2010-09-27 14:20:48 +0000
@@ -1,4 +1,4 @@
1SUBDIRS = cmake src lib plugins examples scripts tests m41SUBDIRS = cmake src lib plugins examples scripts tests m4 doc
22
3EXTRA_DIST = \3EXTRA_DIST = \
4 config.h.cmake \4 config.h.cmake \
55
=== modified file 'configure.in'
--- configure.in 2010-05-25 13:44:43 +0000
+++ configure.in 2010-09-27 14:20:48 +0000
@@ -43,6 +43,13 @@
43AC_PROG_MAKE_SET43AC_PROG_MAKE_SET
44AM_PROG_CC_C_O44AM_PROG_CC_C_O
4545
46if test x$RST2HTML = x; then
47 AM_MISSING_PROG(RST2HTMLMISSING, rst2html.py)
48 RST2HTML=$RST2HTMLMISSING
49fi
50AC_SUBST(RST2HTML)
51
52
46dnl more automake stuff53dnl more automake stuff
47AM_C_PROTOTYPES54AM_C_PROTOTYPES
4855
@@ -432,7 +439,8 @@
432AC_CONFIG_FILES([scripts/mysql-proxy:scripts/mysql-proxy-binwrapper.in], [chmod +x scripts/mysql-proxy])439AC_CONFIG_FILES([scripts/mysql-proxy:scripts/mysql-proxy-binwrapper.in], [chmod +x scripts/mysql-proxy])
433AC_CONFIG_FILES([mysql-proxy.pc])440AC_CONFIG_FILES([mysql-proxy.pc])
434AC_CONFIG_FILES([mysql-chassis.pc])441AC_CONFIG_FILES([mysql-chassis.pc])
435442AC_CONFIG_FILES([doc/Makefile])
443AC_CONFIG_FILES([doc/chapter/Makefile])
436AC_OUTPUT444AC_OUTPUT
437445
438446
439447
=== modified file 'doc/Makefile.am'
--- doc/Makefile.am 2009-06-29 15:34:01 +0000
+++ doc/Makefile.am 2010-09-27 14:20:48 +0000
@@ -1,3 +1,5 @@
1SUBDIRS=chapter
2
1EXTRA_DIST = \3EXTRA_DIST = \
2 lua-classes.dot \4 lua-classes.dot \
3 architecture.dot \5 architecture.dot \
@@ -10,3 +12,19 @@
10 tests.txt \12 tests.txt \
11 scripting.txt \13 scripting.txt \
12 lifecycle.msc14 lifecycle.msc
15
16clean-local:
17 rm -f *.html
18
19html-local: book.html protocol.html scripting.html
20
21## we use http://docutils.sourceforge.net/rst.html to generate the docs
22book.html: book.txt chapter/scripting.txt chapter/protocol.txt
23 ${RST2HTML} $< $@
24
25protocol.html: protocol.txt chapter/protocol.txt
26 ${RST2HTML} $< $@
27
28scripting.html: scripting.txt chapter/scripting.txt
29 ${RST2HTML} $< $@
30
1331
=== added file 'doc/book.txt'
--- doc/book.txt 1970-01-01 00:00:00 +0000
+++ doc/book.txt 2010-09-27 14:20:48 +0000
@@ -0,0 +1,17 @@
1***********
2MySQL Proxy
3***********
4
5.. contents::
6
7==============
8MySQL Protocol
9==============
10
11.. include:: chapter/protocol.txt
12
13==========================
14Scripting the proxy plugin
15==========================
16
17.. include:: chapter/scripting.txt
018
=== added directory 'doc/chapter'
=== added file 'doc/chapter/Makefile.am'
--- doc/chapter/Makefile.am 1970-01-01 00:00:00 +0000
+++ doc/chapter/Makefile.am 2010-09-27 14:20:48 +0000
@@ -0,0 +1,4 @@
1EXTRA_DIST=\
2 protocol.txt \
3 scripting.txt
4
05
=== renamed file 'doc/protocol.txt' => 'doc/chapter/protocol.txt'
--- doc/protocol.txt 2009-06-29 16:39:46 +0000
+++ doc/chapter/protocol.txt 2010-09-27 14:20:48 +0000
@@ -1,123 +1,1582 @@
1/** 1The MySQL protocol is used between the MySQL Clients and the MySQL Server. It is implemented by
2@page protocol MySQL Protocol2
33* the Connectors (Connector/C, ...)
4The MySQL Protocol is spilt into the four phases:4* MySQL Proxy
55* the MySQL Server itself for the slaves
6@dot6
7digraph {7The documentation is sparse and is split between:
8connect -> auth;8
9auth -> command;9 http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol
10command -> disconnect;10
11command -> command;11and the source files of the MySQL Server:
12connect -> disconnect;12
13auth -> disconnect;13* sql/sql_parse.cc for the protocol basics
14}14
15@enddot15 * dispatch_command()
1616
17The client and the server send and receive packets as documented in http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol 17* sql/sql_prepare.cc for the prepared statement protocol
1818
19@dot19 * mysqld_stmt_prepare()
20digraph states {20 * mysqld_stmt_execute()
21 graph [rankdir=LR];21 * mysqld_stmt_close()
22 node [fontname=Helvetica, fontsize=10];22 * mysqld_stmt_reset()
2323 * mysqld_stmt_fetch()
24 connect [ shape=record ];24 * mysql_stmt_get_longdata()
25 disconnect [ shape=record ];25
2626* sql/sql_repl.cc for the binlog protocol
27 subgraph cluster_client { 27
28 label = "client";28 * mysql_binlog_send()
29 style = filled;29
30 node [ style=filled, fillcolor=lightblue ];30* sql/protocol.cc for the value and type encoding
31 connect; 31
32 auth_response; 32Tracking the MySQL Protocol
33 auth_old; 33===========================
34 command; 34
35 command_local;35All the examples here are captured with::
36 }36
3737 $ ngrep -x -q -d lo0 '' 'port 3306'
38 subgraph cluster_server { 38
39 label = "server";39A mysql client logs in
40 style = filled;40----------------------
41 node [ style=filled, fillcolor=orange ];41
42 auth_challenge; 42Taking a look at the packet dump when a mysql-client logs in::
43 auth_result; 43
44 command_result; 44 client -> server
45 command_infile;45 <connect>
46 }46
47 47The client initiates the communication by connecting to the server.::
48 subgraph { 48
49 edge [ fontcolor=blue, color=blue, fontsize=10, fontname=Helvetica ];49 server -> client
5050 36 00 00 00 0a 35 2e 35 2e 32 2d 6d 32 00 03 00 6....5.5.2-m2...
51 connect->auth_challenge [ label = "connecting server" ];51 00 00 27 75 3e 6f 38 66 79 4e 00 ff f7 08 02 00 ..'u>o8fyN......
52 auth_response->auth_result [ label = "capabilities, password, default-db", URL="http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Client_Authentication_Packet" ]; 52 00 00 00 00 00 00 00 00 00 00 00 00 00 57 4d 5d .............WM]
53 auth_old->auth_result [ label = "scrambled password" ] ; 53 6a 7c 53 68 32 5c 59 2e 73 00 j|Sh2\Y.s.
54 command->command_result [ label = "command (COM_*)", URL="http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Command_Packet" ] ;54
55 command->command_infile [ label = "LOAD DATA INFILE LOCAL" ];55which responds with a handshake packet which contains the version, some flags and a password challenge.::
56 command_local->command_result [ label = "file content"];56
57 }57 client -> server
5858 3a 00 00 01 05 a6 03 00 00 00 00 01 08 00 00 00 :...............
59 subgraph {59 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60 edge [ fontcolor=red, color=red, fontsize=10, fontname=Helvetica ];60 00 00 00 00 72 6f 6f 74 00 14 cb b5 ea 68 eb 6b ....root.....h.k
61 auth_challenge->disconnect [ label = "ERR: host denied", URL="http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Error_Packet" ];61 3b 03 cb ae fb 9b df 5a cb 0f 6d b5 de fd ;......Z..m...
62 auth_challenge->auth_response [ label = "0x10: auth_challenge", URL="http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Handshake_Initialization_Packet" ];62
63 auth_result->auth_old [ label = "EOF: old password reauth" ];63The client answers with username, some flags and the response to the challenge.::
64 auth_result->command [ label = "OK: auth done", URL="http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#OK_Packet" ];64
65 auth_result->disconnect [ label = "ERR: auth failed", URL="http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Error_Packet" ];65 server -> client
66 command_result->command [ label = "OK|ERR|Resultset", URL="http://forge.mysql.com/wiki/MySQL_Internals_ClientServer_Protocol#Result_Set_Header_Packet" ] ;66 07 00 00 02 00 00 00 02 00 00 00 ...........
67 command_result->disconnect [ label = "command = COM_QUIT" ];67
68 command_result->command_result [ label = "command = COM_BINLOG_DUMP" ];68As the client provided the right password and the flags are fine, the server responds with a `OK packet`_. That closes auth-phase
69 command_infile->command_local [ label = "EOF: filename" ];69and switches to the command-phase.::
70 }70
71}71 client -> server
72@enddot72 21 00 00 00 03 73 65 6c 65 63 74 20 40 40 76 65 !....select @@ve
7373 72 73 69 6f 6e 5f 63 6f 6d 6d 65 6e 74 20 6c 69 rsion_comment li
74The @ref page-core exposes all the states to the @ref page-plugins.74 6d 69 74 20 31 mit 1
7575
76@section section-protocol-use-cases Use Cases76The mysql client first checks the version string of the server and sends a `COM_QUERY`_ packet.::
7777
78-# the client connects to the server and waits for data to return @msc78 server -> client
79 client, backend;79 01 00 00 01 01 27 00 00 02 03 64 65 66 00 00 00 .....'....def...
80 --- [ label = "connect to backend" ];80 11 40 40 76 65 72 73 69 6f 6e 5f 63 6f 6d 6d 65 .@@version_comme
81 client->backend [ label = "INIT" ];81 6e 74 00 0c 08 00 1c 00 00 00 fd 00 00 1f 00 00 nt..............
82@endmsc82 05 00 00 03 fe 00 00 02 00 1d 00 00 04 1c 4d 79 ..............My
83-# the auth-phase handles the new SHA1-style passwords and the old scramble() passwords 83 53 51 4c 20 43 6f 6d 6d 75 6e 69 74 79 20 53 65 SQL Community Se
84 -# 4.1+ passwords @msc84 72 76 65 72 20 28 47 50 4c 29 05 00 00 05 fe 00 rver (GPL)......
85 client, backend;85 00 02 00 ...
86 --- [ label = "authenticate" ];86
87 backend->client [ label = "HANDSHAKE" ];87The server responds with a resultset containing the version-string.::
88 client->backend [ label = "AUTH" ];88
89 backend->client [ label = "AUTH_RESULT" ];89 client -> server
90@endmsc90 0e 00 00 00 03 73 65 6c 65 63 74 20 55 53 45 52 .....select USER
91 -# pre-4.1 passwords @msc91 28 29 ()
92 client, backend;92
93 --- [ label = "authenticate" ];93For the prompt (\u ...) the mysql client also asks for the current username.::
94 backend->client [ label = "HANDSHAKE" ];94
95 client->backend [ label = "AUTH" ];95 server -> client
96 backend->client [ label = "OLD_PASSWORD_SCRAMBLE" ];96 01 00 00 01 01 1c 00 00 02 03 64 65 66 00 00 00 ..........def...
97 client->backend [ label = "OLD_PASSWORD_AUTH" ];97 06 55 53 45 52 28 29 00 0c 08 00 4d 00 00 00 fd .USER()....M....
98 backend->client [ label = "AUTH_RESULT" ];98 01 00 1f 00 00 05 00 00 03 fe 00 00 02 00 0f 00 ................
99@endmsc99 00 04 0e 72 6f 6f 74 40 6c 6f 63 61 6c 68 6f 73 ...root@localhos
100-# the query-phase repeats 100 74 05 00 00 05 fe 00 00 02 00 t.........
101 -# COM_QUERY and friends @msc101
102 client, backend;102which is 'root@localhost' in this example.
103 --- [ label = "query result phase" ];103
104 client->backend [ label = "QUERY" ];104MySQL Packet header
105 backend->client [ label = "QUERY_RESULT" ];105-------------------
106@endmsc106
107 -# COM_QUIT @msc107The packets that are exchanged between client and server look like::
108 client, backend;108
109 --- [ label = "query result phase" ];109 ...
110 client->backend [ label = "QUERY" ];110 T 127.0.0.1:51656 -> 127.0.0.1:3306 [AP]
111 backend->client [ label = "connection close" ];111 01 00 00 00 01
112@endmsc112
113 -# COM_BINLOG_DUMP @msc113The example shows a COM_QUIT packet. It starts (like all packets) with a 4 byte packet header:
114 client, backend;114
115 --- [ label = "query result phase" ];115* 3 byte length
116 client->backend [ label = "QUERY" ];116* 1 byte sequence-id
117 backend->client [ label = "QUERY_RESULT" ];117
118 ... [ label = "more binlog entries" ];118The length is the length of the payload of the packet. If the payload is larger than 2^24-2 bytes the length is set to 2^24-1
119 backend->client [ label = "QUERY_RESULT"];119and a additional packets are sent with the rest of the payload until the payload of a packet is less than 2^24-2 bytes.
120@endmsc120
121 */121The sequence-id is incremented with each packet for a sequence of packets. It is reset, when a new command begins.
122122
123Basic Types
124===========
125
126Integer
127-------
128
129The MySQL Protocol has a set of possible encodings for integers:
130
131* fixed length intergers
132* length encoded integers
133
134fixed length integer
135....................
136
137The fixed length integers can be of a byte-length 1, 2, 3, 4 or 8 and send their first byte first. The packet length
138for example is::
139
140 01 00 00
141
142is a 3-byte fixed length integer with the value `1`.
143
144length encoded integer
145......................
146
147In other places integers have a variable size of 1, 3, 4 or 9 bytes depending on their value:
148
149========================== ======
150value bytes
151========================== ======
152``< 251`` 1
153``>= 251 < (2^16 - 1)`` 3
154``>= (2^16) < (2^24 - 1)`` 4
155``>= (2^24)`` 9
156========================== ======
157
158The 1-byte values from 251 to 255 have a special meaning and aren't used for integers. Instead they
159signal special packets or the 3 other variable length integer types:
160
161======== === ===========
162hex dec description
163======== === ===========
164``0xfb`` 251 NULL in the `Text Resultset Row`_
165``0xfc`` 252 indicator for a 2-byte integer
166``0xfd`` 253 indicator for a 3-byte integer
167``0xfe`` 254 indicator for a 8-byte integer or first byte of a `EOF packet`_
168``0xff`` 255 first byte of a `ERR packet`_
169======== === ===========
170
171They also send least significant byte first.
172
173String
174------
175
176Strings appear in a few forms in the protocol:
177
178_`Fixed Length String`
179 Fixed length strings have a known, hardcoded length. An example is the `sql-state` of the `ERR packet`_ which is always 5 byte long.
180
181_`NUL-terminated String`
182 Strings that are terminated by a [00] byte.
183
184_`Length Encoded String`
185 A length encoded string is a string that is prefixed with `length encoded integer`_ describing the length of the string.
186
187_`End-of-packet String`
188 If a string is the last component of a packet, its length can be calculated from the overall-packet length minus the current position.
189
190Describing packets
191------------------
192
193In this document we describe the packets by first defining their payload and provide examples with packet header and payload as
194you would see it on the wire.::
195
196 <packetname>
197 <description>
198
199 direction: client -> server
200
201 payload:
202 <type> <description>
203
204 Example:
205 01 00 00 00 01
206
207The `<type>` describes the sequence of bytes of the packet:
208
209============== ===========
210type description
211============== ===========
2121 1 byte `fixed length integer`_
2132 2 byte `fixed length integer`_
2143 3 byte `fixed length integer`_
2154 4 byte `fixed length integer`_
2168 8 byte `fixed length integer`_
217lenenc-int `length encoded integer`_
218string `NUL-terminated string`_
219string[p] `End-of-packet string`_
220string[`<n>`] fixed length string with the length `<n>`
221lenenc-str `length encoded string`_
222n a byte sequence of any length
223============== ===========
224
225.. attention::
226 Some packets have optional fields or a different layout depending on the `capability flags`_ that are sent as part of the
227 `Auth Response Packet`_.
228
229If a field has a fixed value its description will show it as hex value in brackets like `[00]`.
230
231Generic Response Packets
232========================
233
234For most of the commands the client sends to the server one of two packets is returned as response:
235
236* `OK packet`_
237* `ERR packet`_
238
239OK packet
240---------
241
242::
243
244 OK
245
246 direction: server -> client
247
248 payload:
249 1 [00] the OK header
250 lenenc-int affected rows
251 lenenc-int last-insert-id
252 2 status flags
253 if capabilities & PROTOCOL_41:
254 2 warnings
255
256 example:
257 07 00 00 02 00 00 00 02 00 00 00 ...........
258
259Status Flags
260............
261
262The status flags are a bit-field:
263
264====== =============
265flag constant name
266====== =============
2670x0001 SERVER_STATUS_IN_TRANS
2680x0002 SERVER_STATUS_AUTOCOMMIT
2690x0008 _`SERVER_MORE_RESULTS_EXISTS`
2700x0010 SERVER_STATUS_NO_GOOD_INDEX_USED
2710x0020 SERVER_STATUS_NO_INDEX_USED
2720x0040 SERVER_STATUS_CURSOR_EXISTS
2730x0080 SERVER_STATUS_LAST_ROW_SENT
2740x0100 SERVER_STATUS_DB_DROPPED
2750x0200 SERVER_STATUS_NO_BACKSLASH_ESCAPES
2760x0400 SERVER_STATUS_METADATA_CHANGED
2770x0800 SERVER_QUERY_WAS_SLOW
2780x1000 SERVER_PS_OUT_PARAMS
279====== =============
280
281ERR packet
282----------
283
284::
285
286 ERR
287
288 direction: server -> client
289
290 payload:
291 1 [ff] the ERR header
292 2 error code
293 if capabilities & PROTOCOL_41:
294 1 '#' the sql-state marker
295 string[5] sql-state
296 all protocols:
297 string[p] error-message
298
299 example:
300 17 00 00 01 ff 48 04 23 48 59 30 30 30 4e 6f 20 .....H.#HY000No
301 74 61 62 6c 65 73 20 75 73 65 64 tables used
302
303
304The Auth Phase
305==============
306
307A simple MySQL 4.1+ auth starts with:
308
3091. the client connecting to the server
3102. the server responds with the `Auth Challenge Packet`_
3113. the client sends the `Auth Response Packet`_
3124. the server responds with `OK Packet`_
313
314If the auth fails, it sends a `ERR Packet`_ instead of a `OK Packet`_ and closes the connection:
315
3161. the client connecting to the server
3172. the server responds with the `Auth Challenge Packet`_
3183. the client sends the `Auth Response Packet`_
3194. the server responds with `ERR Packet`_ and closes connection
320
321or the server denies the client right away if for example its IP is deny:
322
3231. the client connecting to the server
3242. the server responds with the `ERR Packet`_ and closes connection
325
326MySQL 4.1+ server also may respond at step 4 with a `Old Password Auth Challenge Packet`_:
327
3281. the client connecting to the server
3292. the server responds with the `Auth Challenge Packet`_
3303. the client sends the `Auth Response Packet`_
3314. the server responds with the `Old Password Auth Challenge Packet`_
3325. the client sends the `Old Password Auth Response Packet`_
3336. the server responds with `OK Packet`_ or `ERR Packet`_ and closes the connection
334
335Auth Challenge Packet
336---------------------
337
338As first packet the server sends a Auth Challenge to the client. It contains several other fields:
339
340* the protocol version
341* the mysql-server version string
342* the server capabilities
343* the auth challenge
344
345The client answers with a `Auth Response Packet`_.
346
347::
348
349 Auth Challenge Packet
350 response: Auth Response Packet
351
352 payload:
353 1 [0a] protocol version
354 string server version
355 4 connection id
356 string[8] challenge-part-1
357 1 [00] filler
358 2 capability flags
359 1 character set
360 2 status flags
361 string[13] reserved
362 if capabilities & SECURE_CONNECTION:
363 string[12] challenge-part-2
364 1 [00] filler
365
366 example:
367 36 00 00 00 0a 35 2e 35 2e 32 2d 6d 32 00 0b 00 6....5.5.2-m2...
368 00 00 64 76 48 40 49 2d 43 4a 00 ff f7 08 02 00 ..dvH@I-CJ......
369 00 00 00 00 00 00 00 00 00 00 00 00 00 2a 34 64 .............*4d
370 7c 63 5a 77 6b 34 5e 5d 3a 00 |cZwk4^]:.
371
372`status flags` is defined as the `Status Flags`_ of the `OK packet`_.
373
374Capability flags
375................
376
377The capability flags are used by the client and server to indicate which features
378they support and want to use.
379
380====== ============================== ==================================
381flags constant name description
382====== ============================== ==================================
3830x0001 CLIENT_LONG_PASSWORD new more secure passwords
3840x0002 CLIENT_FOUND_ROWS Found instead of affected rows
3850x0004 CLIENT_LONG_FLAG Get all column flags
3860x0008 CLIENT_CONNECT_WITH_DB One can specify db on connect
3870x0010 CLIENT_NO_SCHEMA Don't allow database.table.column
3880x0020 CLIENT_COMPRESS Can use compression protocol
3890x0040 CLIENT_ODBC Odbc client
3900x0080 _`CLIENT_LOCAL_FILES` Can use LOAD DATA LOCAL
3910x0100 CLIENT_IGNORE_SPACE Ignore spaces before '('
3920x0200 _`CLIENT_PROTOCOL_41` New 4.1 protocol
3930x0400 CLIENT_INTERACTIVE This is an interactive client
3940x0800 CLIENT_SSL Switch to SSL after handshake
3950x1000 CLIENT_IGNORE_SIGPIPE IGNORE sigpipes
3960x2000 CLIENT_TRANSACTIONS Client knows about transactions
3970x4000 CLIENT_RESERVED Old flag for 4.1 protocol
3980x8000 CLIENT_SECURE_CONNECTION New 4.1 authentication
399====== ============================== ==================================
400
401
402
403Auth Response Packet
404--------------------
405
406The client answers the `Auth Challenge Packet`_ with:
407
408* its capability flags
409* its password hashed with challenge
410
411If the capabilities have a `CLIENT_PROTOCOL_41`_ flag set the response packet is::
412
413 Auth Response Packet 4.1+
414 payload:
415 4 capability flags
416 4 max-packet size
417 1 character set
418 string[23] reserved
419 string username
420 if capabilities & SECURE_CONNECTION:
421 lenenc-str auth-response
422 else:
423 string auth-response
424 all:
425 string[p] database
426
427If not, it is::
428
429 Auth Response Packet pre-4.1
430 payload:
431 2 capability flags
432 3 max-packet size
433 string username
434 string auth-response
435
436`capability flags` are the same as defined in the `Capability flags`_ of the `Auth Challenge Packet`_ plus:
437
438========== ============================== ==================================
439flags constant name description
440========== ============================== ==================================
4410x00010000 _`CLIENT_MULTI_STATEMENTS` Enable/disable multi-stmt support
4420x00020000 _`CLIENT_MULTI_RESULTS` Enable/disable multi-results
4430x00040000 _`CLIENT_PS_MULTI_RESULTS` Multi-results in PS-protocol
4440x40000000 CLIENT_SSL_VERIFY_SERVER_CERT
4450x80000000 CLIENT_REMEMBER_OPTIONS
446========== ============================== ==================================
447
448
449
450Old Password Auth Challenge Packet
451----------------------------------
452
453In case the server stored a password in the OLD_PASSWORD() fashion for this
454user the client has to use another hash for the password.
455
456::
457
458 Old Password Auth Challenge Packet
459 ask the client to send the password hashed with insecure hash-function
460
461 payload:
462 1 [fe]
463
464 example:
465 01 00 00 02 fe
466
467Old Password Auth Response Packet
468---------------------------------
469
470::
471
472 Old Password Auth Response Packet
473 the password hashed with old, insecure hash-function
474
475 payload:
476 string auth-response
477
478 example:
479 09 00 00 03 5c 49 4d 5e 4e 58 4f 47 00 ....\IM^NXOG.
480
481
482
483The Command Phase
484=================
485
486In the command phase the client sends a command packet with the sequence-id [00]::
487
488 13 00 00 00 03 53 ...
489 01 00 00 00 01
490 ^^- command-byte
491 ^^---- sequence-id == 0
492
493The first byte of the payload describes the command-type like:
494
495=== ======================
496hex constant name
497=== ======================
49800 `COM_SLEEP`_
49901 `COM_QUIT`_
50002 `COM_INIT_DB`_
50103 `COM_QUERY`_
50204 `COM_FIELD_LIST`_
50305 `COM_CREATE_DB`_
50406 `COM_DROP_DB`_
50507 `COM_REFRESH`_
50608 `COM_SHUTDOWN`_
50709 `COM_STATISTICS`_
5080a `COM_PROCESS_INFO`_
5090b `COM_CONNECT`_
5100c `COM_PROCESS_KILL`_
5110d `COM_DEBUG`_
5120e `COM_PING`_
5130f `COM_TIME`_
51410 `COM_DELAYED_INSERT`_
51511 `COM_CHANGE_USER`_
51612 COM_BINLOG_DUMP
51713 `COM_TABLE_DUMP`_
51814 `COM_CONNECT_OUT`_
51915 COM_REGISTER_SLAVE
52016 `COM_STMT_PREPARE`_
52117 `COM_STMT_EXECUTE`_
52218 `COM_STMT_SEND_LONG_DATA`_
52319 `COM_STMT_CLOSE`_
5241a `COM_STMT_RESET`_
5251b `COM_SET_OPTION`_
5261c `COM_STMT_FETCH`_
5271d `COM_DAEMON`_
528=== ======================
529
530.. _COM_SLEEP: `unhandled commands`_
531.. _COM_CONNECT: `unhandled commands`_
532.. _COM_TIME: `unhandled commands`_
533.. _COM_DELAYED_INSERT: `unhandled commands`_
534.. _COM_CONNECT_OUT: `unhandled commands`_
535.. _COM_TABLE_DUMP: `unhandled commands`_
536.. _COM_DAEMON: `unhandled commands`_
537
538The commands belong to
539
540* the `Old Commands`_
541* the `Prepared Statements`_ Commands
542* the `Stored Procedures`_ Commands
543* or the Replication Commands
544
545Old Commands
546============
547
548The old commands are supported for all MySQL Server versions from 3.23 upwards (and perhaps older).
549
550unhandled commands
551------------------
552
553* COM_SLEEP
554* COM_CONNECT
555* COM_TIME
556* COM_DELAYED_INSERT
557* COM_DAEMON
558
559These commands are only used internally by the server or are deprecated. Sending the to the server always results in a
560`ERR packet`_.
561
562COM_QUIT
563--------
564
565::
566
567 COM_QUIT
568 tells the server that the client wants to close the connection
569
570 direction: client -> server
571 response: either a connection close or a OK packet
572
573 payload:
574 1 [01] COM_QUIT
575
576 Example:
577 01 00 00 00 01
578
579COM_INIT_DB
580-----------
581
582::
583
584 COM_INIT_DB
585 change the default schema of the connection
586
587 direction: client -> server
588 response: OK or ERR
589
590 payload:
591 1 [02] COM_INIT_DB
592 string[p] schema name
593
594 example:
595 05 00 00 00 02 74 65 73 74 .....test
596
597COM_QUERY
598---------
599
600A COM_QUERY is used to send the server a text-based query that is executed immediately.
601
602The server replies to a COM_QUERY packet with a `COM_QUERY Response`_.
603
604::
605
606 COM_QUERY
607 tells the server to execute a text-based query
608
609 direction: client -> server
610
611 payload:
612 1 [03] COM_QUERY
613 string[p] the query the server shall execute
614
615 Example:
616 21 00 00 00 03 73 65 6c 65 63 74 20 40 40 76 65 !....select @@ve
617 72 73 69 6f 6e 5f 63 6f 6d 6d 65 6e 74 20 6c 69 rsion_comment li
618 6d 69 74 20 31 mit 1
619
620The length of the query-string is a taken from the packet length - 1.
621
622API call: `mysql_query() <http://dev.mysql.com/doc/refman/5.1/en/mysql-query.html>`_
623
624
625COM_QUERY Response
626..................
627
628The query-response packet is a meta packet which can be one of
629
630* a `ERR packet`_
631* a `OK packet`_
632* a `LOCAL INFILE request`_
633* a `Text Resultset`_
634
635The type of the packet is defined by the type-identifier::
636
637 COM_QUERY response
638 response to a COM_QUERY packet
639
640 payload
641 lenenc-int number of columns in the resultset
642
643If the number of columns in the resultset is 0, this is a `OK packet`_.
644
645If it is not a valid `length encoded integer`_ it is a either a `ERR packet`_ or a `LOCAL INFILE request`_.
646
647Text Resultset
648**************
649
650A Text Resultset is a possible `COM_QUERY Response`_.
651
652It is made up of a two parts:
653
654* the column definition
655* the rows
656
657which consists of a sequence of packets.
658
659The column defintion is starts with a packet containing the column-count and is
660followed by as many `Column Definition`_ packets as we have columns and is terminated
661by a `EOF packet`.
662
663Each row is a packet too. The rows are terminated by another `EOF packet`_. In case
664the query could generate the column-definition, but generating the rows afterwards
665failed a `ERR packet`_ may be sent instead of the last `EOF packet`_.
666
667* a packet containing a `length encoded integer`_ column-count
668* column-count * `Column Definition`_ packets
669* `EOF packet`_
670* row-count * packets as in `Text Resultset Row`_ format
671* `EOF packet`_
672
673In the example we use `|` to show the packet borders::
674
675 01 00 00 01 01|27 00 00 02 03 64 65 66 00 00 00 .....'....def...
676 11 40 40 76 65 72 73 69 6f 6e 5f 63 6f 6d 6d 65 .@@version_comme
677 6e 74 00 0c 08 00 1c 00 00 00 fd 00 00 1f 00 00| nt..............
678 05 00 00 03 fe 00 00 02 00|1d 00 00 04 1c 4d 79 ..............My
679 53 51 4c 20 43 6f 6d 6d 75 6e 69 74 79 20 53 65 SQL Community Se
680 72 76 65 72 20 28 47 50 4c 29|05 00 00 05 fe 00 rver (GPL)......
681 00 02 00 ...
682
683It has one column (`01 00 00 01 01`) and a field named `@@version_comment` whichs is a `MYSQL_TYPE_VAR_STRING`_ [fd].
684It has one row and its one value is `MySQL Community Server (GPL)`.
685
686If the `SERVER_MORE_RESULTS_EXISTS`_ flag is set in the last `EOF packet`_ a `multi-resultset`_ is sent.
687
688
689It may also be resultset with an closing `ERR packet`_:
690
691* a packet containing a `length encoded integer`_ column-count
692* column-count * `Column Definition`_ packets
693* `EOF packet`_
694* `ERR packet`_
695
696which is generated for queries like `EXPLAIN SELECT * FROM dual`.
697
698Column Types
699,,,,,,,,,,,,
700
701=== ======================
702hex constant name
703=== ======================
70400 _`MYSQL_TYPE_DECIMAL`
70501 _`MYSQL_TYPE_TINY`
70602 _`MYSQL_TYPE_SHORT`
70703 _`MYSQL_TYPE_LONG`
70804 _`MYSQL_TYPE_FLOAT`
70905 _`MYSQL_TYPE_DOUBLE`
71006 _`MYSQL_TYPE_NULL`
71107 _`MYSQL_TYPE_TIMESTAMP`
71208 _`MYSQL_TYPE_LONGLONG`
71309 _`MYSQL_TYPE_INT24`
7140a _`MYSQL_TYPE_DATE`
7150b _`MYSQL_TYPE_TIME`
7160c _`MYSQL_TYPE_DATETIME`
7170d _`MYSQL_TYPE_YEAR`
7180e _`MYSQL_TYPE_NEWDATE`
7190f _`MYSQL_TYPE_VARCHAR`
72010 _`MYSQL_TYPE_BIT`
721f6 _`MYSQL_TYPE_NEWDECIMAL`
722f7 _`MYSQL_TYPE_ENUM`
723f8 _`MYSQL_TYPE_SET`
724f9 _`MYSQL_TYPE_TINY_BLOB`
725fa _`MYSQL_TYPE_MEDIUM_BLOB`
726fb _`MYSQL_TYPE_LONG_BLOB`
727fc _`MYSQL_TYPE_BLOB`
728fd _`MYSQL_TYPE_VAR_STRING`
729fe _`MYSQL_TYPE_STRING`
730ff _`MYSQL_TYPE_GEOMETRY`
731=== ======================
732
733Column Definition
734,,,,,,,,,,,,,,,,,
735
736If the PROTOCOL_41 capability is set::
737
738 Column Definition - 4.1+
739
740 payload:
741 lenenc-str catalog
742 lenenc-str schema
743 lenenc-str table
744 lenenc-str org_table
745 lenenc-str name
746 lenenc-str org_name
747 1 filler [00]
748 2 character set
749 4 column length
750 1 type
751 2 flags
752 1 decimals
753 2 filler [00] [00]
754
755If not ::
756
757 Column Definition - pre-4.1
758
759 payload:
760 lenenc-str table
761 lenenc-str name
762 1 [03]
763 3 column length
764 1 [01]
765 1 type
766 1 [02] or [03]
767 if above field == 02:
768 1 flags
769 if ... == 03:
770 2 flags
771 all:
772 1 decimals
773
774Text Resultset Row
775,,,,,,,,,,,,,,,,,,
776
777A row with the data for each column.
778
779* Integers are sent as `length encoded integer`_.
780* everything else sent as `length encoded string`_.
781
782If a field is NULL `0xfb` is sent as described in `length encoded integer`_.
783
784EOF packet
785,,,,,,,,,,
786
787::
788
789 EOF
790
791 direction: server -> client
792
793 payload:
794 1 [fe] the EOF header
795 if capabilities & PROTOCOL_41:
796 2 warning count
797 2 status flags
798
799 example:
800 05 00 00 05 fe 00 00 02 00
801
802The status flags are a bit-field as defined in the `Status Flags`_ of the `OK packet`_.
803
804LOCAL INFILE request
805********************
806
807If the client wants to LOAD DATA from a LOCAL file into the server it sends::
808
809 LOAD DATA LOCAL INFILE '<filename>' INTO TABLE <table>;
810
811The LOCAL keyword triggers the server to send a LOAD INFILE packet asks the client
812to send the file via a `LOCAL INFILE data`_ response.
813
814The client has to set the `CLIENT_LOCAL_FILES`_ capability.
815
816::
817
818 LOCAL INFILE packet
819
820 direction: server -> client
821 response: LOCAL INFILE data
822
823 payload:
824 1 [fb] LOCAL INFILE
825 string[p] filename the client shall send
826
827 example:
828 0c 00 00 01 fb 2f 65 74 63 2f 70 61 73 73 77 64 ...../etc/passwd
829
830LOCAL INFILE data
831,,,,,,,,,,,,,,,,,
832
833The client sends its file data AS IS to the server in response to a `LOCAL INFILE request`_.
834
835::
836
837 LOAD INFILE data
838
839 direction: client data
840
841 payload:
842 n the filedata
843
844COM_FIELD_LIST
845--------------
846
847::
848
849 COM_FIELD_LIST
850 get the column definition of a tables
851
852 direction: client -> server
853 response:
854
855 payload:
856 1 [04] COM_FIELD_LIST
857 string table
858 string[p] field wildcard
859
860API call: `mysql_list_fields() <http://dev.mysql.com/doc/refman/5.1/en/mysql-list-fields.html>`_
861
862COM_FIELD_LIST response
863.......................
864
865The response to a `COM_FIELD_LIST`_ can either be a
866
867* a `ERR packet`_ or the
868* first half of a `Text Resultset`_
869
870 * a packet containing a `length encoded integer`_ column-count
871 * column-count * `Column Definition`_ packets
872 * `EOF packet`_
873
874COM_CREATE_DB
875-------------
876
877::
878
879 COM_CREATE_DB
880 create a schema
881
882 direction: client -> server
883 response: OK or ERR
884
885 payload:
886 1 [05] COM_CREATE_DB
887 string[p] schema name
888
889 example:
890 05 00 00 00 05 74 65 73 74 .....test
891
892COM_DROP_DB
893-----------
894
895::
896
897 COM_DROP_DB
898 drop a schema
899
900 direction: client -> server
901 response: OK or ERR
902
903 payload:
904 1 [06] COM_DROP_DB
905 string[p] schema name
906
907 example:
908 05 00 00 00 06 74 65 73 74 .....test
909
910COM_REFRESH
911-----------
912
913a low-level version of several `FLUSH ...` and `RESET ...` commands.
914
915==== =============== ===========
916flag constant name description
917==== =============== ===========
9180x01 REFRESH_GRANT Refresh grant tables `FLUSH PRIVILEGES`
9190x02 REFRESH_LOG Start on new log file `FLUSH LOGS`
9200x04 REFRESH_TABLES close all tables `FLUSH TABLES`
9210x08 REFRESH_HOSTS Flush host cache `FLUSH HOSTS`
9220x10 REFRESH_STATUS Flush status variables `FLUSH STATUS`
9230x20 REFRESH_THREADS Flush thread cache
9240x40 REFRESH_SLAVE Reset master info and restart slave thread `RESET SLAVE`
9250x80 REFRESH_MASTER Remove all bin logs in the index and truncate the index `RESET MASTER`
926==== =============== ===========
927
928::
929
930 COM_REFRESH
931 get a list of active threads
932
933 direction: client -> server
934 response: OK or ERR
935
936 payload:
937 1 [07] COM_REFRESH
938 1 flags
939
940COM_SHUTDOWN
941------------
942
943COM_SHUTDOWN is used to shutdown the mysql-server.
944
945Even if several shutdown types are define, right now only one is use: SHUTDOWN_WAIT_ALL_BUFFERS
946
947==== ============================== ===========
948type constant name description
949==== ============================== ===========
9500x00 SHUTDOWN_DEFAULT defaults to SHUTDOWN_WAIT_ALL_BUFFERS
9510x01 SHUTDOWN_WAIT_CONNECTIONS wait for existing connections to finish
9520x02 SHUTDOWN_WAIT_TRANSACTIONS wait for existing trans to finish
9530x08 SHUTDOWN_WAIT_UPDATES wait for existing updates to finish (=> no partial MyISAM update)
9540x10 SHUTDOWN_WAIT_ALL_BUFFERS flush InnoDB buffers and other storage engines' buffers
9550x11 SHUTDOWN_WAIT_CRITICAL_BUFFERS don't flush InnoDB buffers, flush other storage engines' buffers
9560xfe KILL_QUERY
9570xff KILL_CONNECTION
958==== ============================== ===========
959
960`SHUTDOWN` privilege is required.
961
962::
963
964 COM_SHUTDOWN
965 get a list of active threads
966
967 direction: client -> server
968 response: EOF or ERR
969
970 payload:
971 1 [08] COM_SHUTDOWN
972 if shutdown type != 0x00:
973 1 shutdown type
974
975Clients before 4.1.3 don't send the `shutdown type`. `0x00` is assumed in that case.
976
977COM_STATISTICS
978--------------
979
980Get a human readable string of internal statistics.
981
982::
983
984 COM_STATISTICS
985 get a list of active threads
986
987 direction: client -> server
988 response: string[p]
989
990 payload:
991 1 [09] COM_STATISTICS
992
993
994COM_PROCESS_INFO
995----------------
996
997The COM_PROCESS_INFO command is deprecated. `SHOW PROCESSLIST` should be used instead.
998
999It either returns a:
1000
1001* `Text Resultset`_ or
1002* `ERR packet`_.
1003
1004::
1005
1006 COM_PROCESS_INFO
1007 get a list of active threads
1008
1009 direction: client -> server
1010 response: resultset or ERR
1011
1012 payload:
1013 1 [0a] COM_PROCCESS_INFO
1014
1015COM_PROCESS_KILL
1016----------------
1017
1018Same as `KILL <id>`.
1019
1020::
1021
1022 COM_PROCESS_KILL
1023 ask the server to terminate a connection
1024
1025 direction: client -> server
1026 response: OK or ERR
1027
1028 payload:
1029 1 [0c] COM_PROCCESS_KILL
1030 4 connection id
1031
1032
1033COM_DEBUG
1034---------
1035
1036COM_DEBUG triggers a dump on internal debug info to stdout of the mysql-server.
1037
1038The `SUPER` privilege is required for this operation.
1039
1040::
1041
1042 COM_DEBUG
1043 dump debug info to stdout
1044
1045 direction: client -> server
1046 response: EOF or ERR
1047
1048 payload:
1049 1 [0d] COM_DEBUG
1050
1051
1052COM_PING
1053--------
1054
1055::
1056
1057 COM_PING
1058 check if the server is alive
1059
1060 direction: client -> server
1061 response: OK
1062
1063 payload:
1064 1 [0e] COM_PING
1065
1066COM_CHANGE_USER
1067---------------
1068
1069COM_CHANGE_USER changes the user of the current connection and reset the connection state.
1070
1071* user variables
1072* temp tables
1073* prepared statemants
1074* ... and others
1075
1076::
1077
1078 COM_CHANGE_USER
1079 change the user of the current connection
1080
1081 direction: client -> server
1082 response: EOF or ERR
1083
1084 payload:
1085 1 [11] COM_CHANGE_USER
1086 string user
1087 if capabilities & SECURE_CONNECTION:
1088 lenenc-str auth-response
1089 else:
1090 string auth-response
1091 all:
1092 string schema-name
1093 if more bytes in packet:
1094 2 character-set
1095
1096Prepared Statements
1097===================
1098
1099The prepared statement protocol was introduced in MySQL 4.1 and adds a few new commands:
1100
1101* `COM_STMT_PREPARE`_
1102* `COM_STMT_EXECUTE`_
1103* `COM_STMT_CLOSE`_
1104* `COM_STMT_RESET`_
1105* `COM_STMT_SEND_LONG_DATA`_
1106
1107It also defines a more compact resultset format that is used instead of the `Text Resultset`_ to
1108return resultsets.
1109
1110Keep in mind that not all statements can be prepared:
1111
1112 http://forge.mysql.com/worklog/task.php?id=2871
1113
1114Binary Protocol Resultset
1115-------------------------
1116
1117Binary Protocol Resultset is similar the `Text Resultset`_. It just contains the rows in
1118`Binary Protocol Resultset Row`_ format.
1119
1120* lenenc column-count
1121* column-count * `Column Definition`_
1122* `EOF packet`_
1123* n * rows as in `Binary Protocol Resultset Row`_
1124* `EOF packet`_
1125
1126Example::
1127
1128 01 00 00 01 01|1a 00 00 02 03 64 65 66 00 00 00 ..........def...
1129 04 63 6f 6c 31 00 0c 08 00 06 00 00 00 fd 00 00 .col1...........
1130 1f 00 00|05 00 00 03 fe 00 00 02 00|09 00 00 04 ................
1131 00 00 06 66 6f 6f 62 61 72|05 00 00 05 fe 00 00 ...foobar.......
1132 02 00 ..
1133
1134Binary Protocol Resultset Row
1135-----------------------------
1136
1137A Binary Protocol Resultset Row is made up of the bit-mask containing as many bits as we have columns in the
1138resultset + 2 and the values for columns that are not NULL in the `Binary Protocol Value`_ format.
1139
1140::
1141
1142 Binary Protocol Resultset Row
1143 row of a binary resultset (COM_STMT_EXECUTE)
1144
1145 payload:
1146 1 packet header [00]
1147 n nul-bit-mask if length (column-count + 7 + 2) / 8
1148 n values
1149
1150 example:
1151 09 00 00 04 00 00 06 66 6f 6f 62 61 72
1152
1153Binary Protocol Value
1154----------------------
1155
1156* Strings like `MYSQL_TYPE_STRING`_ `MYSQL_TYPE_BLOB`_ and `MYSQL_TYPE_DECIMAL`_::
1157
1158 lenenc-str string
1159
1160 example:
1161 03 66 6f 6f -- string = "foo"
1162
1163* `MYSQL_TYPE_LONGLONG`_::
1164
1165 8 integer least significant byte first
1166
1167 example:
1168 01 00 00 00 00 00 00 00 -- int64 = 1
1169
1170* `MYSQL_TYPE_LONG`_ and `MYSQL_TYPE_INT24`_::
1171
1172 4 integer least significant byte first
1173
1174 example:
1175 01 00 00 00 -- int32 = 1
1176
1177* `MYSQL_TYPE_SHORT`_::
1178
1179 2 integer least significant byte first
1180
1181 example:
1182 01 00 -- int16 = 1
1183
1184* `MYSQL_TYPE_TINY`_::
1185
1186 1 integer
1187
1188 example:
1189 01 -- int8 = 1
1190
1191* `MYSQL_TYPE_DOUBLE`_::
1192
1193 8 double
1194
1195 example:
1196 66 66 66 66 66 66 24 40 -- double = 10.2
1197
1198* `MYSQL_TYPE_FLOAT`_::
1199
1200 4 float
1201
1202 example:
1203 33 33 23 41 -- float = 10.2
1204
1205* `MYSQL_TYPE_DATE`_::
1206
1207 1 [04] length of the encoded value
1208 2 year
1209 1 month
1210 1 day
1211
1212 example:
1213 04 da 07 0a 11 -- date = 2010-10-17
1214
1215* `MYSQL_TYPE_DATETIME`_::
1216
1217 1 [0b] length of the encoded value
1218 2 year
1219 1 month
1220 1 day
1221 1 hour
1222 1 minutes
1223 1 seconds
1224 4 nseconds
1225
1226 example:
1227 0b da 07 0a 11 13 1b 1e 01 00 00 00 -- datetime 2010-10-17 19:27:30.000 000 001
1228
1229* `MYSQL_TYPE_TIME`_::
1230
1231 1 [0c] length of the encoded value
1232 1 sign (1 if minus, 0 for plus)
1233 4 days
1234 1 hour
1235 1 minutes
1236 1 seconds
1237 4 nseconds
1238
1239 example:
1240 0c 01 78 00 00 00 13 1b 1e 01 00 00 00 -- time -120d 19:27:30.000 000 001
1241
1242* `MYSQL_TYPE_TIMESTAMP`_::
1243
1244 1 [0b] length of the encoded value
1245 2 year
1246 1 month
1247 1 day
1248 1 hour
1249 1 minutes
1250 1 seconds
1251 4 nseconds
1252
1253 example:
1254 0b da 07 0a 11 13 1b 1e 01 00 00 00 -- timestamp
1255
1256
1257
1258COM_STMT_PREPARE
1259----------------
1260
1261COM_STMT_PREPARE creates a prepared statement from the passed query string.
1262
1263The server returns a `COM_STMT_PREPARE Response`_ which contains a statement-id which is
1264used to identify the prepared statement.
1265
1266::
1267
1268 COM_STMT_PREPARE
1269 create a prepared statement
1270
1271 direction: client -> server
1272 response: COM_STMT_PREPARE response
1273
1274 payload:
1275 1 [16] the COM_STMT_PREPARE command
1276 string[p] the query to prepare
1277
1278 example:
1279 1c 00 00 00 16 53 45 4c 45 43 54 20 43 4f 4e 43 .....SELECT CONC
1280 41 54 28 3f 2c 20 3f 29 20 41 53 20 63 6f 6c 31 AT(?, ?) AS col1
1281
1282
1283COM_STMT_PREPARE response
1284.........................
1285
1286If the `COM_STMT_PREPARE`_ succeeded, it sends:
1287
1288* `COM_STMT_PREPARE OK packet`_
1289* if num-params > 0
1290
1291 * num-params * `Column Definition`_
1292 * `EOF packet`_
1293
1294* if num-columns > 0
1295
1296 * num-colums * `Column Definition`_
1297 * `EOF packet`_
1298
1299Example::
1300
1301 0c 00 00 01 00 01 00 00 00 01 00 02 00 00 00 00| ................
1302 17 00 00 02 03 64 65 66 00 00 00 01 3f 00 0c 3f .....def....?..?
1303 00 00 00 00 00 fd 80 00 00 00 00|17 00 00 03 03 ................
1304 64 65 66 00 00 00 01 3f 00 0c 3f 00 00 00 00 00 def....?..?.....
1305 fd 80 00 00 00 00|05 00 00 04 fe 00 00 02 00|1a ................
1306 00 00 05 03 64 65 66 00 00 00 04 63 6f 6c 31 00 ....def....col1.
1307 0c 3f 00 00 00 00 00 fd 80 00 1f 00 00|05 00 00 .?..............
1308 06 fe 00 00 02 00 ......
1309
1310for a query without parameters and resultset like "DO 1" it is::
1311
1312 0c 00 00 01 00 01 00 00 00 00 00 00 00 00 00 00
1313
1314If it failed, a `ERR packet`_ is sent.
1315
1316As LOAD DATA isn't supported by `COM_STMT_PREPARE`_ yet, no is `LOCAL INFILE request`_ expected here.
1317Compare this to `COM_QUERY response`_.
1318
1319COM_STMT_PREPARE OK packet
1320**************************
1321
1322The `COM_STMT_PREPARE response`_ starts a packet which contains the meta-information for the following packets::
1323
1324 COM_STMT_PREPARE OK
1325 OK response to a COM_STMT_PREPARE packet
1326
1327 direction: server -> client
1328
1329 payload:
1330 1 [00] OK
1331 4 statement-id
1332 2 num-columns
1333 2 num-params
1334 1 [00] filler
1335 2 warning count
1336
1337
1338COM_STMT_EXECUTE
1339----------------
1340
1341COM_STMT_EXECUTE asks the server to execute a prepared statement as identified by `stmt-id`.
1342
1343It sends the values for the placeholders of the prepared statement (if it contained any) in
1344`Binary Protocol Value`_ form. The type of each parameter is made up of two bytes:
1345
1346* the type as in `Column Types`_
1347* a flag byte which has the highest bit set if the type is unsigned [80]
1348
1349The `num-params` used for this packet has to match the `num-params` of the `COM_STMT_PREPARE OK packet`_
1350of the corresponding prepared statement.
1351
1352The server returns a `COM_STMT_EXECUTE Response`_.
1353
1354::
1355
1356 COM_STMT_EXECUTE
1357 execute a prepared statement
1358
1359 direction: client -> server
1360 response: COM_STMT_EXECUTE Response
1361
1362 payload:
1363 1 [17] COM_STMT_EXECUTE
1364 4 stmt-id
1365 1 flags
1366 4 iteration-count
1367 if num-params > 0:
1368 n nul-bit-map, length: (num-params+7)/8
1369 1 new-params-bound-flag
1370 if new-params-bound-flag == 1:
1371 n type of each parameter, length: num-params * 2
1372 n value of each parameter
1373
1374 example:
1375 12 00 00 00 17 01 00 00 00 00 01 00 00 00 00 01 ................
1376 0f 00 03 66 6f 6f ...foo
1377
1378The `iteration-count` is always `1`.
1379
1380The `flags` are:
1381
1382===== =============
1383flags constant name
1384===== =============
13850x00 CURSOR_TYPE_NO_CURSOR
13860x01 CURSOR_TYPE_READ_ONLY
13870x02 CURSOR_TYPE_FOR_UPDATE
13880x04 CURSOR_TYPE_SCROLLABLE
1389===== =============
1390
1391
1392COM_STMT_EXECUTE Response
1393.........................
1394
1395Similar to the `COM_QUERY Response`_ a `COM_STMT_EXECUTE`_ either returns:
1396
1397* a `OK packet`_
1398* a `ERR packet`_
1399* or a resultset: `Binary Protocol Resultset`_
1400
1401COM_STMT_SEND_LONG_DATA
1402-----------------------
1403
1404COM_STMT_SEND_LONG_DATA sends the data for a column. Repeating to send it, appends the data to the parameter.
1405
1406No response is sent back to the client.
1407
1408::
1409
1410 COM_STMT_SEND_LONG_DATA
1411 direction: client -> server
1412 response: none
1413
1414 payload:
1415 1 [18] COM_STMT_SEND_LONG_DATA
1416 4 statement-id
1417 2 param-id
1418 n data
1419
1420
1421COM_STMT_CLOSE
1422--------------
1423
1424a COM_STMT_CLOSE deallocates a prepared statement
1425
1426No response is sent back to the client.
1427
1428::
1429
1430 COM_STMT_CLOSE
1431 direction: client -> server
1432 response: none
1433
1434 payload:
1435 1 [19] COM_STMT_CLOSE
1436 4 statement-id
1437
1438 example:
1439 05 00 00 00 19 01 00 00 00 .........
1440
1441
1442COM_STMT_RESET
1443--------------
1444
1445a COM_STMT_RESET resets the data of a prepared statement. Useful in together with `COM_STMT_SEND_LONG_DATA`_.
1446
1447The server will send a `OK packet`_ if the statement could be reset, a `ERR packet`_ if not.
1448
1449::
1450
1451 COM_STMT_RESET
1452 direction: client -> server
1453 response: OK or ERR
1454
1455 payload:
1456 1 [1a] COM_STMT_RESET
1457 4 statement-id
1458
1459 example:
1460 05 00 00 00 1a 01 00 00 00 .........
1461
1462
1463Stored Procedures
1464=================
1465
1466In MySQL 5.0 the protocol was extended to handle:
1467
1468* `multi-resultset`_
1469* `multi-statement`_
1470
1471Multi-resultset
1472---------------
1473
1474Multi-resultsets are sent up stored procedures if more than one resultset was generated inside of it::
1475
1476 CREATE TEMPORARY TABLE ins ( id INT );
1477 DROP PROCEDURE IF EXISTS multi;
1478 DELIMITER $$
1479 CREATE PROCEDURE multi() BEGIN
1480 SELECT 1;
1481 SELECT 1;
1482 INSERT INTO ins VALUES (1);
1483 INSERT INTO ins VALUES (2);
1484 END$$
1485 DELIMITER ;
1486
1487 CALL multi();
1488 DROP TABLE ins;
1489
1490results in:
1491
1492* a resultset::
1493
1494 01 00 00 01 01 17 00 00 02 03 64 65 66 00 00 00 ..........def...
1495 01 31 00 0c 3f 00 01 00 00 00 08 81 00 00 00 00 .1..?...........
1496 05 00 00 03 fe 00 00 0a 00 02 00 00 04 01 31 05 ..............1.
1497 00 00 05 fe 00 00 0a 00 ........
1498
1499 * see the `EOF packet`_: `05 00 00 03 fe 00 00 0a 00` with its status-flag being `0a`
1500
1501* the 2nd resultset::
1502
1503 01 00 00 06 01 17 00 00 07 03 64 65 66 00 00 00 ..........def...
1504 01 31 00 0c 3f 00 01 00 00 00 08 81 00 00 00 00 .1..?...........
1505 05 00 00 08 fe 00 00 0a 00 02 00 00 09 01 31 05 ..............1.
1506 00 00 0a fe 00 00 0a 00 ........
1507
1508 * see the `EOF packet`_: `05 00 00 0a fe 00 00 0a 00` with its status-flag being `0a`
1509
1510* ... and a closing empty resultset, a `OK packet`_::
1511
1512 07 00 00 0b 00 01 00 02 00 00 00 ...........
1513
1514`SERVER_MORE_RESULTS_EXISTS`_ is set to indicate that more resultsets will follow.
1515
1516The trailing `OK packet`_ is the response to the CALL statement and contains the affected rows of
1517the last statement. In our case we INSERTed 2 rows, but only the `affected_rows` of the
1518last INSERT statement is returned as part of the `OK packet`_. If the last statement is a SELECT
1519the affected rows is 0.
1520
1521The client has to announce that it wants multi-resultsets by either setting the `CLIENT_MULTI_RESULTS`_ or
1522`CLIENT_PS_MULTI_RESULTS`_ capability.
1523
1524Multi-statement
1525---------------
1526
1527A multi-statement is allowing COM_QUERY to send more than one query to the server, separated by a ';'.
1528
1529The client has to announce that it wants multi-statements by either setting the `CLIENT_MULTI_STATEMENTS`_ capability
1530or by using `COM_SET_OPTION`_.
1531
1532COM_SET_OPTION
1533--------------
1534
1535Allows to enable and disable:
1536
1537* `CLIENT_MULTI_STATEMENTS`_
1538
1539for the current connection. The option operation is one of:
1540
1541=== =============
1542op constant name
1543=== =============
15440 MYSQL_OPTION_MULTI_STATEMENTS_ON
15451 MYSQL_OPTION_MULTI_STATEMENTS_OFF
1546=== =============
1547
1548On success it returns a `EOF packet`_ otherwise a `ERR packet`_.
1549
1550::
1551
1552 COM_SET_OPTION
1553 set options for the current connection
1554
1555 response: EOF or ERR
1556
1557 payload:
1558 1 [1b] COM_SET_OPTION
1559 2 option operation
1560
1561COM_STMT_FETCH
1562--------------
1563
1564::
1565
1566 COM_STMT_FETCH
1567
1568 response: binary rows or ERR
1569
1570 payload:
1571 1 [1c] COM_STMT_FETCH
1572 4 stmt-id
1573 4 num rows
1574
1575COM_STMT_FETCH response
1576.......................
1577
1578A fetch may result:
1579
1580* a `multi-resultset`_
1581* a `ERR packet`_
1231582
1241583
=== renamed file 'doc/scripting.txt' => 'doc/chapter/scripting.txt'
--- doc/scripting.txt 2009-06-24 15:04:59 +0000
+++ doc/chapter/scripting.txt 2010-09-27 14:20:48 +0000
@@ -1,96 +1,326 @@
1/**1Hooks
22=====
3@page page-scripting Lua Scripting3
44connect_server
5The @ref page-plugin-proxy exposes the internals of the MySQL Protocol into a scripting layer allowing you to 5--------------
6change the behaviour of the proxy with a few lines of code without having to understand all the internals.6
77read_auth
8@li @subpage page-examples8---------
9@li @subpage page-lua-classes9
1010read_auth_result
11@page page-lua-classes Classes11----------------
1212
13All structures structures are exposes as tables into Lua. If you want to know the clients username in the @c read_query() function13read_query
14you can check14----------
1515
16@verbatim16read_query_result
17 print(proxy.connection.client.username)17-----------------
18@endverbatim18
1919disconnect_client
20Please check the class-diagram below:20-----------------
2121
22@li @c r if it is readable22Modules
23@li @c w if it is writable23=======
24@li @c x if it is a function24
2525mysql.proto
26@dotfile doc/lua-classes.dot26-----------
2727
28@page page-examples Tutorial28The ``mysql.proto`` module provides encoders and decoders for the packets exchanged between client and server
2929
30@li @subpage page-examples-basic30
31@li @subpage page-examples-exectime31from_err_packet
32@li @subpage page-examples-rewrite32...............
33@li @subpage page-examples-warnings33
34@li @subpage page-examples-resultset34Decodes a ERR-packet into a table.
35@li @subpage page-examples-union35
36@li @subpage page-examples-inject36Parameters:
37@li @subpage page-examples-states37
38@li @subpage page-examples-packets38``packet``
39@li @subpage page-examples-tokenize39 (string) mysql packet
40@li @subpage page-examples-scramble40
4141
42@page page-examples-basic a Minimal Example42On success it returns a table containing:
4343
44@include examples/tutorial-basic.lua44``errmsg``
4545 (string)
4646
4747``sqlstate``
48@page page-examples-exectime Execution Time48 (string)
4949
50@include examples/tutorial-query-time.lua50``errcode``
5151 (int)
5252
53@page page-examples-rewrite Rewriting Queries53Otherwise it raises an error.
5454
55@include examples/tutorial-rewrite.lua55to_err_packet
5656.............
5757
58@page page-examples-warnings Logging all warnings58Encode a table containing a ERR packet into a MySQL packet.
5959
60@include examples/tutorial-warnings.lua60Parameters:
6161
6262``err``
63@page page-examples-resultset Creating resultsets63 (table)
6464
65@include examples/tutorial-resultset.lua65 ``errmsg``
6666 (string)
6767
68@page page-examples-union Merging resultsets68 ``sqlstate``
6969 (string)
70@include examples/tutorial-union.lua70
7171 ``errcode``
7272 (int)
73@page page-examples-inject Query Injection73
7474into a MySQL packet.
75@include examples/tutorial-inject.lua75
7676Returns a string.
7777
78@page page-examples-states States78from_ok_packet
7979..............
80@include examples/tutorial-states.lua80
8181Decodes a OK-packet
8282
83@page page-examples-packets Exploring the Packets content83``packet``
8484 (string) mysql packet
85@include examples/tutorial-packets.lua85
8686
8787On success it returns a table containing:
88@page page-examples-tokenize Tokenizing Queries88
8989``server_status``
90@include examples/tutorial-tokenize.lua90 (int) bit-mask of the connection status
9191
9292``insert_id``
93@page page-examples-scramble Using the scramble functions93 (int) last used insert id
9494
95@include examples/tutorial-scramble.lua95``warnings``
96*/96 (int) number of warnings for the last executed statement
97
98``affected_rows``
99 (int) rows affected by the last statement
100
101Otherwise it raises an error.
102
103
104to_ok_packet
105............
106
107Encode a OK packet
108
109from_eof_packet
110...............
111
112Decodes a EOF-packet
113
114Parameters:
115
116``packet``
117 (string) mysql packet
118
119
120On success it returns a table containing:
121
122``server_status``
123 (int) bit-mask of the connection status
124
125``warnings``
126 (int)
127
128Otherwise it raises an error.
129
130
131to_eof_packet
132.............
133
134from_challenge_packet
135.....................
136
137Decodes a auth-challenge-packet
138
139Parameters:
140
141``packet``
142 (string) mysql packet
143
144On success it returns a table containing:
145
146``protocol_version``
147 (int) version of the mysql protocol, usually 10
148
149``server_version``
150 (int) version of the server as integer: 50506 is MySQL 5.5.6
151
152``thread_id``
153 (int) connection id
154
155``capabilities``
156 (int) bit-mask of the server capabilities
157
158``charset``
159 (int) server default character-set
160
161``server_status``
162 (int) bit-mask of the connection-status
163
164``challenge``
165 (string) password challenge
166
167
168to_challenge_packet
169...................
170
171Encode a auth-response-packet
172
173from_response_packet
174....................
175
176Decodes a auth-response-packet
177
178Parameters:
179
180``packet``
181 (string) mysql packet
182
183
184to_response_packet
185..................
186
187from_masterinfo_string
188......................
189
190Decodes the content of the ``master.info`` file.
191
192
193to_masterinfo_string
194....................
195
196from_stmt_prepare_packet
197........................
198
199Decodes a COM_STMT_PREPARE-packet
200
201Parameters:
202
203``packet``
204 (string) mysql packet
205
206
207On success it returns a table containing:
208
209``stmt_text``
210 (string)
211 text of the prepared statement
212
213Otherwise it raises an error.
214
215from_stmt_prepare_ok_packet
216...........................
217
218Decodes a COM_STMT_PACKET OK-packet
219
220Parameters:
221
222``packet``
223 (string) mysql packet
224
225
226On success it returns a table containing:
227
228``stmt_id``
229 (int) statement-id
230
231``num_columns``
232 (int) number of columns in the resultset
233
234``num_params``
235 (int) number of parameters
236
237``warnings``
238 (int) warnings generated by the prepare statement
239
240Otherwise it raises an error.
241
242
243from_stmt_execute_packet
244........................
245
246Decodes a COM_STMT_EXECUTE-packet
247
248Parameters:
249
250``packet``
251 (string) mysql packet
252
253``num_params``
254 (int) number of parameters of the corresponding prepared statement
255
256On success it returns a table containing:
257
258``stmt_id``
259 (int) statemend-id
260
261``flags``
262 (int) flags describing the kind of cursor used
263
264``iteration_count``
265 (int) iteration count: always 1
266
267``new_params_bound``
268 (bool)
269
270``params``
271 (nil, table)
272 number-index array of parameters if ``new_params_bound`` is ``true``
273
274Each param is a table of:
275
276``type``
277 (int)
278 MYSQL_TYPE_INT, MYSQL_TYPE_STRING ... and so on
279
280``value``
281 (nil, number, string)
282 if the value is a NULL, it ``nil``
283 if it is a number (_INT, _DOUBLE, ...) it is a ``number``
284 otherwise it is a ``string``
285
286If decoding fails it raises an error.
287
288To get the ``num_params`` for this function, you have to track the track the number of parameters as returned
289by the `from_stmt_prepare_ok_packet`_. Use `stmt_id_from_stmt_execute_packet`_ to get the ``statement-id`` from
290the COM_STMT_EXECUTE packet and lookup your tracked information.
291
292stmt_id_from_stmt_execute_packet
293................................
294
295Decodes statement-id from a COM_STMT_EXECUTE-packet
296
297Parameters:
298
299``packet``
300 (string) mysql packet
301
302
303On success it returns the ``statement-id`` as ``int``.
304
305Otherwise it raises an error.
306
307from_stmt_close_packet
308......................
309
310Decodes a COM_STMT_CLOSE-packet
311
312Parameters:
313
314``packet``
315 (string) mysql packet
316
317
318On success it returns a table containing:
319
320``stmt_id``
321 (int)
322 statement-id that shall be closed
323
324Otherwise it raises an error.
325
326
97327
=== added file 'doc/protocol.txt'
--- doc/protocol.txt 1970-01-01 00:00:00 +0000
+++ doc/protocol.txt 2010-09-27 14:20:48 +0000
@@ -0,0 +1,8 @@
1==============
2MySQL Protocol
3==============
4
5.. contents::
6
7.. include:: chapter/protocol.txt
8
09
=== added file 'doc/scripting.txt'
--- doc/scripting.txt 1970-01-01 00:00:00 +0000
+++ doc/scripting.txt 2010-09-27 14:20:48 +0000
@@ -0,0 +1,8 @@
1==========================
2Scripting the proxy plugin
3==========================
4
5.. contents::
6
7.. include:: chapter/scripting.txt
8
09
=== modified file 'examples/Makefile.am'
--- examples/Makefile.am 2009-07-03 12:28:02 +0000
+++ examples/Makefile.am 2010-09-27 14:20:48 +0000
@@ -8,6 +8,7 @@
8 tutorial-monitor.lua \8 tutorial-monitor.lua \
9 tutorial-packets.lua \9 tutorial-packets.lua \
10 tutorial-query-time.lua \10 tutorial-query-time.lua \
11 tutorial-prep-stmts.lua \
11 tutorial-resultset.lua \12 tutorial-resultset.lua \
12 tutorial-rewrite.lua \13 tutorial-rewrite.lua \
13 tutorial-routing.lua \14 tutorial-routing.lua \
1415
=== added file 'examples/tutorial-prep-stmts.lua'
--- examples/tutorial-prep-stmts.lua 1970-01-01 00:00:00 +0000
+++ examples/tutorial-prep-stmts.lua 2010-09-27 14:20:48 +0000
@@ -0,0 +1,74 @@
1--[[ $%BEGINLICENSE%$
2 Copyright (c) 2007, 2008, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; version 2 of the
7 License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 02110-1301 USA
18
19 $%ENDLICENSE%$ --]]
20
21local proto = require("mysql.proto")
22
23local prep_stmts = { }
24
25function read_query( packet )
26 local cmd_type = packet:byte()
27 if cmd_type == proxy.COM_STMT_PREPARE then
28 proxy.queries:append(1, packet, { resultset_is_needed = true } )
29 return proxy.PROXY_SEND_QUERY
30 elseif cmd_type == proxy.COM_STMT_EXECUTE then
31 proxy.queries:append(2, packet, { resultset_is_needed = true } )
32 return proxy.PROXY_SEND_QUERY
33 elseif cmd_type == proxy.COM_STMT_CLOSE then
34 proxy.queries:append(3, packet, { resultset_is_needed = true } )
35 return proxy.PROXY_SEND_QUERY
36 end
37end
38
39function read_query_result(inj)
40 if inj.id == 1 then
41 -- print the query we sent
42 local stmt_prepare = assert(proto.from_stmt_prepare_packet(inj.query))
43 print(("> PREPARE: %s"):format(stmt_prepare.stmt_text))
44
45 -- and the stmt-id we got for it
46 if inj.resultset.raw:byte() == 0 then
47 local stmt_prepare_ok = assert(proto.from_stmt_prepare_ok_packet(inj.resultset.raw))
48 print(("< PREPARE: stmt-id = %d (resultset-cols = %d, params = %d)"):format(
49 stmt_prepare_ok.stmt_id,
50 stmt_prepare_ok.num_columns,
51 stmt_prepare_ok.num_params))
52
53 prep_stmts[stmt_prepare_ok.stmt_id] = {
54 num_columns = stmt_prepare_ok.num_columns,
55 num_params = stmt_prepare_ok.num_params,
56 }
57 end
58 elseif inj.id == 2 then
59 local stmt_id = assert(proto.stmt_id_from_stmt_execute_packet(inj.query))
60 local stmt_execute = assert(proto.from_stmt_execute_packet(inj.query, prep_stmts[stmt_id].num_params))
61 print(("> EXECUTE: stmt-id = %d"):format(stmt_execute.stmt_id))
62 if stmt_execute.new_params_bound then
63 for ndx, v in ipairs(stmt_execute.params) do
64 print((" [%d] %s (type = %d)"):format(ndx, tostring(v.value), v.type))
65 end
66 end
67 elseif inj.id == 3 then
68 local stmt_close = assert(proto.from_stmt_close_packet(inj.query))
69 print(("> CLOSE: stmt-id = %d"):format(stmt_close.stmt_id))
70
71 prep_stmts[stmt_close.stmt_id] = nil -- cleanup
72 end
73end
74
075
=== modified file 'lib/mysql-proto.c'
--- lib/mysql-proto.c 2010-04-06 14:26:51 +0000
+++ lib/mysql-proto.c 2010-09-27 14:20:48 +0000
@@ -30,6 +30,7 @@
3030
31#include "network-mysqld-proto.h"31#include "network-mysqld-proto.h"
32#include "network-mysqld-packet.h"32#include "network-mysqld-packet.h"
33#include "network_mysqld_type.h"
33#include "network-mysqld-masterinfo.h"34#include "network-mysqld-masterinfo.h"
34#include "glib-ext.h"35#include "glib-ext.h"
35#include "lua-env.h"36#include "lua-env.h"
@@ -57,6 +58,10 @@
57 lua_pushinteger(L, x->y); \58 lua_pushinteger(L, x->y); \
58 lua_setfield(L, -2, G_STRINGIFY(y)); 59 lua_setfield(L, -2, G_STRINGIFY(y));
5960
61#define LUA_EXPORT_BOOL(x, y) \
62 lua_pushboolean(L, x->y); \
63 lua_setfield(L, -2, G_STRINGIFY(y));
64
60#define LUA_EXPORT_STR(x, y) \65#define LUA_EXPORT_STR(x, y) \
61 if (x->y->len) { \66 if (x->y->len) { \
62 lua_pushlstring(L, S(x->y)); \67 lua_pushlstring(L, S(x->y)); \
@@ -466,6 +471,266 @@
466 return 1;471 return 1;
467}472}
468473
474static int lua_proto_get_stmt_prepare_packet (lua_State *L) {
475 size_t packet_len;
476 const char *packet_str = luaL_checklstring(L, 1, &packet_len);
477 network_mysqld_stmt_prepare_packet_t *cmd;
478 network_packet packet;
479 GString s;
480 int err = 0;
481
482 s.str = (char *)packet_str;
483 s.len = packet_len;
484
485 packet.data = &s;
486 packet.offset = 0;
487
488 cmd = network_mysqld_stmt_prepare_packet_new();
489
490 err = err || network_mysqld_proto_get_stmt_prepare_packet(&packet, cmd);
491 if (err) {
492 network_mysqld_stmt_prepare_packet_free(cmd);
493
494 luaL_error(L, "%s: network_mysqld_proto_get_stmt_prepare_packet() failed", G_STRLOC);
495 return 0;
496 }
497
498 lua_newtable(L);
499
500 LUA_EXPORT_STR(cmd, stmt_text);
501
502 network_mysqld_stmt_prepare_packet_free(cmd);
503
504 return 1;
505}
506
507/**
508 * transform the OK packet of a COM_STMT_PREPARE result into a table
509 */
510static int lua_proto_get_stmt_prepare_ok_packet (lua_State *L) {
511 size_t packet_len;
512 const char *packet_str = luaL_checklstring(L, 1, &packet_len);
513 network_mysqld_stmt_prepare_ok_packet_t *cmd;
514 network_packet packet;
515 GString s;
516 int err = 0;
517
518 s.str = (char *)packet_str;
519 s.len = packet_len;
520
521 packet.data = &s;
522 packet.offset = 0;
523
524 cmd = network_mysqld_stmt_prepare_ok_packet_new();
525
526 err = err || network_mysqld_proto_get_stmt_prepare_ok_packet(&packet, cmd);
527 if (err) {
528 network_mysqld_stmt_prepare_ok_packet_free(cmd);
529
530 luaL_error(L, "%s: network_mysqld_proto_get_stmt_prepare_ok_packet() failed", G_STRLOC);
531 return 0;
532 }
533
534 lua_newtable(L);
535
536 LUA_EXPORT_INT(cmd, stmt_id);
537 LUA_EXPORT_INT(cmd, num_columns);
538 LUA_EXPORT_INT(cmd, num_params);
539 LUA_EXPORT_INT(cmd, warnings);
540
541 network_mysqld_stmt_prepare_ok_packet_free(cmd);
542
543 return 1;
544}
545
546/**
547 * get the stmt-id from the com-stmt-execute packet
548 */
549static int lua_proto_get_stmt_execute_packet (lua_State *L) {
550 size_t packet_len;
551 const char *packet_str = luaL_checklstring(L, 1, &packet_len);
552 int param_count = luaL_checkint(L, 2);
553 network_mysqld_stmt_execute_packet_t *cmd;
554 network_packet packet;
555 GString s;
556 int err = 0;
557
558 s.str = (char *)packet_str;
559 s.len = packet_len;
560
561 packet.data = &s;
562 packet.offset = 0;
563
564 cmd = network_mysqld_stmt_execute_packet_new();
565
566 err = err || network_mysqld_proto_get_stmt_execute_packet(&packet, cmd, param_count);
567 if (err) {
568 network_mysqld_stmt_execute_packet_free(cmd);
569
570 luaL_error(L, "%s: network_mysqld_proto_get_stmt_execute_packet() failed", G_STRLOC);
571 return 0;
572 }
573
574 lua_newtable(L);
575
576 LUA_EXPORT_INT(cmd, stmt_id);
577 LUA_EXPORT_INT(cmd, flags);
578 LUA_EXPORT_INT(cmd, iteration_count);
579 LUA_EXPORT_BOOL(cmd, new_params_bound);
580
581 if (cmd->new_params_bound) {
582 guint i;
583
584 lua_newtable(L);
585 for (i = 0; i < cmd->params->len; i++) {
586 network_mysqld_type_t *param = g_ptr_array_index(cmd->params, i);
587
588 lua_newtable(L);
589 lua_pushnumber(L, param->type);
590 lua_setfield(L, -2, "type");
591
592 if (param->is_null) {
593 lua_pushnil(L);
594 } else {
595 const char *const_s;
596 char *_s;
597 gsize s_len;
598 guint64 _i;
599 gboolean is_unsigned;
600 double d;
601
602 switch (param->type) {
603 case MYSQL_TYPE_BLOB:
604 case MYSQL_TYPE_MEDIUM_BLOB:
605 case MYSQL_TYPE_LONG_BLOB:
606 case MYSQL_TYPE_STRING:
607 case MYSQL_TYPE_VARCHAR:
608 case MYSQL_TYPE_VAR_STRING:
609 if (0 != network_mysqld_type_get_string_const(param, &const_s, &s_len)) {
610 return luaL_error(L, "%s: _get_string_const() failed for type = %d",
611 G_STRLOC,
612 param->type);
613 }
614
615 lua_pushlstring(L, const_s, s_len);
616 break;
617 case MYSQL_TYPE_TINY:
618 case MYSQL_TYPE_SHORT:
619 case MYSQL_TYPE_LONG:
620 case MYSQL_TYPE_LONGLONG:
621 if (0 != network_mysqld_type_get_int(param, &_i, &is_unsigned)) {
622 return luaL_error(L, "%s: _get_int() failed for type = %d",
623 G_STRLOC,
624 param->type);
625 }
626
627 lua_pushinteger(L, _i);
628 break;
629 case MYSQL_TYPE_DOUBLE:
630 case MYSQL_TYPE_FLOAT:
631 if (0 != network_mysqld_type_get_double(param, &d)) {
632 return luaL_error(L, "%s: _get_double() failed for type = %d",
633 G_STRLOC,
634 param->type);
635 }
636
637 lua_pushnumber(L, d);
638 break;
639 case MYSQL_TYPE_DATETIME:
640 case MYSQL_TYPE_TIMESTAMP:
641 case MYSQL_TYPE_DATE:
642 case MYSQL_TYPE_TIME:
643 _s = NULL;
644 s_len = 0;
645
646 if (0 != network_mysqld_type_get_string(param, &_s, &s_len)) {
647 return luaL_error(L, "%s: _get_string() failed for type = %d",
648 G_STRLOC,
649 param->type);
650 }
651
652 lua_pushlstring(L, _s, s_len);
653
654 if (NULL != _s) g_free(_s);
655 break;
656 default:
657 luaL_error(L, "%s: can't decode type %d yet",
658 G_STRLOC,
659 param->type); /* we don't have that value yet */
660 break;
661 }
662 }
663 lua_setfield(L, -2, "value");
664 lua_rawseti(L, -2, i + 1);
665 }
666 lua_setfield(L, -2, "params");
667 }
668
669 network_mysqld_stmt_execute_packet_free(cmd);
670
671 return 1;
672}
673
674static int lua_proto_get_stmt_execute_packet_stmt_id (lua_State *L) {
675 size_t packet_len;
676 const char *packet_str = luaL_checklstring(L, 1, &packet_len);
677 network_packet packet;
678 GString s;
679 int err = 0;
680 guint32 stmt_id;
681
682 s.str = (char *)packet_str;
683 s.len = packet_len;
684
685 packet.data = &s;
686 packet.offset = 0;
687
688 err = err || network_mysqld_proto_get_stmt_execute_packet_stmt_id(&packet, &stmt_id);
689 if (err) {
690 luaL_error(L, "%s: network_mysqld_proto_get_stmt_execute_packet_stmt_id() failed", G_STRLOC);
691 return 0;
692 }
693
694 lua_pushinteger(L, stmt_id);
695
696 return 1;
697}
698
699
700static int lua_proto_get_stmt_close_packet (lua_State *L) {
701 size_t packet_len;
702 const char *packet_str = luaL_checklstring(L, 1, &packet_len);
703 network_mysqld_stmt_close_packet_t *cmd;
704 network_packet packet;
705 GString s;
706 int err = 0;
707
708 s.str = (char *)packet_str;
709 s.len = packet_len;
710
711 packet.data = &s;
712 packet.offset = 0;
713
714 cmd = network_mysqld_stmt_close_packet_new();
715
716 err = err || network_mysqld_proto_get_stmt_close_packet(&packet, cmd);
717 if (err) {
718 network_mysqld_stmt_close_packet_free(cmd);
719
720 luaL_error(L, "%s: network_mysqld_proto_get_stmt_close_packet() failed", G_STRLOC);
721 return 0;
722 }
723
724 lua_newtable(L);
725
726 LUA_EXPORT_INT(cmd, stmt_id);
727
728 network_mysqld_stmt_close_packet_free(cmd);
729
730 return 1;
731}
732
733
469734
470735
471/*736/*
@@ -497,6 +762,11 @@
497 {"to_response_packet", lua_proto_append_response_packet},762 {"to_response_packet", lua_proto_append_response_packet},
498 {"from_masterinfo_string", lua_proto_get_masterinfo_string},763 {"from_masterinfo_string", lua_proto_get_masterinfo_string},
499 {"to_masterinfo_string", lua_proto_append_masterinfo_string},764 {"to_masterinfo_string", lua_proto_append_masterinfo_string},
765 {"from_stmt_prepare_packet", lua_proto_get_stmt_prepare_packet},
766 {"from_stmt_prepare_ok_packet", lua_proto_get_stmt_prepare_ok_packet},
767 {"from_stmt_execute_packet", lua_proto_get_stmt_execute_packet},
768 {"stmt_id_from_stmt_execute_packet", lua_proto_get_stmt_execute_packet_stmt_id},
769 {"from_stmt_close_packet", lua_proto_get_stmt_close_packet},
500 {NULL, NULL},770 {NULL, NULL},
501};771};
502772
503773
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2010-04-06 15:18:47 +0000
+++ src/CMakeLists.txt 2010-09-27 14:20:48 +0000
@@ -69,6 +69,8 @@
69 network-mysqld.c 69 network-mysqld.c
70 network-mysqld-lua.c 70 network-mysqld-lua.c
71 network-mysqld-proto.c 71 network-mysqld-proto.c
72 network_mysqld_type.c
73 network_mysqld_proto_binary.c
72 network-mysqld-binlog.c 74 network-mysqld-binlog.c
73 network-mysqld-packet.c 75 network-mysqld-packet.c
74 network-mysqld-masterinfo.c 76 network-mysqld-masterinfo.c
@@ -206,6 +208,8 @@
206 network-mysqld.h208 network-mysqld.h
207 network-mysqld-lua.h209 network-mysqld-lua.h
208 network-mysqld-proto.h210 network-mysqld-proto.h
211 network_mysqld_type.h
212 network_mysqld_proto_binary.h
209 network-mysqld-binlog.h213 network-mysqld-binlog.h
210 network-mysqld-packet.h214 network-mysqld-packet.h
211 network-mysqld-masterinfo.h215 network-mysqld-masterinfo.h
212216
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2010-05-03 14:26:32 +0000
+++ src/Makefile.am 2010-09-27 14:20:48 +0000
@@ -109,6 +109,8 @@
109 network-mysqld-proto.c \109 network-mysqld-proto.c \
110 network-mysqld-binlog.c \110 network-mysqld-binlog.c \
111 network-mysqld-packet.c \111 network-mysqld-packet.c \
112 network_mysqld_type.c \
113 network_mysqld_proto_binary.c \
112 network-mysqld-masterinfo.c \114 network-mysqld-masterinfo.c \
113 network-conn-pool.c \115 network-conn-pool.c \
114 network-conn-pool-lua.c \116 network-conn-pool-lua.c \
@@ -137,6 +139,8 @@
137 network-mysqld-proto.h \139 network-mysqld-proto.h \
138 network-mysqld-binlog.h \140 network-mysqld-binlog.h \
139 network-mysqld-packet.h \141 network-mysqld-packet.h \
142 network_mysqld_type.h \
143 network_mysqld_proto_binary.h \
140 network-mysqld-masterinfo.h \144 network-mysqld-masterinfo.h \
141 network-conn-pool.h \145 network-conn-pool.h \
142 network-conn-pool-lua.h \146 network-conn-pool-lua.h \
143147
=== modified file 'src/network-mysqld-packet.c'
--- src/network-mysqld-packet.c 2010-05-10 10:31:45 +0000
+++ src/network-mysqld-packet.c 2010-09-27 14:20:48 +0000
@@ -23,8 +23,11 @@
2323
24#include <stdlib.h>24#include <stdlib.h>
25#include <stdio.h>25#include <stdio.h>
26#include <string.h>
2627
27#include "network-mysqld-packet.h"28#include "network-mysqld-packet.h"
29#include "network_mysqld_type.h"
30#include "network_mysqld_proto_binary.h"
2831
29#include "glib-ext.h"32#include "glib-ext.h"
3033
@@ -668,6 +671,57 @@
668 return is_finished;671 return is_finished;
669}672}
670673
674int network_mysqld_proto_get_fielddef(network_packet *packet, network_mysqld_proto_fielddef_t *field, guint32 capabilities) {
675 int err = 0;
676
677 if (capabilities & CLIENT_PROTOCOL_41) {
678 err = err || network_mysqld_proto_get_lenenc_string(packet, &field->catalog, NULL);
679 err = err || network_mysqld_proto_get_lenenc_string(packet, &field->db, NULL);
680 err = err || network_mysqld_proto_get_lenenc_string(packet, &field->table, NULL);
681 err = err || network_mysqld_proto_get_lenenc_string(packet, &field->org_table, NULL);
682 err = err || network_mysqld_proto_get_lenenc_string(packet, &field->name, NULL);
683 err = err || network_mysqld_proto_get_lenenc_string(packet, &field->org_name, NULL);
684
685 err = err || network_mysqld_proto_skip(packet, 1); /* filler */
686
687 err = err || network_mysqld_proto_get_int16(packet, (guint16 *)&field->charsetnr);
688 err = err || network_mysqld_proto_get_int32(packet, (guint32 *)&field->length);
689 err = err || network_mysqld_proto_get_int8(packet, (guint8 *)&field->type);
690 err = err || network_mysqld_proto_get_int16(packet, (guint16 *)&field->flags);
691 err = err || network_mysqld_proto_get_int8(packet, (guint8 *)&field->decimals);
692
693 err = err || network_mysqld_proto_skip(packet, 2); /* filler */
694 } else {
695 guint8 len;
696
697 /* see protocol.cc Protocol::send_fields */
698
699 err = err || network_mysqld_proto_get_lenenc_string(packet, &field->table, NULL);
700 err = err || network_mysqld_proto_get_lenenc_string(packet, &field->name, NULL);
701 err = err || network_mysqld_proto_get_int8(packet, &len);
702 err = err || (len != 3);
703 err = err || network_mysqld_proto_get_int24(packet, (guint32 *)&field->length);
704 err = err || network_mysqld_proto_get_int8(packet, &len);
705 err = err || (len != 1);
706 err = err || network_mysqld_proto_get_int8(packet, (guint8 *)&field->type);
707 err = err || network_mysqld_proto_get_int8(packet, &len);
708 if (len == 3) { /* the CLIENT_LONG_FLAG is set */
709 err = err || network_mysqld_proto_get_int16(packet, (guint16 *)&field->flags);
710 err = err || network_mysqld_proto_get_int8(packet, (guint8 *)&field->decimals);
711 } else if (len == 2) {
712 guint8 flags = 0;
713 err = err || network_mysqld_proto_get_int8(packet, &flags);
714 err = err || network_mysqld_proto_get_int8(packet, (guint8 *)&field->decimals);
715
716 if (!err) field->flags = flags;
717 } else {
718 /* well */
719 }
720 }
721
722 return err ? -1 : 0;
723}
724
671/**725/**
672 * parse the result-set packet and extract the fields726 * parse the result-set packet and extract the fields
673 *727 *
@@ -714,7 +768,7 @@
714 768
715 /* the next chunk, the field-def */769 /* the next chunk, the field-def */
716 for (i = 0; i < field_count; i++) {770 for (i = 0; i < field_count; i++) {
717 MYSQL_FIELD *field;771 network_mysqld_proto_fielddef_t *field;
718 772
719 chunk = chunk->next;773 chunk = chunk->next;
720 g_assert(chunk);774 g_assert(chunk);
@@ -722,56 +776,11 @@
722 packet.data = chunk->data;776 packet.data = chunk->data;
723 packet.offset = 0;777 packet.offset = 0;
724778
779 field = network_mysqld_proto_fielddef_new();
780
725 err = err || network_mysqld_proto_skip_network_header(&packet);781 err = err || network_mysqld_proto_skip_network_header(&packet);
726 782 err = err || network_mysqld_proto_get_fielddef(&packet, field, capabilities);
727 field = network_mysqld_proto_fielddef_new();783
728
729 if (capabilities & CLIENT_PROTOCOL_41) {
730 err = err || network_mysqld_proto_get_lenenc_string(&packet, &field->catalog, NULL);
731 err = err || network_mysqld_proto_get_lenenc_string(&packet, &field->db, NULL);
732 err = err || network_mysqld_proto_get_lenenc_string(&packet, &field->table, NULL);
733 err = err || network_mysqld_proto_get_lenenc_string(&packet, &field->org_table, NULL);
734 err = err || network_mysqld_proto_get_lenenc_string(&packet, &field->name, NULL);
735 err = err || network_mysqld_proto_get_lenenc_string(&packet, &field->org_name, NULL);
736
737 err = err || network_mysqld_proto_skip(&packet, 1); /* filler */
738
739 err = err || network_mysqld_proto_get_int16(&packet, (guint16 *)&field->charsetnr);
740 err = err || network_mysqld_proto_get_int32(&packet, (guint32 *)&field->length);
741 err = err || network_mysqld_proto_get_int8(&packet, (guint8 *)&field->type);
742 err = err || network_mysqld_proto_get_int16(&packet, (guint16 *)&field->flags);
743 err = err || network_mysqld_proto_get_int8(&packet, (guint8 *)&field->decimals);
744
745 err = err || network_mysqld_proto_skip(&packet, 2); /* filler */
746 } else {
747 guint8 len;
748
749 /* see protocol.cc Protocol::send_fields */
750
751 err = err || network_mysqld_proto_get_lenenc_string(&packet, &field->table, NULL);
752 err = err || network_mysqld_proto_get_lenenc_string(&packet, &field->name, NULL);
753 err = err || network_mysqld_proto_get_int8(&packet, &len);
754 err = err || (len != 3);
755 err = err || network_mysqld_proto_get_int24(&packet, (guint32 *)&field->length);
756 err = err || network_mysqld_proto_get_int8(&packet, &len);
757 err = err || (len != 1);
758 err = err || network_mysqld_proto_get_int8(&packet, (guint8 *)&field->type);
759 err = err || network_mysqld_proto_get_int8(&packet, &len);
760 if (len == 3) { /* the CLIENT_LONG_FLAG is set */
761 err = err || network_mysqld_proto_get_int16(&packet, (guint16 *)&field->flags);
762 err = err || network_mysqld_proto_get_int8(&packet, (guint8 *)&field->decimals);
763 } else if (len == 2) {
764 guint8 flags = 0;
765 err = err || network_mysqld_proto_get_int8(&packet, &flags);
766 err = err || network_mysqld_proto_get_int8(&packet, (guint8 *)&field->decimals);
767
768 if (!err) field->flags = flags;
769 } else {
770 /* well */
771 }
772
773 }
774
775 g_ptr_array_add(fields, field); /* even if we had an error, append it so that we can free it later */784 g_ptr_array_add(fields, field); /* even if we had an error, append it so that we can free it later */
776785
777 if (err) return NULL;786 if (err) return NULL;
@@ -1358,4 +1367,451 @@
1358 return dst;1367 return dst;
1359}1368}
13601369
1370/*
1371 * prepared statements
1372 */
1373
1374/**
1375 *
1376 */
1377network_mysqld_stmt_prepare_packet_t *network_mysqld_stmt_prepare_packet_new() {
1378 network_mysqld_stmt_prepare_packet_t *stmt_prepare_packet;
1379
1380 stmt_prepare_packet = g_slice_new0(network_mysqld_stmt_prepare_packet_t);
1381 stmt_prepare_packet->stmt_text = g_string_new(NULL);
1382
1383 return stmt_prepare_packet;
1384}
1385
1386/**
1387 *
1388 */
1389void network_mysqld_stmt_prepare_packet_free(network_mysqld_stmt_prepare_packet_t *stmt_prepare_packet) {
1390 if (NULL == stmt_prepare_packet) return;
1391
1392 if (NULL != stmt_prepare_packet->stmt_text) g_string_free(stmt_prepare_packet->stmt_text, TRUE);
1393
1394 g_slice_free(network_mysqld_stmt_prepare_packet_t, stmt_prepare_packet);
1395}
1396
1397/**
1398 *
1399 */
1400int network_mysqld_proto_get_stmt_prepare_packet(network_packet *packet, network_mysqld_stmt_prepare_packet_t *stmt_prepare_packet) {
1401 guint8 packet_type;
1402
1403 int err = 0;
1404
1405 err = err || network_mysqld_proto_get_int8(packet, &packet_type);
1406 if (err) return -1;
1407
1408 if (COM_STMT_PREPARE != packet_type) {
1409 g_critical("%s: expected the first byte to be %02x, got %02x",
1410 G_STRLOC,
1411 COM_STMT_PREPARE,
1412 packet_type);
1413 return -1;
1414 }
1415
1416 g_string_assign_len(stmt_prepare_packet->stmt_text, packet->data->str + packet->offset, packet->data->len - packet->offset);
1417
1418 return err ? -1 : 0;
1419}
1420
1421int network_mysqld_proto_append_stmt_prepare_packet(GString *packet, network_mysqld_stmt_prepare_packet_t *stmt_prepare_packet) {
1422 network_mysqld_proto_append_int8(packet, COM_STMT_PREPARE);
1423 g_string_append_len(packet, S(stmt_prepare_packet->stmt_text));
1424
1425 return 0;
1426}
1427
1428/**
1429 *
1430 */
1431network_mysqld_stmt_prepare_ok_packet_t *network_mysqld_stmt_prepare_ok_packet_new() {
1432 network_mysqld_stmt_prepare_ok_packet_t *stmt_prepare_ok_packet;
1433
1434 stmt_prepare_ok_packet = g_slice_new0(network_mysqld_stmt_prepare_ok_packet_t);
1435
1436 return stmt_prepare_ok_packet;
1437}
1438
1439/**
1440 *
1441 */
1442void network_mysqld_stmt_prepare_ok_packet_free(network_mysqld_stmt_prepare_ok_packet_t *stmt_prepare_ok_packet) {
1443 if (NULL == stmt_prepare_ok_packet) return;
1444
1445 g_slice_free(network_mysqld_stmt_prepare_ok_packet_t, stmt_prepare_ok_packet);
1446}
1447
1448/**
1449 * parse the first packet of the OK response for a COM_STMT_PREPARE
1450 *
1451 * it is followed by the field defs for the params and the columns and their EOF packets which is handled elsewhere
1452 */
1453int network_mysqld_proto_get_stmt_prepare_ok_packet(network_packet *packet, network_mysqld_stmt_prepare_ok_packet_t *stmt_prepare_ok_packet) {
1454 guint8 packet_type;
1455 guint16 num_columns;
1456 guint16 num_params;
1457 guint16 warnings;
1458 guint32 stmt_id;
1459
1460 int err = 0;
1461
1462 err = err || network_mysqld_proto_get_int8(packet, &packet_type);
1463 if (err) return -1;
1464
1465 if (0x00 != packet_type) {
1466 g_critical("%s: expected the first byte to be %02x, got %02x",
1467 G_STRLOC,
1468 0x00,
1469 packet_type);
1470 return -1;
1471 }
1472 err = err || network_mysqld_proto_get_int32(packet, &stmt_id);
1473 err = err || network_mysqld_proto_get_int16(packet, &num_columns);
1474 err = err || network_mysqld_proto_get_int16(packet, &num_params);
1475 err = err || network_mysqld_proto_skip(packet, 1); /* the filler */
1476 err = err || network_mysqld_proto_get_int16(packet, &warnings);
1477
1478 if (!err) {
1479 stmt_prepare_ok_packet->stmt_id = stmt_id;
1480 stmt_prepare_ok_packet->num_columns = num_columns;
1481 stmt_prepare_ok_packet->num_params = num_params;
1482 stmt_prepare_ok_packet->warnings = warnings;
1483 }
1484
1485 return err ? -1 : 0;
1486}
1487
1488int network_mysqld_proto_append_stmt_prepare_ok_packet(GString *packet, network_mysqld_stmt_prepare_ok_packet_t *stmt_prepare_ok_packet) {
1489 int err = 0;
1490
1491 err = err || network_mysqld_proto_append_int8(packet, MYSQLD_PACKET_OK);
1492 err = err || network_mysqld_proto_append_int32(packet, stmt_prepare_ok_packet->stmt_id);
1493 err = err || network_mysqld_proto_append_int16(packet, stmt_prepare_ok_packet->num_columns);
1494 err = err || network_mysqld_proto_append_int16(packet, stmt_prepare_ok_packet->num_params);
1495 err = err || network_mysqld_proto_append_int8(packet, 0x00);
1496 err = err || network_mysqld_proto_append_int16(packet, stmt_prepare_ok_packet->warnings);
1497
1498 return err ? -1 : 0;
1499}
1500
1501/**
1502 * create a struct for a COM_STMT_EXECUTE packet
1503 */
1504network_mysqld_stmt_execute_packet_t *network_mysqld_stmt_execute_packet_new() {
1505 network_mysqld_stmt_execute_packet_t *stmt_execute_packet;
1506
1507 stmt_execute_packet = g_slice_new0(network_mysqld_stmt_execute_packet_t);
1508 stmt_execute_packet->params = g_ptr_array_new();
1509
1510 return stmt_execute_packet;
1511}
1512
1513/**
1514 * free a struct for a COM_STMT_EXECUTE packet
1515 */
1516void network_mysqld_stmt_execute_packet_free(network_mysqld_stmt_execute_packet_t *stmt_execute_packet) {
1517 guint i;
1518
1519 if (NULL == stmt_execute_packet) return;
1520
1521 for (i = 0; i < stmt_execute_packet->params->len; i++) {
1522 network_mysqld_type_t *param = g_ptr_array_index(stmt_execute_packet->params, i);
1523
1524 network_mysqld_type_free(param);
1525 }
1526
1527 g_ptr_array_free(stmt_execute_packet->params, TRUE);
1528
1529 g_slice_free(network_mysqld_stmt_execute_packet_t, stmt_execute_packet);
1530}
1531
1532/**
1533 * get the statement-id from the COM_STMT_EXECUTE packet
1534 *
1535 * as network_mysqld_proto_get_stmt_execute_packet() needs the parameter count
1536 * to calculate the number of null-bits, we need a way to look it up in a
1537 * external store which is very likely indexed by the stmt-id
1538 *
1539 * @see network_mysqld_proto_get_stmt_execute_packet()
1540 */
1541int network_mysqld_proto_get_stmt_execute_packet_stmt_id(network_packet *packet,
1542 guint32 *stmt_id) {
1543 guint8 packet_type;
1544 int err = 0;
1545
1546 err = err || network_mysqld_proto_get_int8(packet, &packet_type);
1547 if (err) return -1;
1548
1549 if (COM_STMT_EXECUTE != packet_type) {
1550 g_critical("%s: expected the first byte to be %02x, got %02x",
1551 G_STRLOC,
1552 COM_STMT_EXECUTE,
1553 packet_type);
1554 return -1;
1555 }
1556
1557 err = err || network_mysqld_proto_get_int32(packet, stmt_id);
1558
1559 return err ? -1 : 0;
1560}
1561
1562/**
1563 *
1564 * param_count has to be taken from the response of the prepare-stmt-ok packet
1565 *
1566 * @param param_count number of parameters that we expect to see here
1567 */
1568int network_mysqld_proto_get_stmt_execute_packet(network_packet *packet,
1569 network_mysqld_stmt_execute_packet_t *stmt_execute_packet,
1570 guint param_count) {
1571 int err = 0;
1572 GString *nul_bits;
1573 gsize nul_bits_len;
1574
1575 err = err || network_mysqld_proto_get_stmt_execute_packet_stmt_id(packet, &stmt_execute_packet->stmt_id);
1576 err = err || network_mysqld_proto_get_int8(packet, &stmt_execute_packet->flags);
1577 err = err || network_mysqld_proto_get_int32(packet, &stmt_execute_packet->iteration_count);
1578
1579 if (0 == param_count) {
1580 return err ? -1 : 0;
1581 }
1582
1583 nul_bits_len = (param_count + 7) / 8;
1584 nul_bits = g_string_sized_new(nul_bits_len);
1585 err = err || network_mysqld_proto_get_gstring_len(packet, nul_bits_len, nul_bits);
1586 err = err || network_mysqld_proto_get_int8(packet, &stmt_execute_packet->new_params_bound);
1587
1588 if (0 != err) {
1589 g_string_free(nul_bits, TRUE);
1590
1591 return -1; /* exit early if something failed up to now */
1592 }
1593
1594 if (stmt_execute_packet->new_params_bound) {
1595 guint i;
1596
1597 for (i = 0; 0 == err && i < param_count; i++) {
1598 guint16 param_type;
1599
1600 err = err || network_mysqld_proto_get_int16(packet, &param_type);
1601
1602 if (0 == err) {
1603 network_mysqld_type_t *param;
1604
1605 param = network_mysqld_type_new(param_type & 0xff);
1606 if (NULL == param) {
1607 g_critical("%s: couldn't create type = %d", G_STRLOC, param_type & 0xff);
1608
1609 err = -1;
1610 break;
1611 }
1612 param->is_null = (nul_bits->str[i / 8] & (1 << (i % 8))) != 0;
1613 param->is_unsigned = (param_type & 0x8000) != 0;
1614
1615 g_ptr_array_add(stmt_execute_packet->params, param);
1616 }
1617 }
1618
1619 for (i = 0; 0 == err && i < param_count; i++) {
1620 network_mysqld_type_t *param = g_ptr_array_index(stmt_execute_packet->params, i);
1621
1622 if (!param->is_null) {
1623 err = err || network_mysqld_proto_binary_get_type(packet, param);
1624 }
1625 }
1626 }
1627
1628 g_string_free(nul_bits, TRUE);
1629
1630 return err ? -1 : 0;
1631}
1632
1633int network_mysqld_proto_append_stmt_execute_packet(GString *packet,
1634 network_mysqld_stmt_execute_packet_t *stmt_execute_packet,
1635 guint param_count) {
1636 gsize nul_bits_len;
1637 GString *nul_bits;
1638 guint i;
1639 int err = 0;
1640
1641 nul_bits_len = (param_count + 7) / 8;
1642 nul_bits = g_string_sized_new(nul_bits_len);
1643 memset(nul_bits->str, 0, nul_bits->len); /* set it all to zero */
1644
1645 for (i = 0; i < param_count; i++) {
1646 network_mysqld_type_t *param = g_ptr_array_index(stmt_execute_packet->params, i);
1647
1648 if (param->is_null) {
1649 nul_bits->str[i / 8] |= 1 << (i % 8);
1650 }
1651 }
1652
1653 network_mysqld_proto_append_int8(packet, COM_STMT_EXECUTE);
1654 network_mysqld_proto_append_int32(packet, stmt_execute_packet->stmt_id);
1655 network_mysqld_proto_append_int8(packet, stmt_execute_packet->flags);
1656 network_mysqld_proto_append_int32(packet, stmt_execute_packet->iteration_count);
1657 g_string_append_len(packet, S(nul_bits));
1658 network_mysqld_proto_append_int8(packet, stmt_execute_packet->new_params_bound);
1659
1660 if (stmt_execute_packet->new_params_bound) {
1661 for (i = 0; i < stmt_execute_packet->params->len; i++) {
1662 network_mysqld_type_t *param = g_ptr_array_index(stmt_execute_packet->params, i);
1663
1664 network_mysqld_proto_append_int16(packet, (guint16)param->type);
1665 }
1666 for (i = 0; 0 == err && i < stmt_execute_packet->params->len; i++) {
1667 network_mysqld_type_t *param = g_ptr_array_index(stmt_execute_packet->params, i);
1668
1669 if (!param->is_null) {
1670 err = err || network_mysqld_proto_binary_append_type(packet, param);
1671 }
1672 }
1673 }
1674
1675 return err ? -1 : 0;
1676}
1677
1678/**
1679 * create a struct for a COM_STMT_EXECUTE resultset row
1680 */
1681network_mysqld_resultset_row_t *network_mysqld_resultset_row_new() {
1682 return g_ptr_array_new();
1683}
1684
1685/**
1686 * free a struct for a COM_STMT_EXECUTE resultset row
1687 */
1688void network_mysqld_resultset_row_free(network_mysqld_resultset_row_t *row) {
1689 guint i;
1690
1691 if (NULL == row) return;
1692
1693 for (i = 0; i < row->len; i++) {
1694 network_mysqld_type_t *field = g_ptr_array_index(row, i);
1695
1696 network_mysqld_type_free(field);
1697 }
1698
1699 g_ptr_array_free(row, TRUE);
1700}
1701
1702/**
1703 * get the fields of a row that is in binary row format
1704 */
1705int network_mysqld_proto_get_binary_row(network_packet *packet, network_mysqld_proto_fielddefs_t *coldefs, network_mysqld_resultset_row_t *row) {
1706 int err = 0;
1707 guint i;
1708 guint nul_bytes_len;
1709 GString *nul_bytes;
1710
1711 err = err || network_mysqld_proto_skip(packet, 1); /* the packet header which seems to be always 0 */
1712
1713 nul_bytes_len = (coldefs->len + 7 + 2) / 8; /* the first 2 bits are reserved */
1714 nul_bytes = g_string_sized_new(nul_bytes_len);
1715 err = err || network_mysqld_proto_get_gstring_len(packet, nul_bytes_len, nul_bytes);
1716
1717 for (i = 0; 0 == err && i < coldefs->len; i++) {
1718 network_mysqld_type_t *param;
1719 network_mysqld_proto_fielddef_t *coldef = g_ptr_array_index(coldefs, i);
1720
1721 param = network_mysqld_type_new(coldef->type);
1722 if (NULL == param) {
1723 g_critical("%s: coulnd't create type = %d", G_STRLOC, coldef->type);
1724
1725 err = -1;
1726 break;
1727 }
1728
1729 if (nul_bytes->str[(i + 2) / 8] & (1 << ((i + 2) % 8))) {
1730 param->is_null = TRUE;
1731 } else {
1732 err = err || network_mysqld_proto_binary_get_type(packet, param);
1733 }
1734
1735 g_ptr_array_add(row, param);
1736 }
1737
1738 g_string_free(nul_bytes, TRUE);
1739
1740 return err ? -1 : 0;
1741}
1742
1743/**
1744 */
1745GList *network_mysqld_proto_get_next_binary_row(GList *chunk, network_mysqld_proto_fielddefs_t *fields, network_mysqld_resultset_row_t *row) {
1746 network_packet packet;
1747 int err = 0;
1748 network_mysqld_lenenc_type lenenc_type;
1749
1750 packet.data = chunk->data;
1751 packet.offset = 0;
1752
1753 err = err || network_mysqld_proto_skip_network_header(&packet);
1754
1755 err = err || network_mysqld_proto_peek_lenenc_type(&packet, &lenenc_type);
1756 if (0 != err) return NULL;
1757
1758 if (NETWORK_MYSQLD_LENENC_TYPE_EOF == lenenc_type) {
1759 /* this is a EOF packet, we are done */
1760 return NULL;
1761 }
1762
1763 err = err || network_mysqld_proto_get_binary_row(&packet, fields, row);
1764
1765 return err ? NULL : chunk->next;
1766}
1767
1768/**
1769 * create a struct for a COM_STMT_CLOSE packet
1770 */
1771network_mysqld_stmt_close_packet_t *network_mysqld_stmt_close_packet_new() {
1772 network_mysqld_stmt_close_packet_t *stmt_close_packet;
1773
1774 stmt_close_packet = g_slice_new0(network_mysqld_stmt_close_packet_t);
1775
1776 return stmt_close_packet;
1777}
1778
1779/**
1780 * free a struct for a COM_STMT_CLOSE packet
1781 */
1782void network_mysqld_stmt_close_packet_free(network_mysqld_stmt_close_packet_t *stmt_close_packet) {
1783 if (NULL == stmt_close_packet) return;
1784
1785 g_slice_free(network_mysqld_stmt_close_packet_t, stmt_close_packet);
1786}
1787
1788/**
1789 */
1790int network_mysqld_proto_get_stmt_close_packet(network_packet *packet, network_mysqld_stmt_close_packet_t *stmt_close_packet) {
1791 guint8 packet_type;
1792 int err = 0;
1793
1794 err = err || network_mysqld_proto_get_int8(packet, &packet_type);
1795 if (err) return -1;
1796
1797 if (COM_STMT_CLOSE != packet_type) {
1798 g_critical("%s: expected the first byte to be %02x, got %02x",
1799 G_STRLOC,
1800 COM_STMT_CLOSE,
1801 packet_type);
1802 return -1;
1803 }
1804
1805 err = err || network_mysqld_proto_get_int32(packet, &stmt_close_packet->stmt_id);
1806
1807 return err ? -1 : 0;
1808}
1809
1810int network_mysqld_proto_append_stmt_close_packet(GString *packet, network_mysqld_stmt_close_packet_t *stmt_close_packet) {
1811 network_mysqld_proto_append_int8(packet, COM_STMT_CLOSE);
1812 network_mysqld_proto_append_int32(packet, stmt_close_packet->stmt_id);
1813
1814 return 0;
1815}
1816
13611817
13621818
=== modified file 'src/network-mysqld-packet.h'
--- src/network-mysqld-packet.h 2010-05-10 10:31:45 +0000
+++ src/network-mysqld-packet.h 2010-09-27 14:20:48 +0000
@@ -185,4 +185,58 @@
185NETWORK_API int network_mysqld_proto_get_auth_response(network_packet *packet, network_mysqld_auth_response *auth);185NETWORK_API int network_mysqld_proto_get_auth_response(network_packet *packet, network_mysqld_auth_response *auth);
186NETWORK_API network_mysqld_auth_response *network_mysqld_auth_response_copy(network_mysqld_auth_response *src);186NETWORK_API network_mysqld_auth_response *network_mysqld_auth_response_copy(network_mysqld_auth_response *src);
187187
188/* COM_STMT_* */
189
190typedef struct {
191 GString *stmt_text;
192} network_mysqld_stmt_prepare_packet_t;
193
194NETWORK_API network_mysqld_stmt_prepare_packet_t *network_mysqld_stmt_prepare_packet_new();
195NETWORK_API void network_mysqld_stmt_prepare_packet_free(network_mysqld_stmt_prepare_packet_t *stmt_prepare_packet);
196NETWORK_API int network_mysqld_proto_get_stmt_prepare_packet(network_packet *packet, network_mysqld_stmt_prepare_packet_t *stmt_prepare_packet);
197NETWORK_API int network_mysqld_proto_append_stmt_prepare_packet(GString *packet, network_mysqld_stmt_prepare_packet_t *stmt_prepare_packet);
198
199typedef struct {
200 guint32 stmt_id;
201 guint16 num_columns;
202 guint16 num_params;
203 guint16 warnings;
204} network_mysqld_stmt_prepare_ok_packet_t;
205
206NETWORK_API network_mysqld_stmt_prepare_ok_packet_t *network_mysqld_stmt_prepare_ok_packet_new(void);
207NETWORK_API void network_mysqld_stmt_prepare_ok_packet_free(network_mysqld_stmt_prepare_ok_packet_t *stmt_prepare_ok_packet);
208NETWORK_API int network_mysqld_proto_get_stmt_prepare_ok_packet(network_packet *packet, network_mysqld_stmt_prepare_ok_packet_t *stmt_prepare_ok_packet);
209NETWORK_API int network_mysqld_proto_append_stmt_prepare_ok_packet(GString *packet, network_mysqld_stmt_prepare_ok_packet_t *stmt_prepare_ok_packet);
210
211typedef struct {
212 guint32 stmt_id;
213 guint8 flags;
214 guint32 iteration_count;
215 guint8 new_params_bound;
216 GPtrArray *params; /**< array<network_mysqld_type *> */
217} network_mysqld_stmt_execute_packet_t;
218
219NETWORK_API network_mysqld_stmt_execute_packet_t *network_mysqld_stmt_execute_packet_new(void);
220NETWORK_API void network_mysqld_stmt_execute_packet_free(network_mysqld_stmt_execute_packet_t *stmt_execute_packet);
221NETWORK_API int network_mysqld_proto_get_stmt_execute_packet(network_packet *packet, network_mysqld_stmt_execute_packet_t *stmt_execute_packet, guint param_count);
222NETWORK_API int network_mysqld_proto_append_stmt_execute_packet(GString *packet, network_mysqld_stmt_execute_packet_t *stmt_execute_packet, guint param_count);
223NETWORK_API int network_mysqld_proto_get_stmt_execute_packet_stmt_id(network_packet *packet, guint32 *stmt_id);
224
225
226typedef GPtrArray network_mysqld_resultset_row_t;
227
228NETWORK_API network_mysqld_resultset_row_t *network_mysqld_resultset_row_new(void);
229NETWORK_API void network_mysqld_resultset_row_free(network_mysqld_resultset_row_t *row);
230NETWORK_API int network_mysqld_proto_get_binary_row(network_packet *packet, network_mysqld_proto_fielddefs_t *fields, network_mysqld_resultset_row_t *row);
231NETWORK_API GList *network_mysqld_proto_get_next_binary_row(GList *chunk, network_mysqld_proto_fielddefs_t *fields, network_mysqld_resultset_row_t *row);
232
233typedef struct {
234 guint32 stmt_id;
235} network_mysqld_stmt_close_packet_t;
236
237NETWORK_API network_mysqld_stmt_close_packet_t *network_mysqld_stmt_close_packet_new(void);
238NETWORK_API void network_mysqld_stmt_close_packet_free(network_mysqld_stmt_close_packet_t *stmt_close_packet);
239NETWORK_API int network_mysqld_proto_get_stmt_close_packet(network_packet *packet, network_mysqld_stmt_close_packet_t *stmt_close_packet);
240NETWORK_API int network_mysqld_proto_append_stmt_close_packet(GString *packet, network_mysqld_stmt_close_packet_t *stmt_close_packet);
241
188#endif242#endif
189243
=== modified file 'src/network-mysqld-proto.h'
--- src/network-mysqld-proto.h 2010-04-06 14:26:51 +0000
+++ src/network-mysqld-proto.h 2010-09-27 14:20:48 +0000
@@ -111,11 +111,14 @@
111NETWORK_API int network_mysqld_proto_peek_lenenc_type(network_packet *packet, network_mysqld_lenenc_type *type);111NETWORK_API int network_mysqld_proto_peek_lenenc_type(network_packet *packet, network_mysqld_lenenc_type *type);
112NETWORK_API int network_mysqld_proto_get_lenenc_int(network_packet *packet, guint64 *v);112NETWORK_API int network_mysqld_proto_get_lenenc_int(network_packet *packet, guint64 *v);
113113
114NETWORK_API MYSQL_FIELD *network_mysqld_proto_fielddef_new(void);114typedef MYSQL_FIELD network_mysqld_proto_fielddef_t;
115NETWORK_API void network_mysqld_proto_fielddef_free(MYSQL_FIELD *fielddef);115NETWORK_API network_mysqld_proto_fielddef_t *network_mysqld_proto_fielddef_new(void);
116NETWORK_API void network_mysqld_proto_fielddef_free(network_mysqld_proto_fielddef_t *fielddef);
117NETWORK_API int network_mysqld_proto_get_fielddef(network_packet *packet, network_mysqld_proto_fielddef_t *field, guint32 capabilities);
116118
117NETWORK_API GPtrArray *network_mysqld_proto_fielddefs_new(void);119typedef GPtrArray network_mysqld_proto_fielddefs_t;
118NETWORK_API void network_mysqld_proto_fielddefs_free(GPtrArray *fielddefs);120NETWORK_API network_mysqld_proto_fielddefs_t *network_mysqld_proto_fielddefs_new(void);
121NETWORK_API void network_mysqld_proto_fielddefs_free(network_mysqld_proto_fielddefs_t *fielddefs);
119122
120NETWORK_API guint32 network_mysqld_proto_get_packet_len(GString *_header);123NETWORK_API guint32 network_mysqld_proto_get_packet_len(GString *_header);
121NETWORK_API guint8 network_mysqld_proto_get_packet_id(GString *_header);124NETWORK_API guint8 network_mysqld_proto_get_packet_id(GString *_header);
122125
=== added file 'src/network_mysqld_proto_binary.c'
--- src/network_mysqld_proto_binary.c 1970-01-01 00:00:00 +0000
+++ src/network_mysqld_proto_binary.c 2010-09-27 14:20:48 +0000
@@ -0,0 +1,436 @@
1/* $%BEGINLICENSE%$
2 Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
3
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License as
6 published by the Free Software Foundation; version 2 of the
7 License.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
17 02110-1301 USA
18
19 $%ENDLICENSE%$ */
20
21/**
22 * codec's for the binary MySQL client protocol
23 */
24
25#include <stdlib.h>
26#include <stdio.h>
27#include <string.h>
28
29#include "network_mysqld_proto_binary.h"
30
31#include "glib-ext.h"
32#include "string-len.h"
33
34/* ints */
35static int network_mysqld_proto_binary_get_int_type(network_packet *packet, network_mysqld_type_t *type) {
36 int err = 0;
37 guint8 i8;
38 guint16 i16;
39 guint32 i32;
40 guint64 i64;
41
42 switch (type->type) {
43 case MYSQL_TYPE_TINY:
44 err = err || network_mysqld_proto_get_int8(packet, &i8);
45 err = err || network_mysqld_type_set_int(type, (guint64)i8, type->is_unsigned);
46 break;
47 case MYSQL_TYPE_SHORT:
48 err = err || network_mysqld_proto_get_int16(packet, &i16);
49 err = err || network_mysqld_type_set_int(type, (guint64)i16, type->is_unsigned);
50 break;
51 case MYSQL_TYPE_LONG:
52 case MYSQL_TYPE_INT24:
53 err = err || network_mysqld_proto_get_int32(packet, &i32);
54 err = err || network_mysqld_type_set_int(type, (guint64)i32, type->is_unsigned);
55 break;
56 case MYSQL_TYPE_LONGLONG:
57 err = err || network_mysqld_proto_get_int64(packet, &i64);
58 err = err || network_mysqld_type_set_int(type, i64, type->is_unsigned);
59 break;
60 default:
61 err = -1;
62 break;
63 }
64
65 return err ? -1 : 0;
66}
67
68static int network_mysqld_proto_binary_append_int_type(GString *packet, network_mysqld_type_t *type) {
69 guint64 i64;
70 guint8 i8;
71 guint16 i16;
72 guint32 i32;
73 int err = 0;
74
75 err = err || network_mysqld_type_get_int(type, &i64, NULL);
76 if (0 != err) return -1;
77
78 switch (type->type) {
79 case MYSQL_TYPE_TINY:
80
81 i8 = i64;
82
83 err = err || network_mysqld_proto_append_int8(packet, i8);
84 break;
85 case MYSQL_TYPE_SHORT:
86 i16 = i64;
87
88 err = err || network_mysqld_proto_append_int16(packet, i16);
89 break;
90 case MYSQL_TYPE_LONG:
91 case MYSQL_TYPE_INT24:
92 i32 = i64;
93
94 err = err || network_mysqld_proto_append_int32(packet, i32);
95 break;
96 case MYSQL_TYPE_LONGLONG:
97 err = err || network_mysqld_proto_append_int64(packet, i64);
98 break;
99 default:
100 err = -1;
101 break;
102 }
103
104 return err ? -1 : 0;
105}
106
107/* double */
108static int network_mysqld_proto_binary_get_double_type(network_packet *packet, network_mysqld_type_t *type) {
109 int err = 0;
110 union {
111 double d;
112 char d_char_shadow[sizeof(double) + 1];
113 } double_copy;
114
115 GString s;
116 s.str = double_copy.d_char_shadow;
117 s.len = 0;
118 s.allocated_len = sizeof(double_copy.d_char_shadow);
119
120 err = err || network_mysqld_proto_get_gstring_len(packet, sizeof(double), &s);
121 err = err || network_mysqld_type_set_double(type, double_copy.d);
122
123 return err ? -1 : 0;
124}
125
126static int network_mysqld_proto_binary_append_double_type(GString *packet, network_mysqld_type_t *type) {
127 union {
128 double d;
129 char d_char_shadow[sizeof(double)];
130 } double_copy;
131 int err = 0;
132
133 err = err || network_mysqld_type_get_double(type, &double_copy.d);
134 if (0 != err) return -1;
135
136 g_string_append_len(packet, double_copy.d_char_shadow, sizeof(double));
137
138 return err ? -1 : 0;
139}
140
141/* float */
142static int network_mysqld_proto_binary_get_float_type(network_packet *packet, network_mysqld_type_t *type) {
143 int err = 0;
144 union {
145 float d;
146 char d_char_shadow[sizeof(float) + 1];
147 } float_copy;
148
149 GString s;
150 s.str = float_copy.d_char_shadow;
151 s.len = 0;
152 s.allocated_len = sizeof(float_copy.d_char_shadow);
153
154 err = err || network_mysqld_proto_get_gstring_len(packet, sizeof(float), &s);
155 err = err || network_mysqld_type_set_double(type, (double)float_copy.d);
156
157 return err ? -1 : 0;
158}
159
160static int network_mysqld_proto_binary_append_float_type(GString *packet, network_mysqld_type_t *type) {
161 double d;
162 int err = 0;
163
164 err = err || network_mysqld_type_get_double(type, &d); /* get our float as double */
165
166 if (0 == err) {
167 /* copy the float directly without switching byte-order */
168 union {
169 float f;
170 char f_char_shadow[sizeof(float)];
171 } float_copy; /* shadow the float with a equally sized char to be able to copy it without extra type-casting */
172
173 float_copy.f = (float)d;
174
175 g_string_append_len(packet, float_copy.f_char_shadow, sizeof(float));
176 }
177
178 return err ? -1 : 0;
179}
180
181/* all kinds of strings */
182static int network_mysqld_proto_binary_get_string_type(network_packet *packet, network_mysqld_type_t *type) {
183 GString *str;
184 int err = 0;
185
186 str = g_string_new(NULL);
187
188 err = err || network_mysqld_proto_get_lenenc_gstring(packet, str);
189
190 network_mysqld_type_set_string(type, S(str));
191
192 g_string_free(str, TRUE);
193
194 return err ? -1 : 0;
195}
196
197static int network_mysqld_proto_binary_append_string_type(GString *packet, network_mysqld_type_t *type) {
198 const char *s;
199 gsize s_len;
200 int err = 0;
201
202 err = err || network_mysqld_type_get_string_const(type, &s, &s_len);
203 err = err || network_mysqld_proto_append_lenenc_string_len(packet, s, s_len);
204
205 return err ? -1 : 0;
206}
207
208/* all kinds of time */
209
210/**
211 * extract the date from a binary resultset row
212 */
213static int network_mysqld_proto_binary_get_date_type(network_packet *packet, network_mysqld_type_t *type) {
214 int err = 0;
215 guint8 len;
216 network_mysqld_type_date_t date;
217
218 err = err || network_mysqld_proto_get_int8(packet, &len);
219
220 /* check the valid lengths
221 */
222 switch (len) {
223 case 11: /* date + time + ms */
224 case 7: /* date + time ( ms is .0000 ) */
225 case 4: /* date ( time is 00:00:00 )*/
226 case 0: /* date == 0000-00-00 */
227 break;
228 default:
229 return -1;
230 }
231
232 memset(&date, 0, sizeof(date));
233 if (len > 0) {
234 err = err || network_mysqld_proto_get_int16(packet, &date.year);
235 err = err || network_mysqld_proto_get_int8(packet, &date.month);
236 err = err || network_mysqld_proto_get_int8(packet, &date.day);
237
238 if (len > 4) {
239 err = err || network_mysqld_proto_get_int8(packet, &date.hour);
240 err = err || network_mysqld_proto_get_int8(packet, &date.min);
241 err = err || network_mysqld_proto_get_int8(packet, &date.sec);
242
243 if (len > 7) {
244 err = err || network_mysqld_proto_get_int32(packet, &date.nsec);
245 }
246 }
247 }
248
249 if (0 == err) {
250 err = err || network_mysqld_type_set_date(type, &date);
251 }
252
253 return err ? -1 : 0;
254}
255
256static int network_mysqld_proto_binary_append_date_type(GString G_GNUC_UNUSED *packet, network_mysqld_type_t G_GNUC_UNUSED *type) {
257 /* not implemented yet */
258 return -1;
259}
260
261/**
262 * extract the time from a binary resultset row
263 */
264static int network_mysqld_proto_binary_get_time_type(network_packet *packet, network_mysqld_type_t *type) {
265 int err = 0;
266 guint8 len;
267 network_mysqld_type_time_t t;
268
269 err = err || network_mysqld_proto_get_int8(packet, &len);
270
271 /* check the valid lengths
272 */
273 switch (len) {
274 case 12: /* day + time + ms */
275 case 8: /* day + time ( ms is .0000 ) */
276 case 0: /* time == 00:00:00 */
277 break;
278 default:
279 return -1;
280 }
281
282 memset(&t, 0, sizeof(t));
283 if (len > 0) {
284 err = err || network_mysqld_proto_get_int8(packet, &t.sign);
285 err = err || network_mysqld_proto_get_int32(packet, &t.days);
286
287 err = err || network_mysqld_proto_get_int8(packet, &t.hour);
288 err = err || network_mysqld_proto_get_int8(packet, &t.min);
289 err = err || network_mysqld_proto_get_int8(packet, &t.sec);
290
291 if (len > 8) {
292 err = err || network_mysqld_proto_get_int32(packet, &t.nsec);
293 }
294 }
295
296 if (0 == err) {
297 err = err || network_mysqld_type_set_time(type, &t);
298 }
299
300 return err ? -1 : 0;
301}
302
303static int network_mysqld_proto_binary_append_time_type(GString G_GNUC_UNUSED *packet, network_mysqld_type_t G_GNUC_UNUSED *type) {
304 /* not implemented yet */
305 return -1;
306}
307
308/**
309 * valid types for prepared statements parameters we receive from the client
310 */
311gboolean network_mysql_proto_binary_type_is_valid_input(enum enum_field_types field_type) {
312 switch (field_type) {
313 case MYSQL_TYPE_TINY:
314 case MYSQL_TYPE_SHORT:
315 case MYSQL_TYPE_LONG:
316 case MYSQL_TYPE_LONGLONG:
317
318 case MYSQL_TYPE_FLOAT:
319 case MYSQL_TYPE_DOUBLE:
320
321 case MYSQL_TYPE_BLOB:
322 case MYSQL_TYPE_STRING:
323
324 case MYSQL_TYPE_DATE:
325 case MYSQL_TYPE_DATETIME:
326 case MYSQL_TYPE_TIME:
327 case MYSQL_TYPE_TIMESTAMP:
328
329 case MYSQL_TYPE_NULL:
330 return TRUE;
331 default:
332 return FALSE;
333 }
334}
335
336/**
337 * types we allow the send back to the client
338 */
339gboolean network_mysql_proto_binary_is_valid_output(enum enum_field_types field_type) {
340 switch (field_type) {
341 case MYSQL_TYPE_TINY:
342 case MYSQL_TYPE_SHORT:
343 case MYSQL_TYPE_INT24:
344 case MYSQL_TYPE_LONG:
345 case MYSQL_TYPE_LONGLONG:
346
347 case MYSQL_TYPE_FLOAT:
348 case MYSQL_TYPE_DOUBLE:
349 case MYSQL_TYPE_NEWDECIMAL:
350
351 case MYSQL_TYPE_TINY_BLOB:
352 case MYSQL_TYPE_BLOB:
353 case MYSQL_TYPE_MEDIUM_BLOB:
354 case MYSQL_TYPE_LONG_BLOB:
355 case MYSQL_TYPE_STRING:
356 case MYSQL_TYPE_VAR_STRING:
357
358 case MYSQL_TYPE_DATE:
359 case MYSQL_TYPE_DATETIME:
360 case MYSQL_TYPE_TIME:
361 case MYSQL_TYPE_TIMESTAMP:
362
363 case MYSQL_TYPE_BIT:
364 return TRUE;
365 default:
366 return FALSE;
367 }
368}
369
370int network_mysqld_proto_binary_get_type(network_packet *packet, network_mysqld_type_t *type) {
371 switch (type->type) {
372 case MYSQL_TYPE_TINY:
373 case MYSQL_TYPE_SHORT:
374 case MYSQL_TYPE_LONG:
375 case MYSQL_TYPE_INT24:
376 case MYSQL_TYPE_LONGLONG:
377 return network_mysqld_proto_binary_get_int_type(packet, type);
378 case MYSQL_TYPE_DATE:
379 case MYSQL_TYPE_DATETIME:
380 case MYSQL_TYPE_TIMESTAMP:
381 return network_mysqld_proto_binary_get_date_type(packet, type);
382 case MYSQL_TYPE_TIME:
383 return network_mysqld_proto_binary_get_time_type(packet, type);
384 case MYSQL_TYPE_FLOAT:
385 return network_mysqld_proto_binary_get_float_type(packet, type);
386 case MYSQL_TYPE_DOUBLE:
387 return network_mysqld_proto_binary_get_double_type(packet, type);
388 case MYSQL_TYPE_BIT:
389 case MYSQL_TYPE_NEWDECIMAL:
390 case MYSQL_TYPE_BLOB:
391 case MYSQL_TYPE_TINY_BLOB:
392 case MYSQL_TYPE_MEDIUM_BLOB:
393 case MYSQL_TYPE_LONG_BLOB:
394 case MYSQL_TYPE_STRING:
395 case MYSQL_TYPE_VAR_STRING:
396 /* they are all length-encoded strings */
397 return network_mysqld_proto_binary_get_string_type(packet, type);
398 }
399
400 return -1;
401}
402
403int network_mysqld_proto_binary_append_type(GString *packet, network_mysqld_type_t *type) {
404 switch (type->type) {
405 case MYSQL_TYPE_TINY:
406 case MYSQL_TYPE_SHORT:
407 case MYSQL_TYPE_LONG:
408 case MYSQL_TYPE_INT24:
409 case MYSQL_TYPE_LONGLONG:
410 return network_mysqld_proto_binary_append_int_type(packet, type);
411 case MYSQL_TYPE_DATE:
412 case MYSQL_TYPE_DATETIME:
413 case MYSQL_TYPE_TIMESTAMP:
414 return network_mysqld_proto_binary_append_date_type(packet, type);
415 case MYSQL_TYPE_TIME:
416 return network_mysqld_proto_binary_append_time_type(packet, type);
417 case MYSQL_TYPE_FLOAT:
418 return network_mysqld_proto_binary_append_float_type(packet, type);
419 case MYSQL_TYPE_DOUBLE:
420 return network_mysqld_proto_binary_append_double_type(packet, type);
421 case MYSQL_TYPE_BIT:
422 case MYSQL_TYPE_NEWDECIMAL:
423 case MYSQL_TYPE_BLOB:
424 case MYSQL_TYPE_TINY_BLOB:
425 case MYSQL_TYPE_MEDIUM_BLOB:
426 case MYSQL_TYPE_LONG_BLOB:
427 case MYSQL_TYPE_STRING:
428 case MYSQL_TYPE_VAR_STRING:
429 /* they are all length-encoded strings */
430 return network_mysqld_proto_binary_append_string_type(packet, type);
431 }
432
433 return -1;
434}
435
436
0437
=== added file 'src/network_mysqld_proto_binary.h'
--- src/network_mysqld_proto_binary.h 1970-01-01 00:00:00 +0000
+++ src/network_mysqld_proto_binary.h 2010-09-27 14:20:48 +0000
@@ -0,0 +1,12 @@
1#ifndef __NETWORK_MYSQLD_PROTO_BINARY_H__
2#define __NETWORK_MYSQLD_PROTO_BINARY_H__
3
4#include <glib.h>
5
6#include "network-socket.h"
7#include "network_mysqld_type.h"
8
9int network_mysqld_proto_binary_get_type(network_packet *packet, network_mysqld_type_t *type);
10int network_mysqld_proto_binary_append_type(GString *packet, network_mysqld_type_t *type);
11
12#endif
013
=== added file 'src/network_mysqld_type.c'
--- src/network_mysqld_type.c 1970-01-01 00:00:00 +0000
+++ src/network_mysqld_type.c 2010-09-27 14:20:48 +0000
@@ -0,0 +1,656 @@
1#include <string.h>
2#include <stdlib.h>
3#include <stdio.h>
4
5#include <glib.h>
6
7#include "network_mysqld_type.h"
8#include "string-len.h"
9
10#include "glib-ext.h"
11
12/* expose the types itself and their internal representation */
13
14typedef double network_mysqld_type_double_t;
15
16typedef float network_mysqld_type_float_t;
17
18typedef GString network_mysqld_type_string_t;
19
20typedef struct {
21 guint64 i;
22 gboolean is_unsigned;
23} network_mysqld_type_int_t;
24
25/**
26 * create a type that can hold a MYSQL_TYPE_LONGLONG
27 */
28static network_mysqld_type_int_t *network_mysqld_type_int_new(void) {
29 network_mysqld_type_int_t *ll;
30
31 ll = g_slice_new0(network_mysqld_type_int_t);
32
33 return ll;
34}
35
36/**
37 * free a network_mysqld_type_int_t
38 */
39static void network_mysqld_type_int_free(network_mysqld_type_int_t *ll) {
40 if (NULL == ll) return;
41
42 g_slice_free(network_mysqld_type_int_t, ll);
43}
44
45static int network_mysqld_type_data_int_get_int(network_mysqld_type_t *type, guint64 *i, gboolean *is_unsigned) {
46 network_mysqld_type_int_t *value;
47
48 if (NULL == type->data) return -1;
49
50 value = type->data;
51
52 *i = value->i;
53 *is_unsigned = value->is_unsigned;
54
55 return 0;
56}
57
58static int network_mysqld_type_data_int_set_int(network_mysqld_type_t *type, guint64 i, gboolean is_unsigned) {
59 network_mysqld_type_int_t *value;
60
61 if (NULL == type->data) {
62 type->data = network_mysqld_type_int_new();
63 }
64 value = type->data;
65
66 value->i = i;
67 value->is_unsigned = is_unsigned;
68
69 return 0;
70}
71
72
73/**
74 * typesafe wrapper for network_mysqld_type_new()
75 */
76static void network_mysqld_type_data_int_free(network_mysqld_type_t *type) {
77 if (NULL == type) return;
78 if (NULL == type->data) return;
79
80 network_mysqld_type_int_free(type->data);
81}
82
83static void network_mysqld_type_data_int_init(network_mysqld_type_t *type, enum enum_field_types field_type) {
84 type->type = field_type;
85 type->free_data = network_mysqld_type_data_int_free;
86 type->get_int = network_mysqld_type_data_int_get_int;
87 type->set_int = network_mysqld_type_data_int_set_int;
88}
89
90/* MYSQL_TYPE_DOUBLE */
91
92/**
93 * create a type that can hold a MYSQL_TYPE_DOUBLE
94 */
95static network_mysqld_type_double_t *network_mysqld_type_double_new(void) {
96 network_mysqld_type_double_t *t;
97
98 t = g_slice_new0(network_mysqld_type_double_t);
99
100 return t;
101}
102
103/**
104 * free a network_mysqld_type_double_t
105 */
106static void network_mysqld_type_double_free(network_mysqld_type_double_t *t) {
107 if (NULL == t) return;
108
109 g_slice_free(network_mysqld_type_double_t, t);
110}
111
112static void network_mysqld_type_data_double_free(network_mysqld_type_t *type) {
113 if (NULL == type) return;
114 if (NULL == type->data) return;
115
116 network_mysqld_type_double_free(type->data);
117}
118
119static int network_mysqld_type_data_double_get_double(network_mysqld_type_t *type, double *d) {
120 network_mysqld_type_double_t *value = type->data;
121
122 if (NULL == value) return -1;
123
124 *d = *value;
125
126 return 0;
127}
128
129static int network_mysqld_type_data_double_set_double(network_mysqld_type_t *type, double d) {
130 network_mysqld_type_double_t *value;
131
132 if (NULL == type->data) {
133 type->data = network_mysqld_type_double_new();
134 }
135
136 value = type->data;
137 *value = d;
138
139 return 0;
140}
141
142static void network_mysqld_type_data_double_init(network_mysqld_type_t *type, enum enum_field_types field_type) {
143 type->type = field_type;
144 type->free_data = network_mysqld_type_data_double_free;
145 type->get_double = network_mysqld_type_data_double_get_double;
146 type->set_double = network_mysqld_type_data_double_set_double;
147}
148
149/* MYSQL_TYPE_FLOAT */
150
151/**
152 * create a type that can hold a MYSQL_TYPE_FLOAT
153 */
154
155static network_mysqld_type_float_t *network_mysqld_type_float_new(void) {
156 network_mysqld_type_float_t *t;
157
158 t = g_slice_new0(network_mysqld_type_float_t);
159
160 return t;
161}
162
163/**
164 * free a network_mysqld_type_float_t
165 */
166static void network_mysqld_type_float_free(network_mysqld_type_float_t *t) {
167 if (NULL == t) return;
168
169 g_slice_free(network_mysqld_type_float_t, t);
170}
171
172static void network_mysqld_type_data_float_free(network_mysqld_type_t *type) {
173 if (NULL == type) return;
174 if (NULL == type->data) return;
175
176 network_mysqld_type_float_free(type->data);
177}
178
179static int network_mysqld_type_data_float_get_double(network_mysqld_type_t *type, double *dst) {
180 network_mysqld_type_float_t *src = type->data;
181
182 if (NULL == type->data) return -1;
183
184 *dst = (double)*src;
185
186 return 0;
187}
188
189static int network_mysqld_type_data_float_set_double(network_mysqld_type_t *type, double src) {
190 network_mysqld_type_float_t *dst = type->data;
191
192 if (NULL == type->data) {
193 type->data = network_mysqld_type_float_new();
194 }
195
196 dst = type->data;
197 *dst = (float)src;
198
199 return 0;
200}
201
202static void network_mysqld_type_data_float_init(network_mysqld_type_t *type, enum enum_field_types field_type) {
203 type->type = field_type;
204 type->free_data = network_mysqld_type_data_float_free;
205 type->get_double = network_mysqld_type_data_float_get_double;
206 type->set_double = network_mysqld_type_data_float_set_double;
207}
208
209/* MYSQL_TYPE_STRING */
210static network_mysqld_type_string_t *network_mysqld_type_string_new(void) {
211 network_mysqld_type_string_t *str;
212
213 str = g_string_new(NULL);
214
215 return str;
216}
217
218static void network_mysqld_type_string_free(network_mysqld_type_string_t *str) {
219 if (NULL == str) return;
220
221 g_string_free(str, TRUE);
222}
223
224static void network_mysqld_type_data_string_free(network_mysqld_type_t *type) {
225 if (NULL == type) return;
226
227 network_mysqld_type_string_free(type->data);
228}
229
230static int network_mysqld_type_data_string_get_string_const(network_mysqld_type_t *type, const char **dst, gsize *dst_len) {
231 GString *src = type->data;
232
233 if (NULL == type->data) return -1;
234
235 *dst = src->str;
236 *dst_len = src->len;
237
238 return 0;
239}
240
241static int network_mysqld_type_data_string_set_string(network_mysqld_type_t *type, const char *src, gsize src_len) {
242 GString *dst;
243
244 if (NULL == type->data) {
245 type->data = g_string_sized_new(src_len);
246 }
247
248 dst = type->data;
249
250 g_string_assign_len(dst, src, src_len);
251
252 return 0;
253}
254
255static void network_mysqld_type_data_string_init(network_mysqld_type_t *type, enum enum_field_types field_type) {
256 type->type = field_type;
257 type->free_data = network_mysqld_type_data_string_free;
258 type->get_string_const = network_mysqld_type_data_string_get_string_const;
259 type->set_string = network_mysqld_type_data_string_set_string;
260}
261
262/* MYSQL_TYPE_DATE */
263static network_mysqld_type_date_t *network_mysqld_type_date_new(void) {
264 network_mysqld_type_date_t *date;
265
266 date = g_slice_new0(network_mysqld_type_date_t);
267
268 return date;
269}
270
271static void network_mysqld_type_date_free(network_mysqld_type_date_t *date) {
272 if (NULL == date) return;
273
274 g_slice_free(network_mysqld_type_date_t, date);
275}
276
277static void network_mysqld_type_data_date_free(network_mysqld_type_t *type) {
278 if (NULL == type) return;
279
280 network_mysqld_type_date_free(type->data);
281}
282
283static int network_mysqld_type_data_date_get_date(network_mysqld_type_t *type, network_mysqld_type_date_t *dst) {
284 network_mysqld_type_date_t *src = type->data;
285
286 if (NULL == type->data) return -1;
287
288 memcpy(dst, src, sizeof(*src));
289
290 return 0;
291}
292
293static gboolean network_mysqld_type_date_time_is_valid(network_mysqld_type_date_t *date) {
294 return (date->nsec < 1000000000 &&
295 date->sec < 100 &&
296 date->min <= 60 &&
297 date->hour <= 24);
298}
299
300static gboolean network_mysqld_type_date_date_is_valid(network_mysqld_type_date_t *date) {
301 return (date->day <= 31 &&
302 date->month <= 12 &&
303 date->year <= 9999);
304}
305
306gboolean network_mysqld_type_date_is_valid(network_mysqld_type_date_t *date) {
307 return network_mysqld_type_date_time_is_valid(date) &&
308 network_mysqld_type_date_date_is_valid(date);
309}
310
311static int network_mysqld_type_data_date_get_string(network_mysqld_type_t *type, char **dst, gsize *dst_len) {
312 network_mysqld_type_date_t *src = type->data;
313
314 if (NULL == type->data) return -1;
315
316 switch (type->type) {
317 case MYSQL_TYPE_DATE:
318 if (!network_mysqld_type_date_date_is_valid(src)) {
319 return -1;
320 }
321 break;
322 case MYSQL_TYPE_DATETIME:
323 case MYSQL_TYPE_TIMESTAMP:
324 if (!network_mysqld_type_date_is_valid(src)) {
325 return -1;
326 }
327 break;
328 default:
329 /* we shouldn't be here */
330 return -1;
331 }
332
333 if (NULL != *dst) {
334 switch (type->type) {
335 case MYSQL_TYPE_DATE:
336 /* dst_len already contains a size and we don't have to alloc */
337 if (*dst_len < NETWORK_MYSQLD_TYPE_DATE_MIN_BUF_LEN) {
338 return -1; /* ... but it is too small .. we could return the right size here */
339 }
340 *dst_len = snprintf(*dst, *dst_len, "%04u-%02u-%02u",
341 src->year,
342 src->month,
343 src->day);
344 break;
345 case MYSQL_TYPE_DATETIME:
346 case MYSQL_TYPE_TIMESTAMP:
347 /* dst_len already contains a size and we don't have to alloc */
348 if (*dst_len < NETWORK_MYSQLD_TYPE_DATETIME_MIN_BUF_LEN) {
349 return -1; /* ... but it is too small .. we could return the right size here */
350 }
351 *dst_len = snprintf(*dst, *dst_len, "%04u-%02u-%02u %02u:%02u:%02u.%09u",
352 src->year,
353 src->month,
354 src->day,
355 src->hour,
356 src->min,
357 src->sec,
358 src->nsec);
359 break;
360 default:
361 g_assert_not_reached();
362 break;
363 }
364 } else {
365 switch (type->type) {
366 case MYSQL_TYPE_DATE:
367 *dst = g_strdup_printf("%04u-%02u-%02u",
368 src->year,
369 src->month,
370 src->day);
371 *dst_len = strlen(*dst);
372 break;
373 case MYSQL_TYPE_DATETIME:
374 case MYSQL_TYPE_TIMESTAMP:
375 *dst = g_strdup_printf("%04u-%02u-%02u %02u:%02u:%02u.%09u",
376 src->year,
377 src->month,
378 src->day,
379 src->hour,
380 src->min,
381 src->sec,
382 src->nsec);
383 *dst_len = strlen(*dst);
384 break;
385 default:
386 g_assert_not_reached();
387 break;
388 }
389 }
390
391 return 0;
392}
393
394
395static int network_mysqld_type_data_date_set_date(network_mysqld_type_t *type, network_mysqld_type_date_t *src) {
396 network_mysqld_type_date_t *dst;
397
398 if (NULL == type->data) {
399 type->data = network_mysqld_type_date_new();
400 }
401
402 dst = type->data;
403
404 memcpy(dst, src, sizeof(*src));
405
406 return 0;
407}
408
409static void network_mysqld_type_data_date_init(network_mysqld_type_t *type, enum enum_field_types field_type) {
410 type->type = field_type;
411 type->free_data = network_mysqld_type_data_date_free;
412 type->get_date = network_mysqld_type_data_date_get_date;
413 type->get_string = network_mysqld_type_data_date_get_string;
414 type->set_date = network_mysqld_type_data_date_set_date;
415}
416
417
418/* MYSQL_TYPE_TIME */
419static network_mysqld_type_time_t *network_mysqld_type_time_new(void) {
420 network_mysqld_type_time_t *t;
421
422 t = g_slice_new0(network_mysqld_type_time_t);
423
424 return t;
425}
426
427static void network_mysqld_type_time_free(network_mysqld_type_time_t *t) {
428 if (NULL == t) return;
429
430 g_slice_free(network_mysqld_type_time_t, t);
431}
432
433static void network_mysqld_type_data_time_free(network_mysqld_type_t *type) {
434 if (NULL == type) return;
435
436 network_mysqld_type_time_free(type->data);
437}
438
439static int network_mysqld_type_data_time_get_time(network_mysqld_type_t *type, network_mysqld_type_time_t *dst) {
440 network_mysqld_type_time_t *src = type->data;
441
442 if (NULL == type->data) return -1;
443
444 memcpy(dst, src, sizeof(*src));
445
446 return 0;
447}
448
449static int network_mysqld_type_data_time_get_string(network_mysqld_type_t *type, char **dst, gsize *dst_len) {
450 network_mysqld_type_time_t *src = type->data;
451
452 if (NULL == type->data) return -1;
453
454 if (NULL != *dst) {
455 /* dst_len already contains a size and we don't have to alloc */
456 if (*dst_len < NETWORK_MYSQLD_TYPE_TIME_MIN_BUF_LEN) {
457 return -1; /* ... but it is too small .. we could return the right size here */
458 }
459 *dst_len = snprintf(*dst, *dst_len, "%s%d %02u:%02u:%02u.%09u",
460 src->sign ? "-" : "",
461 src->days,
462 src->hour,
463 src->min,
464 src->sec,
465 src->nsec);
466 } else {
467 *dst = g_strdup_printf("%s%d %02u:%02u:%02u.%09u",
468 src->sign ? "-" : "",
469 src->days,
470 src->hour,
471 src->min,
472 src->sec,
473 src->nsec);
474 *dst_len = strlen(*dst);
475 }
476
477 return 0;
478}
479
480static int network_mysqld_type_data_time_set_time(network_mysqld_type_t *type, network_mysqld_type_time_t *src) {
481 network_mysqld_type_date_t *dst;
482
483 if (NULL == type->data) {
484 type->data = network_mysqld_type_time_new();
485 }
486 dst = type->data;
487
488 memcpy(dst, src, sizeof(*src));
489
490 return 0;
491}
492
493
494static void network_mysqld_type_data_time_init(network_mysqld_type_t *type, enum enum_field_types field_type) {
495 type->type = field_type;
496 type->free_data = network_mysqld_type_data_time_free;
497 type->get_time = network_mysqld_type_data_time_get_time;
498 type->get_string = network_mysqld_type_data_time_get_string;
499 type->set_time = network_mysqld_type_data_time_set_time;
500}
501
502
503/**
504 * create a type
505 */
506network_mysqld_type_t *network_mysqld_type_new(enum enum_field_types field_type) {
507 network_mysqld_type_t *type = NULL;
508
509 switch (field_type) {
510 case MYSQL_TYPE_TINY:
511 case MYSQL_TYPE_SHORT:
512 case MYSQL_TYPE_LONG:
513 case MYSQL_TYPE_INT24:
514 case MYSQL_TYPE_LONGLONG:
515 type = g_slice_new0(network_mysqld_type_t);
516
517 network_mysqld_type_data_int_init(type, field_type);
518 break;
519 case MYSQL_TYPE_FLOAT: /* 4 bytes */
520 type = g_slice_new0(network_mysqld_type_t);
521
522 network_mysqld_type_data_float_init(type, field_type);
523 break;
524 case MYSQL_TYPE_DOUBLE: /* 8 bytes */
525 type = g_slice_new0(network_mysqld_type_t);
526
527 network_mysqld_type_data_double_init(type, field_type);
528 break;
529 case MYSQL_TYPE_DATETIME:
530 case MYSQL_TYPE_DATE:
531 case MYSQL_TYPE_TIMESTAMP:
532 type = g_slice_new0(network_mysqld_type_t);
533
534 network_mysqld_type_data_date_init(type, field_type);
535 break;
536 case MYSQL_TYPE_TIME:
537 type = g_slice_new0(network_mysqld_type_t);
538
539 network_mysqld_type_data_time_init(type, field_type);
540 break;
541 case MYSQL_TYPE_NEWDECIMAL:
542 case MYSQL_TYPE_BLOB:
543 case MYSQL_TYPE_TINY_BLOB:
544 case MYSQL_TYPE_MEDIUM_BLOB:
545 case MYSQL_TYPE_LONG_BLOB:
546 case MYSQL_TYPE_STRING:
547 case MYSQL_TYPE_VAR_STRING:
548 case MYSQL_TYPE_VARCHAR:
549 /* they are all length-encoded strings */
550 type = g_slice_new0(network_mysqld_type_t);
551
552 network_mysqld_type_data_string_init(type, field_type);
553 break;
554 case MYSQL_TYPE_NULL:
555 type = g_slice_new0(network_mysqld_type_t);
556
557 type->type = field_type;
558 break;
559 }
560
561 return type;
562}
563/**
564 * free a type
565 */
566void network_mysqld_type_free(network_mysqld_type_t *type) {
567 if (NULL == type) return;
568
569 if (NULL != type->free_data) {
570 type->free_data(type);
571 }
572 g_slice_free(network_mysqld_type_t, type);
573}
574
575int network_mysqld_type_get_gstring(network_mysqld_type_t *type, GString *s) {
576 if (NULL == type->get_gstring) return -1;
577
578 return type->get_gstring(type, s);
579}
580
581int network_mysqld_type_get_string_const(network_mysqld_type_t *type, const char **s, gsize *s_len) {
582 if (NULL == type->get_string_const) return -1;
583
584 return type->get_string_const(type, s, s_len);
585}
586
587int network_mysqld_type_get_string(network_mysqld_type_t *type, char **s, gsize *s_len) {
588 if (NULL == type->get_string) return -1;
589
590 return type->get_string(type, s, s_len);
591}
592
593
594int network_mysqld_type_set_string(network_mysqld_type_t *type, const char *s, gsize s_len) {
595 if (NULL == type->set_string) return -1;
596
597 return type->set_string(type, s, s_len);
598}
599
600
601int network_mysqld_type_get_int(network_mysqld_type_t *type, guint64 *i, gboolean *is_unsigned) {
602 if (NULL == type->get_int) return -1;
603
604 return type->get_int(type, i, is_unsigned);
605}
606
607
608int network_mysqld_type_set_int(network_mysqld_type_t *type, guint64 i, gboolean is_unsigned) {
609 if (NULL == type->set_int) return -1;
610
611 return type->set_int(type, i, is_unsigned);
612}
613
614
615int network_mysqld_type_get_double(network_mysqld_type_t *type, double *d) {
616 if (NULL == type->get_double) return -1;
617
618 return type->get_double(type, d);
619}
620
621
622int network_mysqld_type_set_double(network_mysqld_type_t *type, double d) {
623 if (NULL == type->set_double) return -1;
624
625 return type->set_double(type, d);
626}
627
628
629int network_mysqld_type_get_date(network_mysqld_type_t *type, network_mysqld_type_date_t *date) {
630 if (NULL == type->get_date) return -1;
631
632 return type->get_date(type, date);
633}
634
635
636int network_mysqld_type_set_date(network_mysqld_type_t *type, network_mysqld_type_date_t *date) {
637 if (NULL == type->set_date) return -1;
638
639 return type->set_date(type, date);
640}
641
642
643int network_mysqld_type_get_time(network_mysqld_type_t *type, network_mysqld_type_time_t *t) {
644 if (NULL == type->get_time) return -1;
645
646 return type->get_time(type, t);
647}
648
649
650int network_mysqld_type_set_time(network_mysqld_type_t *type, network_mysqld_type_time_t *t) {
651 if (NULL == type->set_time) return -1;
652
653 return type->set_time(type, t);
654}
655
656
0657
=== added file 'src/network_mysqld_type.h'
--- src/network_mysqld_type.h 1970-01-01 00:00:00 +0000
+++ src/network_mysqld_type.h 2010-09-27 14:20:48 +0000
@@ -0,0 +1,147 @@
1#ifndef __NETWORK_MYSQLD_TYPE_H__
2#define __NETWORK_MYSQLD_TYPE_H__
3
4#include <mysql.h>
5#include <glib.h>
6
7#include "network-mysqld-proto.h"
8
9/**
10 * struct for the MYSQL_TYPE_DATE and friends
11 */
12typedef struct {
13 guint16 year;
14 guint8 month;
15 guint8 day;
16
17 guint8 hour;
18 guint8 min;
19 guint8 sec;
20
21 guint32 nsec; /* the nano-second part */
22} network_mysqld_type_date_t;
23
24#define NETWORK_MYSQLD_TYPE_DATE_MIN_BUF_LEN (sizeof("2010-10-27"))
25#define NETWORK_MYSQLD_TYPE_DATETIME_MIN_BUF_LEN (sizeof("2010-10-27 19:27:30.000000001"))
26#define NETWORK_MYSQLD_TYPE_TIMESTAMP_MIN_BUF_LEN NETWORK_MYSQLD_TYPE_DATETIME_MIN_BUF_LEN
27
28/**
29 * struct for the MYSQL_TYPE_TIME
30 */
31typedef struct {
32 guint8 sign;
33 guint32 days;
34
35 guint8 hour;
36 guint8 min;
37 guint8 sec;
38
39 guint32 nsec; /* the nano-second part */
40} network_mysqld_type_time_t;
41
42#define NETWORK_MYSQLD_TYPE_TIME_MIN_BUF_LEN (sizeof("-2147483647 19:27:30.000000001"))
43
44typedef struct _network_mysqld_type_t network_mysqld_type_t;
45
46struct _network_mysqld_type_t {
47 enum enum_field_types type;
48
49 gpointer data;
50 void (*free_data)(network_mysqld_type_t *type);
51
52 /**
53 * get a copy of ->data as GString
54 *
55 * @param type the type to get the data from
56 * @param s GString that the converted data will be assigned too
57 * @return 0 on success, -1 on error
58 */
59 int (*get_gstring)(network_mysqld_type_t *type, GString *s);
60 /**
61 * expose the ->data as constant string
62 *
63 * only available for types that have a "string" storage like _STRING, _CHAR, _BLOB
64 * the caller can copy the data out, but not change it
65 *
66 * @param type the type to get the data from
67 * @param s place to store the pointer to the const char * in
68 * @param s_len length of the const char *
69 * @return 0 on success, -1 on error
70 */
71 int (*get_string_const)(network_mysqld_type_t *type, const char **s, gsize *s_len);
72 /**
73 * get a copy of ->data as char *
74 *
75 * has 2 modes:
76 * - no-alloc-mode if *s is not NULL where it is expected that s and s_len point
77 * to a buffer of that size that we can copy into
78 * *s_len will contain the size of the stored string on success
79 * if *s_len is too small, -1 will be returned
80 * - alloc-mode when *s is NULL where we return a alloced buffer that is large enough
81 *
82 * @param type the type to get the data from
83 * @param s pointer to a buffer of *s_len size or pointer to (char *)NULL for alloc-mode
84 * @param s_len pointer to the length of the buffer if *s is not NULL. Points to the length of the *s on success
85 * @return 0 on success, -1 on error
86 */
87 int (*get_string)(network_mysqld_type_t *type, char **s, gsize *len);
88 /**
89 * set the ->data from a string
90 */
91 int (*set_string)(network_mysqld_type_t *type, const char *s, gsize s_len);
92 /**
93 * get ->data as uint64
94 */
95 int (*get_int)(network_mysqld_type_t *type, guint64 *i, gboolean *is_unsigned);
96 /**
97 * set ->data from uint64
98 */
99 int (*set_int)(network_mysqld_type_t *type, guint64 i, gboolean is_unsigned);
100 /**
101 * get ->data as double
102 */
103 int (*get_double)(network_mysqld_type_t *type, double *d);
104 /**
105 * set ->data from double
106 */
107 int (*set_double)(network_mysqld_type_t *type, double d);
108 int (*get_date)(network_mysqld_type_t *type, network_mysqld_type_date_t *date);
109 int (*set_date)(network_mysqld_type_t *type, network_mysqld_type_date_t *date);
110 /**
111 * get the ->data as _time_t
112 */
113 int (*get_time)(network_mysqld_type_t *type, network_mysqld_type_time_t *t);
114 /**
115 * set the ->data from a _time_t
116 */
117 int (*set_time)(network_mysqld_type_t *type, network_mysqld_type_time_t *t);
118
119
120 gboolean is_null; /**< is the value of this type NULL */
121 gboolean is_unsigned; /**< is the type signed or unsigned, only used by the integer types */
122};
123
124
125NETWORK_API network_mysqld_type_t *network_mysqld_type_new(enum enum_field_types _type);
126NETWORK_API void network_mysqld_type_free(network_mysqld_type_t *type);
127
128/**
129 * wrappers around the gettors and settors
130 *
131 * @return -1 if no settor or gettor defined or settor or gettor failed to convert
132 */
133NETWORK_API int network_mysqld_type_get_gstring(network_mysqld_type_t *type, GString *s);
134NETWORK_API int network_mysqld_type_get_gstring(network_mysqld_type_t *type, GString *s);
135NETWORK_API int network_mysqld_type_get_string_const(network_mysqld_type_t *type, const char **s, gsize *s_len);
136NETWORK_API int network_mysqld_type_get_string(network_mysqld_type_t *type, char **s, gsize *len);
137NETWORK_API int network_mysqld_type_set_string(network_mysqld_type_t *type, const char *s, gsize s_len);
138NETWORK_API int network_mysqld_type_get_int(network_mysqld_type_t *type, guint64 *i, gboolean *is_unsigned);
139NETWORK_API int network_mysqld_type_set_int(network_mysqld_type_t *type, guint64 i, gboolean is_unsigned);
140NETWORK_API int network_mysqld_type_get_double(network_mysqld_type_t *type, double *d);
141NETWORK_API int network_mysqld_type_set_double(network_mysqld_type_t *type, double d);
142NETWORK_API int network_mysqld_type_get_date(network_mysqld_type_t *type, network_mysqld_type_date_t *date);
143NETWORK_API int network_mysqld_type_set_date(network_mysqld_type_t *type, network_mysqld_type_date_t *date);
144NETWORK_API int network_mysqld_type_get_time(network_mysqld_type_t *type, network_mysqld_type_time_t *t);
145NETWORK_API int network_mysqld_type_set_time(network_mysqld_type_t *type, network_mysqld_type_time_t *t);
146
147#endif
0148
=== modified file 'tests/unit/CMakeLists.txt'
--- tests/unit/CMakeLists.txt 2010-02-24 11:02:26 +0000
+++ tests/unit/CMakeLists.txt 2010-09-27 14:20:48 +0000
@@ -113,6 +113,7 @@
113 ../../src/glib-ext.c 113 ../../src/glib-ext.c
114 ../../src/network-mysqld-proto.c 114 ../../src/network-mysqld-proto.c
115 ../../src/network-mysqld-packet.c 115 ../../src/network-mysqld-packet.c
116 ../../src/network_mysqld_type.c
116 ../../src/chassis-timings.c117 ../../src/chassis-timings.c
117 ../../src/my_rdtsc.c118 ../../src/my_rdtsc.c
118)119)
@@ -132,6 +133,7 @@
132 ../../src/glib-ext.c133 ../../src/glib-ext.c
133 ../../src/network-mysqld-proto.c134 ../../src/network-mysqld-proto.c
134 ../../src/network-mysqld-packet.c135 ../../src/network-mysqld-packet.c
136 ../../src/network_mysqld_type.c
135 ../../src/network-address.c137 ../../src/network-address.c
136)138)
137139
138140
=== modified file 'tests/unit/Makefile.am'
--- tests/unit/Makefile.am 2010-04-06 14:26:51 +0000
+++ tests/unit/Makefile.am 2010-09-27 14:20:48 +0000
@@ -32,6 +32,7 @@
32 t_network_backend \32 t_network_backend \
33 t_network_injection \33 t_network_injection \
34 t_network_mysqld_packet \34 t_network_mysqld_packet \
35 t_network_mysqld_type \
35 t_network_mysqld_masterinfo \36 t_network_mysqld_masterinfo \
36 t_chassis_timings \37 t_chassis_timings \
37 t_chassis_shutdown_hooks \38 t_chassis_shutdown_hooks \
@@ -81,10 +82,21 @@
81check_mysqld_proto_CPPFLAGS = -I$(top_srcdir)/src/ $(GLIB_CFLAGS) $(MYSQL_CFLAGS)82check_mysqld_proto_CPPFLAGS = -I$(top_srcdir)/src/ $(GLIB_CFLAGS) $(MYSQL_CFLAGS)
82check_mysqld_proto_LDADD = $(GLIB_LIBS)83check_mysqld_proto_LDADD = $(GLIB_LIBS)
8384
85t_network_mysqld_type_SOURCES = \
86 t_network_mysqld_type.c \
87 $(top_srcdir)/src/network_mysqld_type.c \
88 $(top_srcdir)/src/glib-ext.c
89
90t_network_mysqld_type_CPPFLAGS = -I$(top_srcdir)/src/ $(GLIB_CFLAGS) $(MYSQL_CFLAGS) $(LUA_CFLAGS)
91t_network_mysqld_type_LDADD = $(GLIB_LIBS) $(LUA_LIBS) $(EVENT_LIBS)
92
93
84t_network_mysqld_packet_SOURCES = \94t_network_mysqld_packet_SOURCES = \
85 t_network_mysqld_packet.c \95 t_network_mysqld_packet.c \
86 $(top_srcdir)/src/network-mysqld-packet.c \96 $(top_srcdir)/src/network-mysqld-packet.c \
87 $(top_srcdir)/src/network-mysqld-proto.c \97 $(top_srcdir)/src/network-mysqld-proto.c \
98 $(top_srcdir)/src/network_mysqld_type.c \
99 $(top_srcdir)/src/network_mysqld_proto_binary.c \
88 $(top_srcdir)/src/network-queue.c \100 $(top_srcdir)/src/network-queue.c \
89 $(top_srcdir)/src/network-socket.c \101 $(top_srcdir)/src/network-socket.c \
90 $(top_srcdir)/src/network-address.c \102 $(top_srcdir)/src/network-address.c \
@@ -126,6 +138,8 @@
126 $(top_srcdir)/src/glib-ext.c \138 $(top_srcdir)/src/glib-ext.c \
127 $(top_srcdir)/src/network-mysqld-proto.c \139 $(top_srcdir)/src/network-mysqld-proto.c \
128 $(top_srcdir)/src/network-mysqld-packet.c \140 $(top_srcdir)/src/network-mysqld-packet.c \
141 $(top_srcdir)/src/network_mysqld_type.c \
142 $(top_srcdir)/src/network_mysqld_proto_binary.c \
129 $(top_srcdir)/src/network-address.c \143 $(top_srcdir)/src/network-address.c \
130 $(top_srcdir)/src/network-queue.c \144 $(top_srcdir)/src/network-queue.c \
131 $(top_srcdir)/src/network-socket.c145 $(top_srcdir)/src/network-socket.c
@@ -177,6 +191,8 @@
177 $(top_srcdir)/src/network-backend.c \191 $(top_srcdir)/src/network-backend.c \
178 $(top_srcdir)/src/network-mysqld-proto.c \192 $(top_srcdir)/src/network-mysqld-proto.c \
179 $(top_srcdir)/src/network-mysqld-packet.c \193 $(top_srcdir)/src/network-mysqld-packet.c \
194 $(top_srcdir)/src/network_mysqld_type.c \
195 $(top_srcdir)/src/network_mysqld_proto_binary.c \
180 $(top_srcdir)/src/network-conn-pool.c \196 $(top_srcdir)/src/network-conn-pool.c \
181 $(top_srcdir)/src/network-address.c \197 $(top_srcdir)/src/network-address.c \
182 $(top_srcdir)/src/network-queue.c \198 $(top_srcdir)/src/network-queue.c \
@@ -204,6 +220,8 @@
204 $(top_srcdir)/src/glib-ext.c \220 $(top_srcdir)/src/glib-ext.c \
205 $(top_srcdir)/src/network-mysqld-proto.c \221 $(top_srcdir)/src/network-mysqld-proto.c \
206 $(top_srcdir)/src/network-mysqld-packet.c \222 $(top_srcdir)/src/network-mysqld-packet.c \
223 $(top_srcdir)/src/network_mysqld_type.c \
224 $(top_srcdir)/src/network_mysqld_proto_binary.c \
207 $(top_srcdir)/src/network-injection.c \225 $(top_srcdir)/src/network-injection.c \
208 $(top_srcdir)/src/my_rdtsc.c \226 $(top_srcdir)/src/my_rdtsc.c \
209 $(top_srcdir)/src/chassis-timings.c227 $(top_srcdir)/src/chassis-timings.c
210228
=== modified file 'tests/unit/lua/mysql-proto.lua'
--- tests/unit/lua/mysql-proto.lua 2010-04-06 14:26:51 +0000
+++ tests/unit/lua/mysql-proto.lua 2010-09-27 14:20:48 +0000
@@ -19,7 +19,7 @@
19 $%ENDLICENSE%$ --]]19 $%ENDLICENSE%$ --]]
20local proto = assert(require("mysql.proto"))20local proto = assert(require("mysql.proto"))
21local password = assert(require("mysql.password"))21local password = assert(require("mysql.password"))
2222require("proxy.test")
23---23---
24-- err packet24-- err packet
2525
@@ -265,3 +265,72 @@
265265
266assert(false == password.check(challenge, response, dbl_hashed))266assert(false == password.check(challenge, response, dbl_hashed))
267267
268---
269-- prepared stmt decoders
270--
271
272-- EXECUTE packet, no params
273local packet = "\023\001\000\000\000\000\001\000\000\000"
274local execute = proto.from_stmt_execute_packet(packet, 0)
275assert(execute)
276assertEquals(execute.stmt_id, 1)
277assertEquals(execute.flags, 0)
278assertEquals(execute.iteration_count, 1)
279assertEquals(execute.new_params_bound, false)
280
281-- EXECUTE packet with 14 params
282local packet = "\023" ..
283 "\001\000\000\000" ..
284 "\000" ..
285 "\001\000\000\000" ..
286 "\003\000" ..
287 "\001" ..
288 "\254\000\006\000\254\000\008\000\008\128\003\000\002\000\001\000\005\000\004\000\010\000\012\000\007\000\011\000" ..
289 "\003\102\111\111" ..
290 "\001\000\000\000\000\000\000\000" ..
291 "\001\000\000\000\000\000\000\000" ..
292 "\001\000\000\000" ..
293 "\001\000" ..
294 "\001" ..
295 "\102\102\102\102\102\102\036\064" ..
296 "\000\000\036\065" ..
297 "\004\218\007\010\017" ..
298 "\011\218\007\010\017\019\027\030\001\000\000\000" ..
299 "\011\218\007\010\017\019\027\030\001\000\000\000" ..
300 "\012\001\120\000\000\000\019\027\030\001\000\000\000"
301
302local execute = proto.from_stmt_execute_packet(packet, 14)
303assert(execute)
304assertEquals(execute.stmt_id, 1)
305assertEquals(execute.flags, 0)
306assertEquals(execute.iteration_count, 1)
307assertEquals(execute.new_params_bound, true)
308
309local params = execute.params
310assert(params)
311assertEquals(#params, 14)
312
313local expected_params = {
314 { type = 254, value = nil },
315 { type = 6, value = nil },
316 { type = 254, value = "foo" },
317 { type = 8, value = 1 },
318 { type = 8, value = 1 },
319 { type = 3, value = 1 },
320 { type = 2, value = 1 },
321 { type = 1, value = 1 },
322 { type = 5, value = 10.2 }, --[[ double ]]--
323 { type = 4, value = 10.25 }, --[[ float ]]--
324 { type = 10, value = "2010-10-17" }, --[[ date ]]--
325 { type = 12, value = "2010-10-17 19:27:30.000000001" }, --[[ datetime ]]--
326 { type = 7, value = "2010-10-17 19:27:30.000000001" }, --[[ timestamp ]]--
327 { type = 11, value = "-120 19:27:30.000000001" }, --[[ time ]]--
328}
329
330for ndx, expected_param in ipairs(expected_params) do
331 local param = params[ndx]
332 assert(param)
333 assertEquals(param.type, expected_param.type)
334 assertEquals(param.value, expected_param.value)
335end
336
268337
=== modified file 'tests/unit/t_network_mysqld_packet.c'
--- tests/unit/t_network_mysqld_packet.c 2010-04-06 14:26:51 +0000
+++ tests/unit/t_network_mysqld_packet.c 2010-09-27 14:20:48 +0000
@@ -27,6 +27,7 @@
2727
28#include "network-mysqld-proto.h"28#include "network-mysqld-proto.h"
29#include "network-mysqld-packet.h"29#include "network-mysqld-packet.h"
30#include "network_mysqld_type.h"
30#include "glib-ext.h"31#include "glib-ext.h"
3132
32#if GLIB_CHECK_VERSION(2, 16, 0)33#if GLIB_CHECK_VERSION(2, 16, 0)
@@ -695,6 +696,624 @@
695 network_queue_free(q);696 network_queue_free(q);
696}697}
697698
699/* prepared statements */
700
701/* COM_STMT_PREPARE */
702static void t_com_stmt_prepare_new(void) {
703 network_mysqld_stmt_prepare_packet_t *cmd;
704
705 cmd = network_mysqld_stmt_prepare_packet_new();
706 g_assert(cmd);
707
708 network_mysqld_stmt_prepare_packet_free(cmd);
709}
710
711static void t_com_stmt_prepare_from_packet(void) {
712 network_mysqld_stmt_prepare_packet_t *cmd;
713 const char raw_packet[] = "\x1c\x00\x00\x00\x16SELECT CONCAT(?, ?) AS col1";
714 network_packet packet;
715
716 packet.data = g_string_new_len(C(raw_packet));
717 packet.offset = 0;
718
719 cmd = network_mysqld_stmt_prepare_packet_new();
720 g_assert_cmpint(0, ==, network_mysqld_proto_skip_network_header(&packet));
721 g_assert_cmpint(0, ==, network_mysqld_proto_get_stmt_prepare_packet(&packet, cmd));
722 g_assert_cmpint(sizeof("SELECT CONCAT(?, ?) AS col1") - 1, ==, cmd->stmt_text->len);
723 g_assert_cmpstr("SELECT CONCAT(?, ?) AS col1", ==, cmd->stmt_text->str);
724
725 network_mysqld_stmt_prepare_packet_free(cmd);
726}
727
728/* COM_STMT_PREPARE OK-result */
729
730static void t_com_stmt_prepare_ok_new(void) {
731 network_mysqld_stmt_prepare_ok_packet_t *cmd;
732
733 cmd = network_mysqld_stmt_prepare_ok_packet_new();
734 g_assert(cmd);
735
736 network_mysqld_stmt_prepare_ok_packet_free(cmd);
737}
738
739/**
740 * test if we parse all the fields of a COM_STMT_PREPARE-ok response correctly
741 */
742static void t_com_stmt_prepare_ok_from_packet(void) {
743 network_mysqld_stmt_prepare_ok_packet_t *cmd;
744 network_mysqld_eof_packet_t *eof;
745 network_mysqld_proto_fielddef_t *coldef;
746
747 /* a response for the COM_STMT_PREPARE command
748 *
749 * the OK part with stmt-id and so on is in the first packet. The others are
750 * the field-defs, a EOF, the param-defs, and the last EOF */
751 strings packets[] = {
752 { C("\x0c\x00\x00\x01\x00\x01\x00\x00\x00\x01\x00\x02\x00\x00\x00\x00") }, /* the PREPARE OK packet */
753 { C("\x17\x00\x00\x02\x03\x64\x65\x66\x00\x00\x00\x01\x3f\x00\x0c\x3f\x00\x00\x00\x00\x00\xfd\x80\x00\x00\x00\x00") }, /* column-def: param 1 */
754 { C("\x17\x00\x00\x03\x03\x64\x65\x66\x00\x00\x00\x01\x3f\x00\x0c\x3f\x00\x00\x00\x00\x00\xfd\x80\x00\x00\x00\x00") }, /* column-def: param 2 */
755 { C("\x05\x00\x00\x04\xfe\x00\x00\x02\x00") }, /* the seperator */
756 { C("\x1a\x00\x00\x05\x03\x64\x65\x66\x00\x00\x00\x04\x63\x6f\x6c\x31\x00\x0c\x3f\x00\x00\x00\x00\x00\xfd\x80\x00\x1f\x00\x00") }, /* column-def: result-col 1 */
757 { C("\x05\x00\x00\x06\xfe\x00\x00\x02\x00") } /* the terminator */
758 };
759 network_packet packet;
760
761 packet.data = g_string_new_len(packets[0].s, packets[0].s_len);
762 packet.offset = 0;
763
764 cmd = network_mysqld_stmt_prepare_ok_packet_new();
765 g_assert_cmpint(0, ==, network_mysqld_proto_skip_network_header(&packet));
766 g_assert_cmpint(0, ==, network_mysqld_proto_get_stmt_prepare_ok_packet(&packet, cmd));
767 g_assert_cmpint(1, ==, cmd->stmt_id);
768 g_assert_cmpint(1, ==, cmd->num_columns);
769 g_assert_cmpint(2, ==, cmd->num_params);
770 g_assert_cmpint(0, ==, cmd->warnings);
771
772 network_mysqld_stmt_prepare_ok_packet_free(cmd);
773
774 g_assert_cmpint(packet.offset, ==, packet.data->len); /* is everything parsed */
775 g_string_free(packet.data, TRUE);
776
777 packet.data = g_string_new_len(packets[1].s, packets[1].s_len);
778 packet.offset = 0;
779
780 coldef = network_mysqld_proto_fielddef_new();
781 g_assert_cmpint(0, ==, network_mysqld_proto_skip_network_header(&packet));
782 g_assert_cmpint(0, ==, network_mysqld_proto_get_fielddef(&packet, coldef, CLIENT_PROTOCOL_41));
783
784 network_mysqld_proto_fielddef_free(coldef);
785
786 g_assert_cmpint(packet.offset, ==, packet.data->len); /* is everything parsed */
787 g_string_free(packet.data, TRUE);
788
789
790 packet.data = g_string_new_len(packets[2].s, packets[2].s_len);
791 packet.offset = 0;
792
793 coldef = network_mysqld_proto_fielddef_new();
794 g_assert_cmpint(0, ==, network_mysqld_proto_skip_network_header(&packet));
795 g_assert_cmpint(0, ==, network_mysqld_proto_get_fielddef(&packet, coldef, CLIENT_PROTOCOL_41));
796 network_mysqld_proto_fielddef_free(coldef);
797
798 g_assert_cmpint(packet.offset, ==, packet.data->len); /* is everything parsed */
799 g_string_free(packet.data, TRUE);
800
801
802 packet.data = g_string_new_len(packets[3].s, packets[3].s_len);
803 packet.offset = 0;
804
805 eof = network_mysqld_eof_packet_new();
806 g_assert_cmpint(0, ==, network_mysqld_proto_skip_network_header(&packet));
807 g_assert_cmpint(0, ==, network_mysqld_proto_get_eof_packet(&packet, eof));
808
809 network_mysqld_eof_packet_free(eof);
810
811 g_assert_cmpint(packet.offset, ==, packet.data->len); /* is everything parsed */
812 g_string_free(packet.data, TRUE);
813
814
815 packet.data = g_string_new_len(packets[4].s, packets[4].s_len);
816 packet.offset = 0;
817
818 coldef = network_mysqld_proto_fielddef_new();
819 g_assert_cmpint(0, ==, network_mysqld_proto_skip_network_header(&packet));
820 g_assert_cmpint(0, ==, network_mysqld_proto_get_fielddef(&packet, coldef, CLIENT_PROTOCOL_41));
821 network_mysqld_proto_fielddef_free(coldef);
822
823 g_assert_cmpint(packet.offset, ==, packet.data->len); /* is everything parsed */
824 g_string_free(packet.data, TRUE);
825
826
827 packet.data = g_string_new_len(packets[5].s, packets[5].s_len);
828 packet.offset = 0;
829
830 eof = network_mysqld_eof_packet_new();
831 g_assert_cmpint(0, ==, network_mysqld_proto_skip_network_header(&packet));
832 g_assert_cmpint(0, ==, network_mysqld_proto_get_eof_packet(&packet, eof));
833
834 network_mysqld_eof_packet_free(eof);
835
836 g_assert_cmpint(packet.offset, ==, packet.data->len); /* is everything parsed */
837 g_string_free(packet.data, TRUE);
838}
839
840/* COM_STMT_EXECUTE */
841
842static void t_com_stmt_execute_new(void) {
843 network_mysqld_stmt_execute_packet_t *cmd;
844
845 cmd = network_mysqld_stmt_execute_packet_new();
846 g_assert(cmd);
847
848 network_mysqld_stmt_execute_packet_free(cmd);
849}
850
851/**
852 * test if we decode all valid types from EXECUTE stmt
853 */
854static void t_com_stmt_execute_from_packet(void) {
855 network_mysqld_stmt_execute_packet_t *cmd;
856 const char raw_packet[] =
857 "\x7a\x00\x00\x00"
858 "\x17" /* COM_STMT_EXECUTE */
859 "\x01\x00\x00\x00" /* stmt-id */
860 "\x00" /* flags */
861 "\x01\x00\x00\x00" /* iteration count */
862 "\x03\x00" /* nul-flags */
863 "\x01" /* yeah, we have parameters */
864 "\xfe\x00\x06\x00\xfe\x00\x08\x00\x08\x80\x03\x00\x02\x00\x01\x00\x05\x00\x04\x00\x0a\x00\x0c\x00\x07\x00\x0b\x00" /* param-defs */
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches