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(&reg_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(&reg_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