1 /* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License, version 2.0,
5 as published by the Free Software Foundation.
6
7 This program is also distributed with certain software (including
8 but not limited to OpenSSL) that is licensed under separate terms,
9 as designated in a particular file or component or in included license
10 documentation. The authors of MySQL hereby grant you an additional
11 permission to link the program and your derivative works with the
12 separately licensed software that they have included with MySQL.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License, version 2.0, for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
22
23 #define LOG_COMPONENT_TAG "test_sql_stmt"
24
25 #include <fcntl.h>
26 #include <mysql/plugin.h>
27 #include <stdlib.h>
28 #include <sys/types.h>
29 #include <vector>
30
31 #include <mysql/components/my_service.h>
32 #include <mysql/components/services/log_builtins.h>
33 #include <mysqld_error.h>
34
35 #include "m_string.h"
36 #include "my_byteorder.h"
37 #include "my_dbug.h"
38 #include "my_inttypes.h"
39 #include "my_io.h"
40 #include "my_sys.h" // my_write, my_malloc
41 #include "mysql_com.h"
42 #include "sql_string.h" /* STRING_PSI_MEMORY_KEY */
43 #include "template_utils.h"
44
45 /* purecov: begin inspected */
46 static const char *log_filename = "test_sql_stmt";
47
48 #define STRING_BUFFER_SIZE 1024
49 #define LARGE_STRING_BUFFER_SIZE 1024
50
51 #define WRITE_STR(format) \
52 { \
53 const size_t blen = snprintf(buffer, sizeof(buffer), "%s", (format)); \
54 my_write(outfile, (uchar *)buffer, blen, MYF(0)); \
55 /*pctx->log.append(buffer, blen); */ \
56 }
57
58 #define WRITE_VAL(format, value) \
59 { \
60 const size_t blen = snprintf(buffer, sizeof(buffer), (format), (value)); \
61 my_write(outfile, (uchar *)buffer, blen, MYF(0)); \
62 /* pctx->log.append(buffer, blen); */ \
63 }
64
65 #define WRITE_VAL2(format, value1, value2) \
66 { \
67 const size_t blen = \
68 snprintf(buffer, sizeof(buffer), (format), (value1), (value2)); \
69 my_write(outfile, (uchar *)buffer, blen, MYF(0)); \
70 /* pctx->log.append(buffer, blen); */ \
71 }
72
73 static const char *sep =
74 "========================================================================"
75 "\n";
76
77 #define WRITE_SEP() \
78 my_write(outfile, pointer_cast<const uchar *>(sep), strlen(sep), MYF(0))
79
80 static SERVICE_TYPE(registry) *reg_srv = nullptr;
81 SERVICE_TYPE(log_builtins) *log_bi = nullptr;
82 SERVICE_TYPE(log_builtins_string) *log_bs = nullptr;
83 static File outfile;
84
85 #define SIZEOF_SQL_STR_VALUE 256
86
87 static void print_cmd(enum_server_command cmd, COM_DATA *data);
88 static char *fieldflags2str(uint f);
89 static const char *fieldtype2str(enum enum_field_types type);
90 static void dump_decoded_server_status(const char *prefix, uint server_status);
91
92 class Column {
93 public:
Column(const char * db_name,const char * table_name,const char * org_table_name,const char * col_name,const char * org_col_name,unsigned long length,unsigned int charsetnr,unsigned int flags,unsigned int decimals,enum_field_types type)94 Column(const char *db_name, const char *table_name,
95 const char *org_table_name, const char *col_name,
96 const char *org_col_name, unsigned long length, unsigned int charsetnr,
97 unsigned int flags, unsigned int decimals, enum_field_types type)
98 : db_name(db_name),
99 table_name(table_name),
100 org_table_name(org_table_name),
101 col_name(col_name),
102 org_col_name(org_col_name),
103 length(length),
104 charsetnr(charsetnr),
105 flags(flags),
106 decimals(decimals),
107 type(type) {}
108
109 std::vector<std::string> row_values;
110 std::string db_name;
111 std::string table_name;
112 std::string org_table_name;
113 std::string col_name;
114 std::string org_col_name;
115 unsigned long length;
116 unsigned int charsetnr;
117 unsigned int flags;
118 unsigned int decimals;
119 enum_field_types type;
120
dump_column_meta()121 void dump_column_meta() {
122 char buffer[STRING_BUFFER_SIZE];
123
124 WRITE_VAL("\t\t[meta][field] db name: %s\n", db_name.c_str());
125 WRITE_VAL("\t\t[meta][field] table name: %s\n", table_name.c_str());
126 WRITE_VAL("\t\t[meta][field] org table name: %s\n", org_table_name.c_str());
127 WRITE_VAL("\t\t[meta][field] col name: %s\n", col_name.c_str());
128 WRITE_VAL("\t\t[meta][field] org col name: %s\n", org_col_name.c_str());
129 WRITE_VAL("\t\t[meta][field] length: %u\n", (uint)length);
130 WRITE_VAL("\t\t[meta][field] charsetnr: %u\n", charsetnr);
131
132 WRITE_VAL("\t\t[meta][field] flags: %u", flags);
133 if (flags) WRITE_VAL(" (%s)", fieldflags2str(flags));
134 WRITE_STR("\n");
135
136 WRITE_VAL("\t\t[meta][field] decimals: %u\n", decimals);
137
138 WRITE_VAL2("\t\t[meta][field] type: %s (%u)\n", fieldtype2str(type), type);
139 WRITE_STR("\n");
140 }
141
dump_row(size_t row_number)142 void dump_row(size_t row_number) {
143 char buffer[STRING_BUFFER_SIZE];
144 WRITE_VAL2("\t\t[data][%s.%s]", table_name.c_str(), col_name.c_str());
145 WRITE_VAL2("[%3u][%s]\n", (uint)row_values[row_number].length(),
146 row_values[row_number].c_str());
147 }
148 };
149
150 class Table {
151 public:
152 uint num_cols;
153 uint num_rows;
154 const CHARSET_INFO *cs_info;
155 std::vector<Column> columns;
156
157 public:
Table(uint num_cols,const CHARSET_INFO * cs_info)158 Table(uint num_cols, const CHARSET_INFO *cs_info)
159 : num_cols(num_cols), num_rows(0), cs_info(cs_info) {}
160
dump_table()161 void dump_table() {
162 char buffer[STRING_BUFFER_SIZE];
163
164 if (!num_cols) {
165 WRITE_STR("\t[meta] no columns\n");
166 return;
167 }
168 for (auto &&column : columns) column.dump_column_meta();
169
170 WRITE_STR("\n");
171 if (!cs_info) {
172 WRITE_STR("\t[meta] no charset\n");
173 return;
174 } else {
175 WRITE_VAL("\t[meta][charset result] number: %d\n", cs_info->number);
176 WRITE_VAL("\t[meta][charset result] name: %s\n", cs_info->csname);
177 WRITE_VAL("\t[meta][charset result] collation: %s\n", cs_info->name);
178 WRITE_VAL("\t[meta][charset result] sort order: %s\n",
179 cs_info->sort_order);
180 WRITE_STR("\n");
181 }
182
183 for (size_t i = 0; i < num_rows; i++) {
184 size_t col = 0;
185 for (auto &&column : columns) {
186 WRITE_VAL("\t[meta] current col: %zu\n", col);
187 col++;
188 column.dump_row(i);
189 }
190 WRITE_STR("\n");
191 }
192 }
193 };
194
195 class Server_context {
196 public:
197 std::vector<Table> tables;
198 uint current_col;
199 uint current_row;
200
201 ulong stmt_id;
202 enum_server_command cmd;
203
204 uint server_status;
205 uint warn_count;
206 uint affected_rows;
207 uint last_insert_id;
208 std::string message;
209
210 uint sql_errno;
211 std::string err_msg;
212 std::string sqlstate;
213
214 std::string log;
215
Server_context()216 Server_context()
217 : current_col(0),
218 current_row(0),
219 server_status(0),
220 warn_count(0),
221 affected_rows(0),
222 last_insert_id(0),
223 sql_errno(0) {
224 err_msg.clear();
225 sqlstate.clear();
226 message.clear();
227 log.clear();
228 }
dump_closing_ok()229 void dump_closing_ok() {
230 char buffer[STRING_BUFFER_SIZE];
231
232 dump_decoded_server_status("\t[end] server status: ", server_status);
233 WRITE_VAL("\t[end] warning count: %u\n", warn_count);
234 WRITE_VAL("\t[end] affected rows: %u\n", affected_rows);
235 WRITE_VAL("\t[end] last insert id: %u\n", last_insert_id);
236 WRITE_VAL("\t[end] message: %s\n", message.c_str());
237 }
238
dump_closing_error()239 void dump_closing_error() {
240 char buffer[STRING_BUFFER_SIZE];
241
242 WRITE_VAL2("[%u][%s]", sql_errno, sqlstate.c_str());
243 WRITE_VAL("[%s]\n", err_msg.c_str());
244 }
245 };
246
dump_decoded_server_status(const char * prefix,uint server_status)247 static void dump_decoded_server_status(const char *prefix, uint server_status) {
248 char buffer[STRING_BUFFER_SIZE];
249 WRITE_STR(prefix);
250 WRITE_VAL("%u\n", server_status);
251 WRITE_STR(prefix);
252 for (int i = 0; i < 30; i++) {
253 uint flag = 1 << i;
254 if (server_status & flag) {
255 #define FLAG_DELIMITER " "
256 switch (flag) {
257 case SERVER_STATUS_IN_TRANS:
258 WRITE_STR("IN_TRANS" FLAG_DELIMITER);
259 break;
260 case SERVER_STATUS_AUTOCOMMIT:
261 WRITE_STR("AUTOCOMMIT" FLAG_DELIMITER);
262 break;
263 case SERVER_MORE_RESULTS_EXISTS:
264 WRITE_STR("MORE_RESULTS_EXISTS" FLAG_DELIMITER);
265 break;
266 case SERVER_QUERY_NO_GOOD_INDEX_USED:
267 WRITE_STR("QUERY_NO_GOOD_INDEX_USED" FLAG_DELIMITER);
268 break;
269 case SERVER_QUERY_NO_INDEX_USED:
270 WRITE_STR("QUERY_NO_INDEX_USED" FLAG_DELIMITER);
271 break;
272 case SERVER_STATUS_CURSOR_EXISTS:
273 WRITE_STR("CURSOR_EXISTS" FLAG_DELIMITER);
274 break;
275 case SERVER_STATUS_LAST_ROW_SENT:
276 WRITE_STR("LAST_ROW_SENT" FLAG_DELIMITER);
277 break;
278 case SERVER_STATUS_DB_DROPPED:
279 WRITE_STR("DB_DROPPED" FLAG_DELIMITER);
280 break;
281 case SERVER_STATUS_NO_BACKSLASH_ESCAPES:
282 WRITE_STR("NO_BACKSLASH_ESCAPES" FLAG_DELIMITER);
283 break;
284 case SERVER_STATUS_METADATA_CHANGED:
285 WRITE_STR("METADATA_CHANGED" FLAG_DELIMITER);
286 break;
287 case SERVER_QUERY_WAS_SLOW:
288 WRITE_STR("QUERY_WAS_SLOW" FLAG_DELIMITER);
289 break;
290 case SERVER_PS_OUT_PARAMS:
291 WRITE_STR("PS_OUT_PARAMS" FLAG_DELIMITER);
292 break;
293 case SERVER_STATUS_IN_TRANS_READONLY:
294 WRITE_STR("IN_TRANS_READONLY" FLAG_DELIMITER);
295 break;
296 case SERVER_SESSION_STATE_CHANGED:
297 WRITE_STR("STATE_CHANGED" FLAG_DELIMITER);
298 break;
299 default:
300 // Add a new flag defined in mysql_com.h above to fix this
301 WRITE_VAL("UNKNOWN_%u\n", flag);
302 }
303 #undef FLAG_DELIMITER
304 }
305 }
306 WRITE_STR("\n");
307 }
308
handle_start_column_metadata(void * pctx,uint num_cols,uint,const CHARSET_INFO * resultcs)309 static int handle_start_column_metadata(void *pctx, uint num_cols, uint,
310 const CHARSET_INFO *resultcs) {
311 Server_context *ctx = (Server_context *)pctx;
312 char buffer[STRING_BUFFER_SIZE];
313 WRITE_STR("handle_start_column_metadata\n");
314 DBUG_TRACE;
315 DBUG_PRINT("info", ("resultcs->number: %d", resultcs->number));
316 DBUG_PRINT("info", ("resultcs->csname: %s", resultcs->csname));
317 DBUG_PRINT("info", ("resultcs->name: %s", resultcs->name));
318
319 ctx->tables.push_back(Table(num_cols, resultcs));
320 ctx->current_col = 0;
321
322 return false;
323 }
324
handle_send_column_metadata(void * pctx,struct st_send_field * field,const CHARSET_INFO *)325 static int handle_send_column_metadata(void *pctx, struct st_send_field *field,
326 const CHARSET_INFO *) {
327 Server_context *ctx = (Server_context *)pctx;
328 // char buffer[STRING_BUFFER_SIZE];
329 // WRITE_STR("handle_send_column_metadata\n");
330 DBUG_TRACE;
331 DBUG_PRINT("info", ("field->db_name: %s", field->db_name));
332 DBUG_PRINT("info", ("field->table_name: %s", field->table_name));
333 DBUG_PRINT("info", ("field->org_table_name: %s", field->org_table_name));
334 DBUG_PRINT("info", ("field->col_name: %s", field->col_name));
335 DBUG_PRINT("info", ("field->org_col_name: %s", field->org_col_name));
336 DBUG_PRINT("info", ("field->length: %d", (int)field->length));
337 DBUG_PRINT("info", ("field->charsetnr: %d", (int)field->charsetnr));
338 DBUG_PRINT("info", ("field->flags: %d", (int)field->flags));
339 DBUG_PRINT("info", ("field->decimals: %d", (int)field->decimals));
340 DBUG_PRINT("info", ("field->type: %d", (int)field->type));
341
342 ctx->tables.back().columns.push_back(
343 Column(field->db_name, field->table_name, field->org_table_name,
344 field->col_name, field->org_col_name, field->length,
345 field->charsetnr, field->flags, field->decimals, field->type));
346 ctx->current_col++;
347 return false;
348 }
349
handle_end_column_metadata(void * pctx,uint server_status,uint warn_count)350 static int handle_end_column_metadata(void *pctx, uint server_status,
351 uint warn_count) {
352 char buffer[STRING_BUFFER_SIZE];
353 Server_context *ctx = (Server_context *)pctx;
354 DBUG_TRACE;
355 ctx->server_status = server_status;
356 ctx->warn_count = warn_count;
357
358 ctx->current_row = 0;
359
360 WRITE_STR("handle_end_column_metadata\n");
361 return false;
362 }
363
handle_start_row(void * pctx)364 static int handle_start_row(void *pctx) {
365 Server_context *ctx = (Server_context *)pctx;
366 char buffer[STRING_BUFFER_SIZE];
367 WRITE_STR("handle_start_row\n");
368 DBUG_TRACE;
369 ctx->current_col = 0;
370 return false;
371 }
372
handle_end_row(void * pctx)373 static int handle_end_row(void *pctx) {
374 Server_context *ctx = (Server_context *)pctx;
375 char buffer[STRING_BUFFER_SIZE];
376 DBUG_TRACE;
377 WRITE_STR("handle_end_row\n");
378
379 // Get the generated statement id
380 if (ctx->cmd == COM_STMT_PREPARE && ctx->current_row == 0 &&
381 ctx->tables.size() == 1 && ctx->tables[0].columns.size() == 4 &&
382 ctx->tables[0].columns[0].row_values.size() == 1) {
383 ctx->stmt_id =
384 std::stoul(ctx->tables[0].columns[0].row_values[0], nullptr, 10);
385 }
386 ctx->tables.back().num_rows++;
387 ctx->current_row++;
388 return false;
389 }
390
handle_abort_row(void *)391 static void handle_abort_row(void *) {
392 char buffer[STRING_BUFFER_SIZE];
393 WRITE_STR("handle_abort_row\n");
394 DBUG_TRACE;
395 }
396
get_client_capabilities(void *)397 static ulong get_client_capabilities(void *) {
398 DBUG_TRACE;
399 return CLIENT_PS_MULTI_RESULTS | CLIENT_MULTI_RESULTS;
400 }
401
handle_store_null(void * pctx)402 static int handle_store_null(void *pctx) {
403 Server_context *ctx = (Server_context *)pctx;
404 // WRITE_STR("handle_store_null\n");
405 DBUG_TRACE;
406 uint col = ctx->current_col;
407 ctx->current_col++;
408 ctx->tables.back().columns[col].row_values.push_back("[NULL]");
409
410 return false;
411 }
412
handle_store_integer(void * pctx,longlong value)413 static int handle_store_integer(void *pctx, longlong value) {
414 char buffer[LARGE_STRING_BUFFER_SIZE];
415 Server_context *ctx = (Server_context *)pctx;
416 DBUG_TRACE;
417 uint col = ctx->current_col;
418 ctx->current_col++;
419
420 size_t len = snprintf(buffer, sizeof(buffer), "%lld", value);
421
422 ctx->tables.back().columns[col].row_values.push_back(
423 std::string(buffer, len));
424
425 return false;
426 }
427
handle_store_longlong(void * pctx,longlong value,uint is_unsigned)428 static int handle_store_longlong(void *pctx, longlong value, uint is_unsigned) {
429 char buffer[LARGE_STRING_BUFFER_SIZE];
430 Server_context *ctx = (Server_context *)pctx;
431 DBUG_TRACE;
432 uint col = ctx->current_col;
433 ctx->current_col++;
434
435 size_t len =
436 snprintf(buffer, sizeof(buffer), is_unsigned ? "%llu" : "%lld", value);
437
438 ctx->tables.back().columns[col].row_values.push_back(
439 std::string(buffer, len));
440
441 return false;
442 }
443
test_decimal_as_string(char * buff,const decimal_t * val,int * length)444 static const char *test_decimal_as_string(char *buff, const decimal_t *val,
445 int *length) {
446 if (!val) return "NULL";
447 (void)decimal2string(val, buff, length);
448 return buff;
449 }
450
handle_store_decimal(void * pctx,const decimal_t * value)451 static int handle_store_decimal(void *pctx, const decimal_t *value) {
452 char buffer[LARGE_STRING_BUFFER_SIZE];
453 Server_context *ctx = (Server_context *)pctx;
454 DBUG_TRACE;
455 uint col = ctx->current_col;
456 ctx->current_col++;
457
458 int len = SIZEOF_SQL_STR_VALUE;
459 test_decimal_as_string(buffer, value, &len);
460 ctx->tables.back().columns[col].row_values.push_back(
461 std::string(buffer, len));
462
463 return false;
464 }
465
handle_store_double(void * pctx,double value,uint32)466 static int handle_store_double(void *pctx, double value, uint32) {
467 char buffer[LARGE_STRING_BUFFER_SIZE];
468 Server_context *ctx = (Server_context *)pctx;
469 DBUG_TRACE;
470 uint col = ctx->current_col;
471 ctx->current_col++;
472
473 size_t len = snprintf(buffer, sizeof(buffer), "%3.7g", value);
474 ctx->tables.back().columns[col].row_values.push_back(
475 std::string(buffer, len));
476
477 return false;
478 }
479
handle_store_date(void * pctx,const MYSQL_TIME * value)480 static int handle_store_date(void *pctx, const MYSQL_TIME *value) {
481 char buffer[LARGE_STRING_BUFFER_SIZE];
482 Server_context *ctx = (Server_context *)pctx;
483 DBUG_TRACE;
484 uint col = ctx->current_col;
485 ctx->current_col++;
486
487 size_t len =
488 snprintf(buffer, sizeof(buffer), "%s%4d-%02d-%02d", value->neg ? "-" : "",
489 value->year, value->month, value->day);
490
491 ctx->tables.back().columns[col].row_values.push_back(
492 std::string(buffer, len));
493
494 return false;
495 }
496
handle_store_time(void * pctx,const MYSQL_TIME * value,uint)497 static int handle_store_time(void *pctx, const MYSQL_TIME *value, uint) {
498 char buffer[LARGE_STRING_BUFFER_SIZE];
499 Server_context *ctx = (Server_context *)pctx;
500 DBUG_TRACE;
501 uint col = ctx->current_col;
502 ctx->current_col++;
503
504 size_t len = snprintf(
505 buffer, sizeof(buffer), "%s%02d:%02d:%02d", value->neg ? "-" : "",
506 value->day ? (value->day * 24 + value->hour) : value->hour, value->minute,
507 value->second);
508 ctx->tables.back().columns[col].row_values.push_back(
509 std::string(buffer, len));
510 return false;
511 }
512
handle_store_datetime(void * pctx,const MYSQL_TIME * value,uint)513 static int handle_store_datetime(void *pctx, const MYSQL_TIME *value, uint) {
514 char buffer[LARGE_STRING_BUFFER_SIZE];
515 Server_context *ctx = (Server_context *)pctx;
516 DBUG_TRACE;
517 uint col = ctx->current_col;
518 ctx->current_col++;
519
520 size_t len =
521 snprintf(buffer, sizeof(buffer), "%s%4d-%02d-%02d %02d:%02d:%02d",
522 value->neg ? "-" : "", value->year, value->month, value->day,
523 value->hour, value->minute, value->second);
524
525 ctx->tables.back().columns[col].row_values.push_back(
526 std::string(buffer, len));
527
528 return false;
529 }
530
handle_store_string(void * pctx,const char * const value,size_t length,const CHARSET_INFO * const)531 static int handle_store_string(void *pctx, const char *const value,
532 size_t length, const CHARSET_INFO *const) {
533 Server_context *ctx = (Server_context *)pctx;
534 DBUG_TRACE;
535 uint col = ctx->current_col;
536 ctx->current_col++;
537
538 ctx->tables.back().columns[col].row_values.push_back(
539 std::string(value, length));
540
541 return false;
542 }
543
handle_ok(void * pctx,uint server_status,uint statement_warn_count,ulonglong affected_rows,ulonglong last_insert_id,const char * const message)544 static void handle_ok(void *pctx, uint server_status, uint statement_warn_count,
545 ulonglong affected_rows, ulonglong last_insert_id,
546 const char *const message) {
547 Server_context *ctx = (Server_context *)pctx;
548 char buffer[STRING_BUFFER_SIZE];
549 WRITE_STR("handle_ok\n");
550 DBUG_TRACE;
551 ctx->sql_errno = 0;
552 ctx->sqlstate.clear();
553 ctx->err_msg.clear();
554 /* This could be an EOF */
555 ctx->server_status = server_status;
556 ctx->warn_count = statement_warn_count;
557 ctx->affected_rows = affected_rows;
558 ctx->last_insert_id = last_insert_id;
559 if (message) ctx->message.assign(message);
560
561 WRITE_STR("<<<<<<<<<<<< Current context >>>>>>>>>>>>>>>\n");
562 for (auto &&table : ctx->tables) {
563 table.dump_table();
564 }
565 ctx->dump_closing_ok();
566 WRITE_STR("<<<<<<<<<<<<>>>>>>>>>>>>>>>\n");
567 }
568
handle_error(void * pctx,uint sql_errno,const char * const err_msg,const char * const sqlstate)569 static void handle_error(void *pctx, uint sql_errno, const char *const err_msg,
570 const char *const sqlstate) {
571 char buffer[LARGE_STRING_BUFFER_SIZE];
572 Server_context *ctx = (Server_context *)pctx;
573 WRITE_STR("handle_error\n");
574 DBUG_TRACE;
575 /// was setting current_row size to 0...
576 if (!ctx->tables.empty()) ctx->tables.pop_back();
577
578 ctx->sql_errno = sql_errno;
579 ctx->sqlstate.assign(sqlstate);
580 ctx->err_msg.assign(err_msg);
581
582 ctx->dump_closing_error();
583 }
584
handle_shutdown(void *,int)585 static void handle_shutdown(void *, int) {
586 char buffer[STRING_BUFFER_SIZE];
587 WRITE_STR("handle_shutdown\n");
588 DBUG_TRACE;
589 }
590
591 const struct st_command_service_cbs protocol_callbacks = {
592 handle_start_column_metadata,
593 handle_send_column_metadata,
594 handle_end_column_metadata,
595 handle_start_row,
596 handle_end_row,
597 handle_abort_row,
598 get_client_capabilities,
599 handle_store_null,
600 handle_store_integer,
601 handle_store_longlong,
602 handle_store_decimal,
603 handle_store_double,
604 handle_store_date,
605 handle_store_time,
606 handle_store_datetime,
607 handle_store_string,
608 handle_ok,
609 handle_error,
610 handle_shutdown,
611 };
612
613 /****************************************************************************************/
614 #define WRITE_DASHED_LINE() \
615 WRITE_STR( \
616 "------------------------------------------------------------------\n");
617
618 #define WRITE_HASHED_LINE() \
619 WRITE_STR( \
620 "##################################################################\n");
621
fieldtype2str(enum enum_field_types type)622 static const char *fieldtype2str(enum enum_field_types type) {
623 switch (type) {
624 case MYSQL_TYPE_BIT:
625 return "BIT";
626 case MYSQL_TYPE_BLOB:
627 return "BLOB";
628 case MYSQL_TYPE_DATE:
629 return "DATE";
630 case MYSQL_TYPE_DATETIME:
631 return "DATETIME";
632 case MYSQL_TYPE_NEWDECIMAL:
633 return "NEWDECIMAL";
634 case MYSQL_TYPE_DECIMAL:
635 return "DECIMAL";
636 case MYSQL_TYPE_DOUBLE:
637 return "DOUBLE";
638 case MYSQL_TYPE_ENUM:
639 return "ENUM";
640 case MYSQL_TYPE_FLOAT:
641 return "FLOAT";
642 case MYSQL_TYPE_GEOMETRY:
643 return "GEOMETRY";
644 case MYSQL_TYPE_INT24:
645 return "INT24";
646 case MYSQL_TYPE_LONG:
647 return "LONG";
648 case MYSQL_TYPE_LONGLONG:
649 return "LONGLONG";
650 case MYSQL_TYPE_LONG_BLOB:
651 return "LONG_BLOB";
652 case MYSQL_TYPE_MEDIUM_BLOB:
653 return "MEDIUM_BLOB";
654 case MYSQL_TYPE_NEWDATE:
655 return "NEWDATE";
656 case MYSQL_TYPE_NULL:
657 return "NULL";
658 case MYSQL_TYPE_SET:
659 return "SET";
660 case MYSQL_TYPE_SHORT:
661 return "SHORT";
662 case MYSQL_TYPE_STRING:
663 return "STRING";
664 case MYSQL_TYPE_TIME:
665 return "TIME";
666 case MYSQL_TYPE_TIMESTAMP:
667 return "TIMESTAMP";
668 case MYSQL_TYPE_TINY:
669 return "TINY";
670 case MYSQL_TYPE_TINY_BLOB:
671 return "TINY_BLOB";
672 case MYSQL_TYPE_VARCHAR:
673 return "VARCHAR";
674 case MYSQL_TYPE_VAR_STRING:
675 return "VAR_STRING";
676 case MYSQL_TYPE_YEAR:
677 return "YEAR";
678 default:
679 return "?-unknown-?";
680 }
681 }
682
fieldflags2str(uint f)683 static char *fieldflags2str(uint f) {
684 static char buf[LARGE_STRING_BUFFER_SIZE];
685 char *s = buf;
686 *s = 0;
687 #define ff2s_check_flag(X) \
688 if (f & X##_FLAG) { \
689 s = my_stpcpy(s, #X " "); \
690 f &= ~X##_FLAG; \
691 }
692 ff2s_check_flag(NOT_NULL);
693 ff2s_check_flag(PRI_KEY);
694 ff2s_check_flag(UNIQUE_KEY);
695 ff2s_check_flag(MULTIPLE_KEY);
696 ff2s_check_flag(BLOB);
697 ff2s_check_flag(UNSIGNED);
698 ff2s_check_flag(ZEROFILL);
699 ff2s_check_flag(BINARY);
700 ff2s_check_flag(ENUM);
701 ff2s_check_flag(AUTO_INCREMENT);
702 ff2s_check_flag(TIMESTAMP);
703 ff2s_check_flag(SET);
704 ff2s_check_flag(NO_DEFAULT_VALUE);
705 ff2s_check_flag(NUM);
706 ff2s_check_flag(PART_KEY);
707 ff2s_check_flag(GROUP);
708 ff2s_check_flag(UNIQUE);
709 ff2s_check_flag(BINCMP);
710 ff2s_check_flag(ON_UPDATE_NOW);
711 #undef ff2s_check_flag
712 if (f) sprintf(s, " unknown=0x%04x", f);
713 return buf;
714 }
715
set_query_in_com_data(union COM_DATA * cmd,const char * query)716 static void set_query_in_com_data(union COM_DATA *cmd, const char *query) {
717 cmd->com_query.query = query;
718 cmd->com_query.length = strlen(query);
719 }
720
run_cmd(MYSQL_SESSION session,enum_server_command cmd,COM_DATA * data,Server_context * ctx,bool generates_result_set,void * p MY_ATTRIBUTE ((unused)))721 static void run_cmd(MYSQL_SESSION session, enum_server_command cmd,
722 COM_DATA *data, Server_context *ctx,
723 bool generates_result_set, void *p MY_ATTRIBUTE((unused))) {
724 char buffer[STRING_BUFFER_SIZE];
725 WRITE_DASHED_LINE();
726
727 enum cs_text_or_binary txt_or_bin = CS_TEXT_REPRESENTATION;
728
729 WRITE_STR("[CS_TEXT_REPRESENTATION]\n");
730 again:
731 print_cmd(cmd, data);
732 ctx->cmd = cmd;
733 int fail = command_service_run_command(session, cmd, data,
734 &my_charset_utf8_general_ci,
735 &protocol_callbacks, txt_or_bin, ctx);
736 if (fail) {
737 LogPluginErrMsg(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "run_statement code: %d\n",
738 fail);
739 return;
740 }
741
742 if (generates_result_set && txt_or_bin == CS_TEXT_REPRESENTATION) {
743 txt_or_bin = CS_BINARY_REPRESENTATION;
744 WRITE_STR("[CS_BINARY_REPRESENTATION]\n");
745 goto again;
746 }
747 WRITE_DASHED_LINE();
748 }
749
print_cmd(enum_server_command cmd,COM_DATA * data)750 static void print_cmd(enum_server_command cmd, COM_DATA *data) {
751 char buffer[STRING_BUFFER_SIZE];
752 switch (cmd) {
753 case COM_INIT_DB:
754 WRITE_VAL("COM_INIT_DB: db_name[%s]\n", data->com_init_db.db_name);
755 break;
756 case COM_QUERY:
757 WRITE_VAL("COM_QUERY: query[%s]\n", data->com_query.query);
758 break;
759 case COM_STMT_PREPARE:
760 WRITE_VAL("COM_STMT_PREPARE: query[%s]\n", data->com_stmt_prepare.query);
761 break;
762 case COM_STMT_EXECUTE:
763 WRITE_VAL("COM_STMT_EXECUTE: stmt_id [%lu]\n",
764 data->com_stmt_execute.stmt_id);
765 break;
766 case COM_STMT_SEND_LONG_DATA:
767 WRITE_VAL("COM_STMT_SEND_LONG_DATA: stmt_id [%lu]\n",
768 data->com_stmt_send_long_data.stmt_id);
769 break;
770 case COM_STMT_CLOSE:
771 WRITE_VAL("COM_STMT_CLOSE: stmt_id [%u]\n", data->com_stmt_close.stmt_id);
772 break;
773 case COM_STMT_RESET:
774 WRITE_VAL("COM_STMT_RESET: stmt_id [%u]\n", data->com_stmt_reset.stmt_id);
775 break;
776 case COM_STMT_FETCH:
777 WRITE_VAL("COM_STMT_FETCH: stmt_id [%lu]\n",
778 data->com_stmt_fetch.stmt_id);
779 break;
780 default:
781 WRITE_STR("NOT FOUND: add command to print_cmd\n");
782 }
783 }
784
setup_test(MYSQL_SESSION session,void * p)785 static void setup_test(MYSQL_SESSION session, void *p) {
786 DBUG_TRACE;
787 char buffer[STRING_BUFFER_SIZE];
788
789 Server_context ctx;
790 COM_DATA cmd;
791
792 WRITE_STR("CHANGE DATABASE\n");
793 cmd.com_init_db.db_name = "test";
794 cmd.com_init_db.length = strlen("test");
795 run_cmd(session, COM_INIT_DB, &cmd, &ctx, false, p);
796
797 WRITE_STR("CREATE TABLE\n");
798 set_query_in_com_data(&cmd,
799 "CREATE TABLE t1 (a INT, b INT, c INT, UNIQUE (A), "
800 "UNIQUE(B))");
801 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
802
803 WRITE_STR("INSERT VALUES INTO THE TABLE\n");
804 set_query_in_com_data(&cmd,
805 "INSERT INTO t1 VALUES"
806 "(1, 12, 1111), (2, 11, 2222),"
807 "(3, 10, 3333), (4, 9, 4444),"
808 "(5, 8, 5555), (6, 7, 6666),"
809 "(7, 6, 7777), (8, 5, -1111),"
810 "(9, 4, -2222), (10, 3, -3333),"
811 "(11, 2, -4444), (12, 1, -5555)");
812 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
813
814 set_query_in_com_data(&cmd,
815 "CREATE PROCEDURE proc_set_out_params("
816 " OUT v_str_1 CHAR(32), "
817 " OUT v_dbl_1 DOUBLE(4, 2), "
818 " OUT v_dec_1 DECIMAL(6, 3), "
819 " OUT v_int_1 INT)"
820 "BEGIN "
821 " SET v_str_1 = 'test_1'; "
822 " SET v_dbl_1 = 12.34; "
823 " SET v_dec_1 = 567.891; "
824 " SET v_int_1 = 2345; "
825 "END");
826 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
827
828 set_query_in_com_data(
829 &cmd,
830 "CREATE PROCEDURE verify_user_variables_are_null(v_str_1 CHAR(32), "
831 " v_dbl_1 DOUBLE(4, 2), "
832 " v_dec_1 DECIMAL(6, 3), "
833 " v_int_1 INT)"
834 "BEGIN "
835 "DECLARE unexpected CONDITION FOR SQLSTATE '45000'; "
836 " IF v_str_1 is not null THEN "
837 " SIGNAL unexpected; "
838 " ELSEIF v_dbl_1 is not null THEN "
839 " SIGNAL unexpected; "
840 " ELSEIF v_dec_1 is not null THEN "
841 " SIGNAL unexpected; "
842 " ELSEIF v_int_1 is not null THEN "
843 " SIGNAL unexpected; "
844 " END IF;"
845 "END");
846 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
847
848 set_query_in_com_data(
849 &cmd,
850 "CREATE PROCEDURE verify_user_variables_are_set(v_str_1 CHAR(32), "
851 " v_dbl_1 DOUBLE(4, 2), "
852 " v_dec_1 DECIMAL(6, 3), "
853 " v_int_1 INT)"
854 "BEGIN "
855 "DECLARE unexpected CONDITION FOR SQLSTATE '45000'; "
856 " IF v_str_1 != 'test_1' THEN "
857 " SIGNAL unexpected; "
858 " ELSEIF v_dbl_1 != 12.34 THEN "
859 " SIGNAL unexpected; "
860 " ELSEIF v_dec_1 != 567.891 THEN "
861 " SIGNAL unexpected; "
862 " ELSEIF v_int_1 != 2345 THEN "
863 " SIGNAL unexpected; "
864 " END IF;"
865 "END");
866 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
867 }
868
test_1(MYSQL_SESSION session,void * p)869 static void test_1(MYSQL_SESSION session, void *p) {
870 DBUG_TRACE;
871 char buffer[STRING_BUFFER_SIZE];
872
873 Server_context ctx;
874 COM_DATA cmd;
875
876 WRITE_STR("CREATE PREPARED STATEMENT\n");
877 cmd.com_stmt_prepare.query = "SELECT * from t1 where a > ? and b < ?";
878 cmd.com_stmt_prepare.length = strlen(cmd.com_stmt_prepare.query);
879 run_cmd(session, COM_STMT_PREPARE, &cmd, &ctx, false, p);
880
881 WRITE_STR("EXECUTE PREPARED STATEMENT WITH PARAMETERS AND CURSOR\n");
882
883 PS_PARAM params[2];
884 params[0].type = MYSQL_TYPE_STRING;
885 params[0].unsigned_type = false;
886 params[0].null_bit = false;
887 params[0].value = (const unsigned char *)"5";
888 params[0].length = 1;
889
890 params[1].type = MYSQL_TYPE_STRING;
891 params[1].unsigned_type = false;
892 params[1].null_bit = false;
893 params[1].value = (const unsigned char *)"20";
894 params[1].length = 2;
895
896 cmd.com_stmt_execute.stmt_id = ctx.stmt_id;
897 cmd.com_stmt_execute.open_cursor = true;
898 cmd.com_stmt_execute.has_new_types = false;
899 cmd.com_stmt_execute.parameters = params;
900 cmd.com_stmt_execute.parameter_count = 2;
901 cmd.com_stmt_execute.has_new_types = true;
902
903 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
904
905 WRITE_STR("EXECUTE PREPARED STATEMENT WITH WRONG NO OF PARAM\n");
906 cmd.com_stmt_execute.parameter_count = 1;
907 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
908
909 WRITE_STR("FETCH ONE ROW FROM THE CURSOR\n");
910 cmd.com_stmt_fetch.stmt_id = ctx.stmt_id;
911 cmd.com_stmt_fetch.num_rows = 1;
912 run_cmd(session, COM_STMT_FETCH, &cmd, &ctx, false, p);
913
914 WRITE_STR("FETCH TWO ROWS FROM THE CURSOR\n");
915 cmd.com_stmt_fetch.num_rows = 2;
916 run_cmd(session, COM_STMT_FETCH, &cmd, &ctx, false, p);
917
918 WRITE_STR("CLOSE THE STATEMENT\n");
919 cmd.com_stmt_close.stmt_id = ctx.stmt_id;
920 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
921
922 WRITE_STR("CLOSE NON-EXISTING STATEMENT\n");
923 cmd.com_stmt_close.stmt_id = 100001;
924 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
925
926 cmd.com_stmt_fetch.stmt_id = ctx.stmt_id;
927 WRITE_STR("TRY TO FETCH ONE ROW FROM A DEALLOCATED(CLOSED) PS\n");
928 run_cmd(session, COM_STMT_FETCH, &cmd, &ctx, false, p);
929 }
930
test_2(MYSQL_SESSION session,void * p)931 static void test_2(MYSQL_SESSION session, void *p) {
932 DBUG_TRACE;
933 char buffer[STRING_BUFFER_SIZE];
934
935 Server_context ctx;
936 COM_DATA cmd;
937
938 WRITE_STR("CREATE PREPARED STATEMENT\n");
939 cmd.com_stmt_prepare.query = "SELECT * from t1 where a > ? and b < ?";
940 cmd.com_stmt_prepare.length = strlen(cmd.com_stmt_prepare.query);
941 run_cmd(session, COM_STMT_PREPARE, &cmd, &ctx, false, p);
942
943 PS_PARAM params[2];
944 params[0].type = MYSQL_TYPE_STRING;
945 params[0].unsigned_type = false;
946 params[0].null_bit = false;
947 params[0].value = (const unsigned char *)"4";
948 params[0].length = 1;
949
950 params[1].type = MYSQL_TYPE_STRING;
951 params[1].unsigned_type = false;
952 params[1].null_bit = false;
953 params[1].value = (const unsigned char *)"7";
954 params[1].length = 1;
955
956 cmd.com_stmt_execute.stmt_id = ctx.stmt_id;
957 cmd.com_stmt_execute.parameters = params;
958 cmd.com_stmt_execute.parameter_count = 2;
959 cmd.com_stmt_execute.has_new_types = true;
960 cmd.com_stmt_execute.open_cursor = true;
961
962 WRITE_STR("EXECUTE THE PS FOR OPEN CURSOR\n");
963 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
964
965 WRITE_STR("FETCH ONE ROW\n");
966 cmd.com_stmt_fetch.stmt_id = ctx.stmt_id;
967 run_cmd(session, COM_STMT_FETCH, &cmd, &ctx, false, p);
968
969 WRITE_STR("RESET THE STATEMENT\n");
970 cmd.com_stmt_reset.stmt_id = ctx.stmt_id;
971 run_cmd(session, COM_STMT_RESET, &cmd, &ctx, false, p);
972
973 WRITE_STR("RESET NON-EXISTING STATEMENT\n");
974 cmd.com_stmt_reset.stmt_id = 199999;
975 run_cmd(session, COM_STMT_RESET, &cmd, &ctx, false, p);
976
977 WRITE_STR("TRY TO FETCH ONE ROW FROM THE PS WITH REMOVED CURSOR\n");
978 cmd.com_stmt_fetch.num_rows = 1;
979 cmd.com_stmt_fetch.stmt_id = ctx.stmt_id;
980 run_cmd(session, COM_STMT_FETCH, &cmd, &ctx, false, p);
981
982 WRITE_STR("CLOSE THE STATEMENT\n");
983 cmd.com_stmt_close.stmt_id = ctx.stmt_id;
984 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
985 }
986
test_3(MYSQL_SESSION session,void * p)987 static void test_3(MYSQL_SESSION session, void *p) {
988 DBUG_TRACE;
989 char buffer[STRING_BUFFER_SIZE];
990
991 Server_context ctx;
992 COM_DATA cmd;
993
994 WRITE_STR("CREATE PREPARED STATEMENT\n");
995 cmd.com_stmt_prepare.query = "SELECT * from t1 where a > ? and b > ?";
996 cmd.com_stmt_prepare.length = strlen(cmd.com_stmt_prepare.query);
997 run_cmd(session, COM_STMT_PREPARE, &cmd, &ctx, false, p);
998
999 PS_PARAM params[2];
1000 params[0].type = MYSQL_TYPE_STRING;
1001 params[0].unsigned_type = false;
1002 params[0].null_bit = false;
1003 params[0].value = (const unsigned char *)"2";
1004 params[0].length = 1;
1005
1006 params[1].type = MYSQL_TYPE_STRING;
1007 params[1].unsigned_type = false;
1008 params[1].null_bit = false;
1009 params[1].value = (const unsigned char *)"3";
1010 params[1].length = 1;
1011
1012 cmd.com_stmt_execute.stmt_id = ctx.stmt_id;
1013 cmd.com_stmt_execute.parameter_count = 2;
1014 cmd.com_stmt_execute.parameters = params;
1015 cmd.com_stmt_execute.open_cursor = false;
1016 cmd.com_stmt_execute.has_new_types = true;
1017
1018 WRITE_STR("EXECUTE THE PS WITHOUT CURSOR\n");
1019 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
1020
1021 WRITE_STR("TRY TO FETCH ONE ROW FROM A PS WITHOUT CURSOR\n");
1022 cmd.com_stmt_fetch.num_rows = 1;
1023 cmd.com_stmt_fetch.stmt_id = ctx.stmt_id;
1024 run_cmd(session, COM_STMT_FETCH, &cmd, &ctx, false, p);
1025
1026 WRITE_STR("TRY TO RESET THE CURSOR FROM A PS WITHOUT CURSOR\n");
1027 cmd.com_stmt_reset.stmt_id = ctx.stmt_id;
1028 run_cmd(session, COM_STMT_RESET, &cmd, &ctx, false, p);
1029
1030 WRITE_STR("TRY TO CLOSE THE CURSOR FROM A PS WITHOUT CURSOR\n");
1031 cmd.com_stmt_close.stmt_id = ctx.stmt_id;
1032 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
1033 }
1034
test_4(MYSQL_SESSION session,void * p)1035 static void test_4(MYSQL_SESSION session, void *p) {
1036 DBUG_TRACE;
1037 char buffer[STRING_BUFFER_SIZE];
1038 uchar param_buff[STRING_BUFFER_SIZE];
1039 uchar *pos = param_buff;
1040
1041 Server_context ctx;
1042 COM_DATA cmd;
1043
1044 WRITE_STR("CREATE TABLE\n");
1045 set_query_in_com_data(&cmd,
1046 "CREATE TABLE t2"
1047 "("
1048 " c1 tinyint,"
1049 " c2 smallint,"
1050 " c3 mediumint,"
1051 " c4 int,"
1052 " c5 integer,"
1053 " c6 bigint,"
1054 " c7 float,"
1055 " c8 double,"
1056 " c9 date)");
1057 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1058
1059 WRITE_STR("CREATE PREPARED STATEMENT\n");
1060 cmd.com_stmt_prepare.query =
1061 "INSERT INTO t2(c1, c2, c3, c4, c5, c6, c7, c8, c9) "
1062 "VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)";
1063 cmd.com_stmt_prepare.length = strlen(cmd.com_stmt_prepare.query);
1064 run_cmd(session, COM_STMT_PREPARE, &cmd, &ctx, false, p);
1065
1066 WRITE_STR("EXECUTE PREPARED STATEMENT WITH PARAMETERS AND CURSOR\n");
1067 PS_PARAM multi_param[9];
1068 cmd.com_stmt_execute.stmt_id = ctx.stmt_id;
1069 cmd.com_stmt_execute.open_cursor = false;
1070 cmd.com_stmt_execute.has_new_types = true;
1071 cmd.com_stmt_execute.parameters = multi_param;
1072
1073 int8 i8_data = 1;
1074 int16 i16_data = 1;
1075 int32 i32_data = 10;
1076 longlong i64_data = 20;
1077 float f_data = 2;
1078 double d_data = 6575.001;
1079
1080 char date_t[4];
1081 int2store(date_t, 1988);
1082 date_t[2] = 12;
1083 date_t[3] = 20;
1084
1085 /*tinyint*/
1086 multi_param[0].null_bit = false;
1087 multi_param[0].length = sizeof(int8);
1088 multi_param[0].type = MYSQL_TYPE_TINY;
1089 multi_param[0].unsigned_type = false;
1090
1091 /*smallint*/
1092 multi_param[1].null_bit = false;
1093 multi_param[1].length = sizeof(int16);
1094 multi_param[1].type = MYSQL_TYPE_SHORT;
1095 multi_param[1].unsigned_type = false;
1096
1097 /*mediumint*/
1098 multi_param[2].null_bit = false;
1099 multi_param[2].length = sizeof(int32);
1100 multi_param[2].type = MYSQL_TYPE_LONG;
1101 multi_param[2].unsigned_type = false;
1102
1103 /*int*/
1104 multi_param[3].null_bit = false;
1105 multi_param[3].length = sizeof(int32);
1106 multi_param[3].type = MYSQL_TYPE_LONG;
1107 multi_param[3].unsigned_type = false;
1108
1109 /*integer*/
1110 multi_param[4].null_bit = false;
1111 multi_param[4].length = sizeof(int32);
1112 multi_param[4].type = MYSQL_TYPE_LONG;
1113 multi_param[4].unsigned_type = false;
1114
1115 /*bigint*/
1116 multi_param[5].null_bit = false;
1117 multi_param[5].length = sizeof(int64);
1118 multi_param[5].type = MYSQL_TYPE_LONGLONG;
1119 multi_param[5].unsigned_type = false;
1120
1121 /*float*/
1122 multi_param[6].null_bit = false;
1123 multi_param[6].length = sizeof(float);
1124 multi_param[6].type = MYSQL_TYPE_FLOAT;
1125 multi_param[6].unsigned_type = false;
1126
1127 /*double*/
1128 multi_param[7].null_bit = false;
1129 multi_param[7].length = sizeof(double);
1130 multi_param[7].type = MYSQL_TYPE_DOUBLE;
1131 multi_param[7].unsigned_type = false;
1132
1133 /*date*/
1134 multi_param[8].null_bit = false;
1135 multi_param[8].length = 4;
1136 multi_param[8].type = MYSQL_TYPE_DATE;
1137 multi_param[8].unsigned_type = false;
1138
1139 while (i8_data < 10) {
1140 multi_param[0].value = (const unsigned char *)&i8_data;
1141
1142 int2store(pos, i16_data);
1143 multi_param[1].value = (const unsigned char *)pos;
1144 pos += 2;
1145
1146 int4store(pos, i32_data);
1147 multi_param[2].value = (const unsigned char *)pos;
1148 pos += 4;
1149
1150 int4store(pos, i32_data);
1151 multi_param[3].value = (const unsigned char *)pos;
1152 pos += 4;
1153
1154 int4store(pos, i32_data);
1155 multi_param[4].value = (const unsigned char *)pos;
1156 pos += 4;
1157
1158 int8store(pos, i64_data);
1159 multi_param[5].value = (const unsigned char *)pos;
1160 pos += 8;
1161
1162 float4store(pos, f_data);
1163 multi_param[6].value = (const unsigned char *)pos;
1164 pos += 4;
1165
1166 float8store(pos, d_data);
1167 multi_param[7].value = (const unsigned char *)pos;
1168 pos += sizeof(double);
1169
1170 multi_param[8].value = (const unsigned char *)&date_t;
1171 cmd.com_stmt_execute.has_new_types = ((i8_data % 2 == 0));
1172 cmd.com_stmt_execute.parameter_count = 9;
1173 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
1174 i8_data++;
1175 i16_data++;
1176 i32_data++;
1177 i64_data++;
1178 f_data++;
1179 d_data++;
1180 }
1181
1182 set_query_in_com_data(&cmd, "SELECT * FROM t2");
1183 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1184
1185 cmd.com_stmt_close.stmt_id = ctx.stmt_id;
1186 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
1187 }
1188
test_5(MYSQL_SESSION session,void * p)1189 static void test_5(MYSQL_SESSION session, void *p) {
1190 DBUG_TRACE;
1191 char buffer[STRING_BUFFER_SIZE];
1192
1193 Server_context ctx;
1194 COM_DATA cmd;
1195
1196 WRITE_STR("CREATE TABLE\n");
1197 set_query_in_com_data(&cmd,
1198 "CREATE TABLE test_long_data(col1 int, "
1199 "col2 long varchar)");
1200 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1201
1202 WRITE_STR("CREATE PREPARED STATEMENT\n");
1203 cmd.com_stmt_prepare.query =
1204 "INSERT INTO test_long_data(col1, col2) VALUES(?, ?)";
1205 cmd.com_stmt_prepare.length = strlen(cmd.com_stmt_prepare.query);
1206 run_cmd(session, COM_STMT_PREPARE, &cmd, &ctx, false, p);
1207
1208 cmd.com_stmt_send_long_data.stmt_id = ctx.stmt_id;
1209 cmd.com_stmt_send_long_data.param_number = 1;
1210 cmd.com_stmt_send_long_data.length = 8;
1211 cmd.com_stmt_send_long_data.longdata =
1212 const_cast<uchar *>(pointer_cast<const uchar *>("Catalin "));
1213 WRITE_STR("SEND PARAMETER AS COM_STMT_SEND_LONG_DATA\n");
1214 run_cmd(session, COM_STMT_SEND_LONG_DATA, &cmd, &ctx, false, p);
1215
1216 cmd.com_stmt_send_long_data.stmt_id = ctx.stmt_id;
1217 // Append data to the same parameter
1218 cmd.com_stmt_send_long_data.param_number = 1;
1219 cmd.com_stmt_send_long_data.length = 8;
1220 cmd.com_stmt_send_long_data.longdata =
1221 const_cast<uchar *>(pointer_cast<const uchar *>("Besleaga"));
1222 WRITE_STR("APPEND TO THE SAME COLUMN\n");
1223 run_cmd(session, COM_STMT_SEND_LONG_DATA, &cmd, &ctx, false, p);
1224
1225 PS_PARAM param[3];
1226 param[0].null_bit = false;
1227 param[0].length = sizeof(int32);
1228 param[0].type = MYSQL_TYPE_LONG;
1229 param[0].unsigned_type = false;
1230 uchar long_data[4];
1231 int4store(long_data, 4);
1232 param[0].value = (const unsigned char *)long_data;
1233
1234 param[1].null_bit = false;
1235 param[1].length = 0;
1236 param[1].value = nullptr;
1237 param[1].type = MYSQL_TYPE_STRING;
1238 param[1].unsigned_type = false;
1239
1240 param[2].null_bit = false;
1241 param[2].length = 0;
1242 param[2].value = nullptr;
1243 param[2].type = MYSQL_TYPE_STRING;
1244 param[2].unsigned_type = false;
1245
1246 cmd.com_stmt_execute.stmt_id = ctx.stmt_id;
1247 cmd.com_stmt_execute.open_cursor = false;
1248 cmd.com_stmt_execute.has_new_types = true;
1249 cmd.com_stmt_execute.parameters = param;
1250 cmd.com_stmt_execute.parameter_count = 2;
1251 WRITE_STR("EXECUTE PS WITH LONG DATA CURSOR\n");
1252 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
1253
1254 set_query_in_com_data(&cmd, "SELECT * from test_long_data");
1255 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1256
1257 // Send long data to non existing prepared statement
1258 cmd.com_stmt_send_long_data.stmt_id = 199999;
1259 cmd.com_stmt_send_long_data.param_number = 1;
1260 cmd.com_stmt_send_long_data.length = 8;
1261 cmd.com_stmt_send_long_data.longdata =
1262 const_cast<uchar *>(pointer_cast<const uchar *>("12345"));
1263 WRITE_STR("APPEND TO A NON EXISTING STATEMENT\n");
1264 run_cmd(session, COM_STMT_SEND_LONG_DATA, &cmd, &ctx, false, p);
1265 WRITE_STR("ERRORS ONLY SHOW AT FIRST EXECUTION OF COM_STMT_EXECUTE\n");
1266 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
1267
1268 // Send long data to non existing parameter
1269 cmd.com_stmt_send_long_data.stmt_id = ctx.stmt_id;
1270 cmd.com_stmt_send_long_data.param_number = 15;
1271 cmd.com_stmt_send_long_data.length = 8;
1272 cmd.com_stmt_send_long_data.longdata =
1273 const_cast<uchar *>(pointer_cast<const uchar *>("12345"));
1274 WRITE_STR("APPEND DATA TO NON EXISTING PARAMETER\n");
1275 run_cmd(session, COM_STMT_SEND_LONG_DATA, &cmd, &ctx, false, p);
1276 WRITE_STR("ERRORS ONLY SHOW AT FIRST EXECUTION OF COM_STMT_EXECUTE\n");
1277 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
1278
1279 WRITE_STR("TRY TO CLOSE THE CURSOR FROM A PS WITHOUT CURSOR\n");
1280 cmd.com_stmt_close.stmt_id = ctx.stmt_id;
1281 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
1282 }
1283
1284 #define STRING_SIZE 30
1285
test_6(MYSQL_SESSION session,void * p)1286 static void test_6(MYSQL_SESSION session, void *p) {
1287 DBUG_TRACE;
1288 char buffer[STRING_BUFFER_SIZE];
1289
1290 Server_context ctx;
1291 COM_DATA cmd;
1292
1293 set_query_in_com_data(&cmd,
1294 "CREATE TABLE t3(a1 INT, a2 CHAR(32), "
1295 "a3 DOUBLE(4, 2), a4 DECIMAL(3, 1))");
1296 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1297
1298 set_query_in_com_data(&cmd,
1299 "CREATE TABLE t4(b0 INT, b1 INT, b2 CHAR(32), "
1300 "b3 DOUBLE(4, 2), b4 DECIMAL(3, 1))");
1301 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1302
1303 set_query_in_com_data(&cmd,
1304 "INSERT INTO t3 VALUES"
1305 "(1, '11', 12.34, 56.7), "
1306 "(2, '12', 56.78, 90.1), "
1307 "(3, '13', 23.45, 67.8)");
1308 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1309
1310 set_query_in_com_data(&cmd,
1311 "INSERT INTO t4 VALUES"
1312 "(100, 10, '110', 70.70, 10.1), "
1313 "(200, 20, '120', 80.80, 20.2), "
1314 "(300, 30, '130', 90.90, 30.3)");
1315 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1316
1317 set_query_in_com_data(&cmd,
1318 "CREATE PROCEDURE p1("
1319 " IN v0 INT, "
1320 " OUT v_str_1 CHAR(32), "
1321 " OUT v_dbl_1 DOUBLE(4, 2), "
1322 " OUT v_dec_1 DECIMAL(6, 3), "
1323 " OUT v_int_1 INT, "
1324 " IN v1 INT, "
1325 " INOUT v_str_2 CHAR(64), "
1326 " INOUT v_dbl_2 DOUBLE(5, 3), "
1327 " INOUT v_dec_2 DECIMAL(7, 4), "
1328 " INOUT v_int_2 INT)"
1329 "BEGIN "
1330 " SET v0 = -1; "
1331 " SET v1 = -1; "
1332 " SET v_str_1 = 'test_1'; "
1333 " SET v_dbl_1 = 12.34; "
1334 " SET v_dec_1 = 567.891; "
1335 " SET v_int_1 = 2345; "
1336 " SET v_str_2 = 'test_2'; "
1337 " SET v_dbl_2 = 67.891; "
1338 " SET v_dec_2 = 234.6789; "
1339 " SET v_int_2 = 6789; "
1340 " SELECT * FROM t3; "
1341 " SELECT * FROM t4; "
1342 "END");
1343 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1344
1345 cmd.com_stmt_prepare.query = "CALL p1(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
1346 cmd.com_stmt_prepare.length = strlen(cmd.com_stmt_prepare.query);
1347 run_cmd(session, COM_STMT_PREPARE, &cmd, &ctx, false, p);
1348
1349 // ---------------------------------------------------------------
1350 char str_data[20][STRING_SIZE];
1351 double dbl_data[20];
1352 char dec_data[20][STRING_SIZE];
1353 int int_data[20];
1354 PS_PARAM ps_params[STRING_SIZE];
1355
1356 memset(str_data, 0, sizeof(str_data));
1357 memset(dbl_data, 0, sizeof(dbl_data));
1358 memset(dec_data, 0, sizeof(dec_data));
1359 memset(int_data, 0, sizeof(int_data));
1360
1361 memset(ps_params, 0, sizeof(ps_params));
1362
1363 /* - v0 -- INT */
1364
1365 ps_params[0].type = MYSQL_TYPE_LONG;
1366 ps_params[0].value = (const unsigned char *)&int_data[0];
1367 ps_params[0].length = sizeof(int32);
1368 ps_params[0].unsigned_type = false;
1369 ps_params[0].null_bit = false;
1370
1371 /* - v_str_1 -- CHAR(32) */
1372
1373 ps_params[1].type = MYSQL_TYPE_STRING;
1374 ps_params[1].value = (const unsigned char *)str_data[0];
1375 ps_params[1].length = STRING_SIZE;
1376 ps_params[1].unsigned_type = false;
1377 ps_params[1].null_bit = false;
1378
1379 /* - v_dbl_1 -- DOUBLE */
1380
1381 ps_params[2].type = MYSQL_TYPE_DOUBLE;
1382 ps_params[2].value = (const unsigned char *)&dbl_data[0];
1383 ps_params[2].length = STRING_SIZE;
1384 ps_params[2].unsigned_type = false;
1385 ps_params[2].null_bit = false;
1386
1387 /* - v_dec_1 -- DECIMAL */
1388
1389 ps_params[3].type = MYSQL_TYPE_NEWDECIMAL;
1390 ps_params[3].value = (const unsigned char *)dec_data[0];
1391 ps_params[3].length = STRING_SIZE;
1392 ps_params[3].unsigned_type = false;
1393 ps_params[3].null_bit = false;
1394
1395 /* - v_int_1 -- INT */
1396
1397 ps_params[4].type = MYSQL_TYPE_LONG;
1398 ps_params[4].value = (const unsigned char *)&int_data[0];
1399 ps_params[4].length = STRING_SIZE;
1400 ps_params[4].unsigned_type = false;
1401 ps_params[4].null_bit = false;
1402
1403 /* - v1 -- INT */
1404
1405 ps_params[5].type = MYSQL_TYPE_LONG;
1406 ps_params[5].value = (const unsigned char *)&int_data[0];
1407 ps_params[5].length = STRING_SIZE;
1408 ps_params[5].unsigned_type = false;
1409 ps_params[5].null_bit = false;
1410
1411 /* - v_str_2 -- CHAR(32) */
1412
1413 ps_params[6].type = MYSQL_TYPE_STRING;
1414 ps_params[6].value = (const unsigned char *)str_data[0];
1415 ps_params[6].length = STRING_SIZE;
1416 ps_params[6].unsigned_type = false;
1417 ps_params[6].null_bit = false;
1418
1419 /* - v_dbl_2 -- DOUBLE */
1420
1421 ps_params[7].type = MYSQL_TYPE_DOUBLE;
1422 ps_params[7].value = (const unsigned char *)&dbl_data[0];
1423 ps_params[7].length = STRING_SIZE;
1424 ps_params[7].unsigned_type = false;
1425 ps_params[7].null_bit = false;
1426
1427 /* - v_dec_2 -- DECIMAL */
1428
1429 ps_params[8].type = MYSQL_TYPE_DECIMAL;
1430 ps_params[8].value = (const unsigned char *)dec_data[0];
1431 ps_params[8].length = STRING_SIZE;
1432 ps_params[8].unsigned_type = false;
1433 ps_params[8].null_bit = false;
1434
1435 /* - v_int_2 -- INT */
1436
1437 ps_params[9].type = MYSQL_TYPE_LONG;
1438 ps_params[9].value = (const unsigned char *)&int_data[0];
1439 ps_params[9].length = STRING_SIZE;
1440 ps_params[9].unsigned_type = false;
1441 ps_params[9].null_bit = false;
1442
1443 cmd.com_stmt_execute.parameters = ps_params;
1444 cmd.com_stmt_execute.open_cursor = false;
1445 cmd.com_stmt_execute.stmt_id = ctx.stmt_id;
1446 cmd.com_stmt_execute.has_new_types = true;
1447 cmd.com_stmt_execute.parameter_count = 10;
1448 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
1449
1450 WRITE_STR("CLOSE PS\n");
1451 cmd.com_stmt_close.stmt_id = ctx.stmt_id;
1452 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
1453 }
1454
test_7(MYSQL_SESSION session,void * p)1455 static void test_7(MYSQL_SESSION session, void *p) {
1456 DBUG_TRACE;
1457 char buffer[STRING_BUFFER_SIZE];
1458
1459 Server_context ctx;
1460 COM_DATA cmd;
1461
1462 WRITE_STR("CREATE PREPARED STATEMENT\n");
1463 cmd.com_stmt_prepare.query = "SELECT CONCAT(9< ?)";
1464 cmd.com_stmt_prepare.length = strlen(cmd.com_stmt_prepare.query);
1465 run_cmd(session, COM_STMT_PREPARE, &cmd, &ctx, false, p);
1466
1467 WRITE_STR("EXECUTE PREPARED STATEMENT WITH PARAMETERS AND CURSOR\n");
1468
1469 PS_PARAM params[1];
1470 params[0].type = MYSQL_TYPE_JSON;
1471 params[0].unsigned_type = false;
1472 params[0].null_bit = false;
1473 params[0].value = (const unsigned char *)"{}";
1474 params[0].length = 2;
1475
1476 cmd.com_stmt_execute.stmt_id = ctx.stmt_id;
1477 cmd.com_stmt_execute.open_cursor = true;
1478 cmd.com_stmt_execute.has_new_types = false;
1479 cmd.com_stmt_execute.parameters = params;
1480 cmd.com_stmt_execute.parameter_count = 1;
1481 cmd.com_stmt_execute.has_new_types = true;
1482
1483 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
1484
1485 WRITE_STR("CLOSE PS\n");
1486 cmd.com_stmt_close.stmt_id = ctx.stmt_id;
1487 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
1488 }
1489
test_8(MYSQL_SESSION session,void * p)1490 static void test_8(MYSQL_SESSION session, void *p) {
1491 DBUG_TRACE;
1492 char buffer[STRING_BUFFER_SIZE];
1493
1494 Server_context ctx;
1495 COM_DATA cmd;
1496
1497 WRITE_STR("RESET VARIABLES THAT ARE GOING TO BE USED FOR OUT-PARAMS\n");
1498 set_query_in_com_data(
1499 &cmd, "SET @my_v1=null, @my_v2=null, @my_v3=null, @my_v4=null");
1500 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1501
1502 ctx.tables.clear();
1503 cmd.com_stmt_prepare.query = "CALL proc_set_out_params(?, ?, ?, ?)";
1504 cmd.com_stmt_prepare.length = strlen(cmd.com_stmt_prepare.query);
1505 run_cmd(session, COM_STMT_PREPARE, &cmd, &ctx, false, p);
1506
1507 WRITE_STR("EXECUTE PREPARED STATEMENT WITH PARAMETERS\n");
1508
1509 PS_PARAM params[4];
1510 std::string values[4]{"@my_v1", "@my_v2", "@my_v3", "@my_v4"};
1511 params[0].type = MYSQL_TYPE_STRING;
1512 params[0].unsigned_type = false;
1513 params[0].null_bit = false;
1514 params[0].value = (const unsigned char *)values[0].c_str();
1515 params[0].length = values[0].length();
1516 params[1].type = MYSQL_TYPE_STRING;
1517 params[1].unsigned_type = false;
1518 params[1].null_bit = false;
1519 params[1].value = (const unsigned char *)values[1].c_str();
1520 params[1].length = values[1].length();
1521 params[2].type = MYSQL_TYPE_STRING;
1522 params[2].unsigned_type = false;
1523 params[2].null_bit = false;
1524 params[2].value = (const unsigned char *)values[2].c_str();
1525 params[2].length = values[2].length();
1526 params[3].type = MYSQL_TYPE_STRING;
1527 params[3].unsigned_type = false;
1528 params[3].null_bit = false;
1529 params[3].value = (const unsigned char *)values[3].c_str();
1530 params[3].length = values[3].length();
1531
1532 ctx.tables.clear();
1533 cmd.com_stmt_execute.stmt_id = ctx.stmt_id;
1534 cmd.com_stmt_execute.open_cursor = false;
1535 cmd.com_stmt_execute.has_new_types = false;
1536 cmd.com_stmt_execute.parameters = params;
1537 cmd.com_stmt_execute.parameter_count = 4;
1538 cmd.com_stmt_execute.has_new_types = true;
1539 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
1540
1541 WRITE_STR(
1542 "VERIFY THAT VARIABLES ARE STILL NULL AND OUT PRAMETERS WERE TRANSFERED "
1543 "IN METADATA\n");
1544
1545 if (ctx.tables.size() != 1 || ctx.tables[0].columns.size() != 4) {
1546 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
1547 "Protocol didn't send the out-parameters to the user");
1548 return;
1549 }
1550
1551 ctx.tables.clear();
1552 set_query_in_com_data(
1553 &cmd,
1554 "CALL verify_user_variables_are_null(@my_v1, @my_v2, @my_v3, @my_v4)");
1555 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1556
1557 if (ctx.sql_errno) {
1558 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
1559 "Call to 'verify_user_variables_are_null' failed, one of the "
1560 "provided user variables may be invalid");
1561 return;
1562 }
1563
1564 WRITE_STR("CLOSE PS\n");
1565 cmd.com_stmt_close.stmt_id = ctx.stmt_id;
1566 ctx.tables.clear();
1567 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
1568 }
1569
test_9(MYSQL_SESSION session,void * p)1570 static void test_9(MYSQL_SESSION session, void *p) {
1571 DBUG_TRACE;
1572 char buffer[STRING_BUFFER_SIZE];
1573
1574 Server_context ctx;
1575 COM_DATA cmd;
1576
1577 WRITE_STR("RESET VARIABLES THAT ARE GOING TO BE USED FOR OUT-PARAMS\n");
1578 set_query_in_com_data(
1579 &cmd, "SET @my_v1=null, @my_v2=null, @my_v3=null, @my_v4=null");
1580 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1581
1582 ctx.tables.clear();
1583 cmd.com_stmt_prepare.query =
1584 "CALL proc_set_out_params(@my_v1, @my_v2, @my_v3, @my_v4)";
1585 cmd.com_stmt_prepare.length = strlen(cmd.com_stmt_prepare.query);
1586 run_cmd(session, COM_STMT_PREPARE, &cmd, &ctx, false, p);
1587
1588 WRITE_STR("EXECUTE PREPARED STATEMENT WITHOUT PARAMETERS\n");
1589
1590 ctx.tables.clear();
1591 cmd.com_stmt_execute.stmt_id = ctx.stmt_id;
1592 cmd.com_stmt_execute.open_cursor = false;
1593 cmd.com_stmt_execute.has_new_types = false;
1594 cmd.com_stmt_execute.parameters = nullptr;
1595 cmd.com_stmt_execute.parameter_count = 0;
1596 cmd.com_stmt_execute.has_new_types = true;
1597 run_cmd(session, COM_STMT_EXECUTE, &cmd, &ctx, false, p);
1598
1599 WRITE_STR(
1600 "VERIFY THAT VARIABLES ARE SET AND OUT PRAMETERS WERE NOT TRANSFERED "
1601 "IN METADATA\n");
1602
1603 if (ctx.tables.size() != 0) {
1604 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
1605 "Protocol send the out-parameters to the user");
1606 return;
1607 }
1608
1609 ctx.tables.clear();
1610 set_query_in_com_data(
1611 &cmd,
1612 "CALL verify_user_variables_are_set(@my_v1, @my_v2, @my_v3, @my_v4)");
1613 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1614
1615 if (ctx.sql_errno) {
1616 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
1617 "Call to 'verify_user_variables_are_set' failed, one of the "
1618 "provided user variables may be invalid");
1619 return;
1620 }
1621
1622 WRITE_STR("CLOSE PS\n");
1623 cmd.com_stmt_close.stmt_id = ctx.stmt_id;
1624 ctx.tables.clear();
1625 run_cmd(session, COM_STMT_CLOSE, &cmd, &ctx, false, p);
1626 }
1627
test_10(MYSQL_SESSION session,void * p)1628 static void test_10(MYSQL_SESSION session, void *p) {
1629 DBUG_TRACE;
1630 char buffer[STRING_BUFFER_SIZE];
1631
1632 Server_context ctx;
1633 COM_DATA cmd;
1634
1635 WRITE_STR("RESET VARIABLES THAT ARE GOING TO BE USED FOR OUT-PARAMS\n");
1636 set_query_in_com_data(
1637 &cmd, "SET @my_v1=null, @my_v2=null, @my_v3=null, @my_v4=null");
1638 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1639
1640 ctx.tables.clear();
1641 set_query_in_com_data(
1642 &cmd, "PREPARE stmt FROM 'CALL proc_set_out_params(?, ?, ?, ?)'");
1643 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1644
1645 WRITE_STR("EXECUTE PREPARED STATEMENT WITHOUT PARAMETERS\n");
1646
1647 ctx.tables.clear();
1648 set_query_in_com_data(&cmd,
1649 "EXECUTE stmt USING @my_v1, @my_v2, @my_v3, @my_v4");
1650 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1651
1652 WRITE_STR(
1653 "VERIFY THAT VARIABLES ARE SET AND OUT PRAMETERS WERE NOT TRANSFERED "
1654 "IN METADATA\n");
1655
1656 if (ctx.tables.size() != 0) {
1657 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
1658 "Protocol send the out-parameters to the user");
1659 return;
1660 }
1661
1662 ctx.tables.clear();
1663 set_query_in_com_data(
1664 &cmd,
1665 "CALL verify_user_variables_are_set(@my_v1, @my_v2, @my_v3, @my_v4)");
1666 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1667
1668 if (ctx.sql_errno) {
1669 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
1670 "Call to 'verify_user_variables_are_set' failed, one of the "
1671 "provided user variables may be invalid");
1672 return;
1673 }
1674
1675 WRITE_STR("CLOSE PS\n");
1676 ctx.tables.clear();
1677 set_query_in_com_data(&cmd, "DEALLOCATE PREPARE stmt;");
1678 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1679 }
1680
tear_down_test(MYSQL_SESSION session,void * p)1681 static void tear_down_test(MYSQL_SESSION session, void *p) {
1682 DBUG_TRACE;
1683
1684 Server_context ctx;
1685 COM_DATA cmd;
1686
1687 set_query_in_com_data(&cmd, "DROP TABLE IF EXISTS t1");
1688 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1689 set_query_in_com_data(&cmd, "DROP TABLE IF EXISTS t2");
1690 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1691 set_query_in_com_data(&cmd, "DROP TABLE IF EXISTS test_long_data");
1692 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1693 set_query_in_com_data(&cmd, "DROP TABLE IF EXISTS t3");
1694 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1695 set_query_in_com_data(&cmd, "DROP TABLE IF EXISTS t4");
1696 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1697 set_query_in_com_data(&cmd, "DROP PROCEDURE IF EXISTS p1");
1698 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1699 set_query_in_com_data(&cmd, "DROP PROCEDURE IF EXISTS proc_set_out_params");
1700 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1701 set_query_in_com_data(
1702 &cmd, "DROP PROCEDURE IF EXISTS verify_user_variables_are_null");
1703 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1704 set_query_in_com_data(
1705 &cmd, "DROP PROCEDURE IF EXISTS verify_user_variables_are_set");
1706 run_cmd(session, COM_QUERY, &cmd, &ctx, false, p);
1707 }
1708
1709 static const char *user_localhost = "localhost";
1710 static const char *user_local = "127.0.0.1";
1711 static const char *user_db = "";
1712 static const char *user_privileged = "root";
1713 // static const char *user_ordinary= "ordinary";
1714
switch_user(MYSQL_SESSION session,const char * user)1715 static void switch_user(MYSQL_SESSION session, const char *user) {
1716 MYSQL_SECURITY_CONTEXT sc;
1717 thd_get_security_context(srv_session_info_get_thd(session), &sc);
1718 security_context_lookup(sc, user, user_localhost, user_local, user_db);
1719 }
1720
1721 struct my_stmt_tests_st {
1722 const char *name;
1723 void (*function)(MYSQL_SESSION, void *);
1724 };
1725
1726 static struct my_stmt_tests_st my_tests[] = {
1727 {"test COM_STMT_EXECUTE and FETCH AFTER CLOSE", test_1},
1728 {"Test COM_STMT_EXECUTE with cursor", test_2},
1729 {"Test COM_STMT_EXECUTE without cursor", test_3},
1730 {"Test ps with different data-types", test_4},
1731 {"Test COM_STMT_SEND_LONG_DATA", test_5},
1732 {"Test COM_STMT_EXECUTE with SELECT nested in CALL", test_6},
1733 {"Test COM_STMT_EXECUTE with wrong data type", test_7},
1734 {"Test COM_STMT_EXECUTE with out-params as placeholders", test_8},
1735 {"Test COM_STMT_EXECUTE with out-params as variables", test_9},
1736 {"Test COM_QUERY with out-params as placeholders", test_10},
1737 {nullptr, nullptr}};
1738
test_sql(void * p)1739 static void test_sql(void *p) {
1740 DBUG_TRACE;
1741
1742 char buffer[LARGE_STRING_BUFFER_SIZE];
1743
1744 /* Session declarations */
1745 MYSQL_SESSION session;
1746
1747 /* Open session 1: Must pass */
1748 WRITE_STR("[srv_session_open]\n");
1749 session = srv_session_open(NULL, NULL);
1750 if (!session) {
1751 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "srv_session_open failed");
1752 goto end;
1753 }
1754
1755 switch_user(session, user_privileged);
1756 setup_test(session, p);
1757
1758 struct my_stmt_tests_st *fptr;
1759 for (fptr = my_tests; fptr->name; fptr++) {
1760 WRITE_HASHED_LINE()
1761 WRITE_VAL("%s\n", fptr->name);
1762 WRITE_HASHED_LINE()
1763 (*fptr->function)(session, p);
1764 }
1765
1766 tear_down_test(session, p);
1767 /* close session 1: Must pass */
1768 WRITE_STR("[srv_session_close]\n");
1769 if (srv_session_close(session))
1770 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "srv_session_close failed.");
1771
1772 end:
1773 return;
1774 }
1775
1776 struct test_thread_context {
1777 my_thread_handle thread;
1778 void *p;
1779 bool thread_finished;
1780 void (*test_function)(void *);
1781 };
1782
test_sql_threaded_wrapper(void * param)1783 static void *test_sql_threaded_wrapper(void *param) {
1784 char buffer[STRING_BUFFER_SIZE];
1785 struct test_thread_context *context = (struct test_thread_context *)param;
1786
1787 WRITE_SEP();
1788 WRITE_STR("init thread\n");
1789 if (srv_session_init_thread(context->p))
1790 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
1791 "srv_session_init_thread failed.");
1792
1793 context->test_function(context->p);
1794
1795 WRITE_STR("deinit thread\n");
1796 srv_session_deinit_thread();
1797
1798 context->thread_finished = true;
1799 return nullptr;
1800 }
1801
create_log_file(const char * log_name)1802 static void create_log_file(const char *log_name) {
1803 char filename[FN_REFLEN];
1804
1805 fn_format(filename, log_name, "", ".log",
1806 MY_REPLACE_EXT | MY_UNPACK_FILENAME);
1807 unlink(filename);
1808 outfile = my_open(filename, O_CREAT | O_RDWR, MYF(0));
1809 }
1810
test_in_spawned_thread(void * p,void (* test_function)(void *))1811 static void test_in_spawned_thread(void *p, void (*test_function)(void *)) {
1812 my_thread_attr_t attr; /* Thread attributes */
1813 my_thread_attr_init(&attr);
1814 (void)my_thread_attr_setdetachstate(&attr, MY_THREAD_CREATE_JOINABLE);
1815
1816 // Default stack size may be too small.
1817 size_t stacksize = 0;
1818 my_thread_attr_getstacksize(&attr, &stacksize);
1819 if (stacksize < my_thread_stack_size)
1820 my_thread_attr_setstacksize(&attr, my_thread_stack_size);
1821
1822 struct test_thread_context context;
1823
1824 context.p = p;
1825 context.thread_finished = false;
1826 context.test_function = test_function;
1827
1828 /* now create the thread and call test_session within the thread. */
1829 if (my_thread_create(&(context.thread), &attr, test_sql_threaded_wrapper,
1830 &context) != 0)
1831 LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
1832 "Could not create test session thread");
1833 else
1834 my_thread_join(&context.thread, nullptr);
1835 }
1836
test_sql_service_plugin_init(void * p)1837 static int test_sql_service_plugin_init(void *p) {
1838 char buffer[STRING_BUFFER_SIZE];
1839 DBUG_TRACE;
1840
1841 if (init_logging_service_for_plugin(®_srv, &log_bi, &log_bs)) return 1;
1842 LogPluginErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "Installation.");
1843
1844 create_log_file(log_filename);
1845
1846 WRITE_SEP();
1847 WRITE_STR("Test in a server thread\n");
1848 test_sql(p);
1849
1850 /* Test in a new thread */
1851 WRITE_STR("Follows threaded run\n");
1852 test_in_spawned_thread(p, test_sql);
1853
1854 my_close(outfile, MYF(0));
1855
1856 return 0;
1857 }
1858
test_sql_service_plugin_deinit(void * p MY_ATTRIBUTE ((unused)))1859 static int test_sql_service_plugin_deinit(void *p MY_ATTRIBUTE((unused))) {
1860 DBUG_TRACE;
1861 LogPluginErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "Uninstallation.");
1862 deinit_logging_service_for_plugin(®_srv, &log_bi, &log_bs);
1863 return 0;
1864 }
1865
1866 static struct st_mysql_daemon test_sql_service_plugin = {
1867 MYSQL_DAEMON_INTERFACE_VERSION};
1868
1869 /*
1870 Plugin library descriptor
1871 */
1872
mysql_declare_plugin(test_daemon)1873 mysql_declare_plugin(test_daemon){
1874 MYSQL_DAEMON_PLUGIN,
1875 &test_sql_service_plugin,
1876 "test_sql_stmt",
1877 PLUGIN_AUTHOR_ORACLE,
1878 "Tests prepared statements",
1879 PLUGIN_LICENSE_GPL,
1880 test_sql_service_plugin_init, /* Plugin Init */
1881 nullptr, /* Plugin Check uninstall */
1882 test_sql_service_plugin_deinit, /* Plugin Deinit */
1883 0x0100 /* 1.0 */,
1884 nullptr, /* status variables */
1885 nullptr, /* system variables */
1886 nullptr, /* config options */
1887 0, /* flags */
1888 } mysql_declare_plugin_end;
1889 /* purecov: end */
1890