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 #include <fcntl.h>
24 #include <mysql/plugin.h>
25 #include <stdlib.h>
26 #include <sys/types.h>
27 
28 #include "m_string.h"
29 #include "my_dbug.h"
30 #include "my_inttypes.h"
31 #include "my_io.h"
32 #include "my_sys.h"  // my_write, my_malloc
33 #include "mysql_com.h"
34 #include "template_utils.h"
35 
36 #define LOG_COMPONENT_TAG "test_session_detach"
37 
38 #include <mysql/components/my_service.h>
39 #include <mysql/components/services/log_builtins.h>
40 #include <mysqld_error.h>
41 
42 static const char *log_filename = "test_session_detach";
43 
44 #define STRING_BUFFER_SIZE 1100
45 
46 static const char *sep =
47     "========================================================================="
48     "\n";
49 
50 #define WRITE_SEP() \
51   my_write(outfile, pointer_cast<const uchar *>(sep), strlen(sep), MYF(0))
52 
53 #define WRITE_STR(format)                                       \
54   {                                                             \
55     snprintf(buffer, sizeof(buffer), "%s", (format));           \
56     my_write(outfile, (uchar *)buffer, strlen(buffer), MYF(0)); \
57   }
58 
59 #define WRITE_VAL(format, value)                                \
60   {                                                             \
61     snprintf(buffer, sizeof(buffer), (format), (value));        \
62     my_write(outfile, (uchar *)buffer, strlen(buffer), MYF(0)); \
63   }
64 
65 #define WRITE_VAL2(format, value1, value2)                          \
66   {                                                                 \
67     snprintf(buffer, sizeof(buffer), (format), (value1), (value2)); \
68     my_write(outfile, (uchar *)buffer, strlen(buffer), MYF(0));     \
69   }
70 
71 static SERVICE_TYPE(registry) *reg_srv = nullptr;
72 SERVICE_TYPE(log_builtins) *log_bi = nullptr;
73 SERVICE_TYPE(log_builtins_string) *log_bs = nullptr;
74 
75 static File outfile;
76 
77 struct st_send_field_n {
78   char db_name[256];
79   char table_name[256];
80   char org_table_name[256];
81   char col_name[256];
82   char org_col_name[256];
83   unsigned long length;
84   unsigned int charsetnr;
85   unsigned int flags;
86   unsigned int decimals;
87   enum_field_types type;
88 };
89 
90 struct st_decimal_n {
91   int intg, frac, len;
92   bool sign;
93   decimal_digit_t buf[256];
94 };
95 
96 struct st_plugin_ctx {
97   const CHARSET_INFO *resultcs;
98   uint meta_server_status;
99   uint meta_warn_count;
100   uint current_col;
101   uint num_cols;
102   uint num_rows;
103   st_send_field_n sql_field[64];
104   char sql_str_value[64][64][256];
105   size_t sql_str_len[64][64];
106   int sql_int_value[64][64];
107   //  longlong sql_int_value[64][64];
108   longlong sql_longlong_value[64][64];
109   uint sql_is_unsigned[64][64];
110   st_decimal_n sql_decimal_value[64][64];
111   double sql_double_value[64][64];
112   uint32 sql_double_decimals[64][64];
113   MYSQL_TIME sql_date_value[64][64];
114   MYSQL_TIME sql_time_value[64][64];
115   uint sql_time_decimals[64][64];
116   MYSQL_TIME sql_datetime_value[64][64];
117   uint sql_datetime_decimals[64][64];
118 
119   uint server_status;
120   uint warn_count;
121   uint affected_rows;
122   uint last_insert_id;
123   char message[1024];
124 
125   uint sql_errno;
126   char err_msg[1024];
127   char sqlstate[6];
st_plugin_ctxst_plugin_ctx128   st_plugin_ctx() { reset(); }
129 
resetst_plugin_ctx130   void reset() {
131     resultcs = nullptr;
132     server_status = 0;
133     current_col = 0;
134     warn_count = 0;
135     num_cols = 0;
136     num_rows = 0;
137     memset(&sql_field, 0, 64 * sizeof(st_send_field_n));
138     memset(&sql_str_value, 0, 64 * 64 * 256 * sizeof(char));
139     memset(&sql_str_len, 0, 64 * 64 * sizeof(size_t));
140     memset(&sql_int_value, 0, 64 * 64 * sizeof(longlong));
141     memset(&sql_longlong_value, 0, 64 * 64 * sizeof(longlong));
142     memset(&sql_is_unsigned, 0, 64 * 64 * sizeof(uint));
143     memset(&sql_decimal_value, 0, 64 * 64 * sizeof(st_decimal_n));
144     memset(&sql_double_value, 0, 64 * 64 * sizeof(double));
145     memset(&sql_double_decimals, 0, 64 * 64 * sizeof(uint32));
146     memset(&sql_date_value, 0, 64 * 64 * sizeof(MYSQL_TIME));
147     memset(&sql_time_value, 0, 64 * 64 * sizeof(MYSQL_TIME));
148     memset(&sql_time_decimals, 0, 64 * 64 * sizeof(uint));
149     memset(&sql_datetime_value, 0, 64 * 64 * sizeof(MYSQL_TIME));
150     memset(&sql_datetime_decimals, 0, 64 * 64 * sizeof(uint));
151 
152     server_status = 0;
153     warn_count = 0;
154     affected_rows = 0;
155     last_insert_id = 0;
156     memset(&message, 0, sizeof(message));
157 
158     sql_errno = 0;
159     memset(&err_msg, 0, sizeof(err_msg));
160     memset(&sqlstate, 0, sizeof(sqlstate));
161   }
162 };
163 
sql_start_result_metadata(void * ctx,uint num_cols,uint,const CHARSET_INFO * resultcs)164 static int sql_start_result_metadata(void *ctx, uint num_cols, uint,
165                                      const CHARSET_INFO *resultcs) {
166   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
167   DBUG_TRACE;
168   DBUG_PRINT("info", ("resultcs->number: %d", resultcs->number));
169   DBUG_PRINT("info", ("resultcs->csname: %s", resultcs->csname));
170   DBUG_PRINT("info", ("resultcs->name: %s", resultcs->name));
171   pctx->num_cols = num_cols;
172   pctx->resultcs = resultcs;
173   pctx->current_col = 0;
174   return false;
175 }
176 
sql_field_metadata(void * ctx,struct st_send_field * field,const CHARSET_INFO *)177 static int sql_field_metadata(void *ctx, struct st_send_field *field,
178                               const CHARSET_INFO *) {
179   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
180   st_send_field_n *cfield = &pctx->sql_field[pctx->current_col];
181   DBUG_TRACE;
182   DBUG_PRINT("info", ("field->db_name: %s", field->db_name));
183   DBUG_PRINT("info", ("field->table_name: %s", field->table_name));
184   DBUG_PRINT("info", ("field->org_table_name: %s", field->org_table_name));
185   DBUG_PRINT("info", ("field->col_name: %s", field->col_name));
186   DBUG_PRINT("info", ("field->org_col_name: %s", field->org_col_name));
187   DBUG_PRINT("info", ("field->length: %d", (int)field->length));
188   DBUG_PRINT("info", ("field->charsetnr: %d", (int)field->charsetnr));
189   DBUG_PRINT("info", ("field->flags: %d", (int)field->flags));
190   DBUG_PRINT("info", ("field->decimals: %d", (int)field->decimals));
191   DBUG_PRINT("info", ("field->type: %d", (int)field->type));
192 
193   strcpy(cfield->db_name, field->db_name);
194   strcpy(cfield->table_name, field->table_name);
195   strcpy(cfield->org_table_name, field->org_table_name);
196   strcpy(cfield->col_name, field->col_name);
197   strcpy(cfield->org_col_name, field->org_col_name);
198   cfield->length = field->length;
199   cfield->charsetnr = field->charsetnr;
200   cfield->flags = field->flags;
201   cfield->decimals = field->decimals;
202   cfield->type = field->type;
203 
204   pctx->current_col++;
205   return false;
206 }
207 
sql_end_result_metadata(void * ctx,uint server_status,uint warn_count)208 static int sql_end_result_metadata(void *ctx, uint server_status,
209                                    uint warn_count) {
210   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
211   DBUG_TRACE;
212   pctx->meta_server_status = server_status;
213   pctx->meta_warn_count = warn_count;
214   pctx->num_rows = 0;
215   return false;
216 }
217 
sql_start_row(void * ctx)218 static int sql_start_row(void *ctx) {
219   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
220   DBUG_TRACE;
221   pctx->current_col = 0;
222   return false;
223 }
224 
sql_end_row(void * ctx)225 static int sql_end_row(void *ctx) {
226   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
227   DBUG_TRACE;
228   pctx->num_rows++;
229   return false;
230 }
231 
sql_abort_row(void * ctx)232 static void sql_abort_row(void *ctx) {
233   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
234   DBUG_TRACE;
235   pctx->current_col = 0;
236 }
237 
sql_get_client_capabilities(void *)238 static ulong sql_get_client_capabilities(void *) {
239   DBUG_TRACE;
240   return 0;
241 }
242 
sql_get_null(void * ctx)243 static int sql_get_null(void *ctx) {
244   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
245   DBUG_TRACE;
246   uint row = pctx->num_rows;
247   uint col = pctx->current_col;
248   pctx->current_col++;
249 
250   memcpy(pctx->sql_str_value[row][col], "[NULL]", sizeof("[NULL]"));
251   pctx->sql_str_len[row][col] = sizeof("[NULL]") - 1;
252 
253   return false;
254 }
255 
sql_get_integer(void * ctx,longlong value)256 static int sql_get_integer(void *ctx, longlong value) {
257   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
258   DBUG_TRACE;
259   uint row = pctx->num_rows;
260   uint col = pctx->current_col;
261   pctx->current_col++;
262 
263   size_t len = snprintf(pctx->sql_str_value[row][col],
264                         sizeof(pctx->sql_str_value[row][col]), "%lld", value);
265   pctx->sql_str_len[row][col] = len;
266   pctx->sql_int_value[row][col] = value;
267 
268   return false;
269 }
270 
sql_get_longlong(void * ctx,longlong value,uint is_unsigned)271 static int sql_get_longlong(void *ctx, longlong value, uint is_unsigned) {
272   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
273   DBUG_TRACE;
274   uint row = pctx->num_rows;
275   uint col = pctx->current_col;
276   pctx->current_col++;
277 
278   size_t len = snprintf(pctx->sql_str_value[row][col],
279                         sizeof(pctx->sql_str_value[row][col]),
280                         is_unsigned ? "%llu" : "%lld", value);
281 
282   pctx->sql_str_len[row][col] = len;
283   pctx->sql_longlong_value[row][col] = value;
284   pctx->sql_is_unsigned[row][col] = is_unsigned;
285 
286   return false;
287 }
288 
sql_get_decimal(void * ctx,const decimal_t * value)289 static int sql_get_decimal(void *ctx, const decimal_t *value) {
290   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
291   DBUG_TRACE;
292   uint row = pctx->num_rows;
293   uint col = pctx->current_col;
294   pctx->current_col++;
295 
296   size_t len = snprintf(pctx->sql_str_value[row][col],
297                         sizeof(pctx->sql_str_value[row][col]),
298                         "%s%d.%d(%d)[%s]", value->sign ? "+" : "-", value->intg,
299                         value->frac, value->len, (char *)value->buf);
300   pctx->sql_str_len[row][col] = len;
301   pctx->sql_decimal_value[row][col].intg = value->intg;
302   pctx->sql_decimal_value[row][col].frac = value->frac;
303   pctx->sql_decimal_value[row][col].len = value->len;
304   pctx->sql_decimal_value[row][col].sign = value->sign;
305   memset((void *)pctx->sql_decimal_value[row][col].buf, '\0', (int)value->len);
306   memcpy((void *)pctx->sql_decimal_value[row][col].buf, (void *)value->buf,
307          (int)value->len);
308 
309   return false;
310 }
311 
sql_get_double(void * ctx,double value,uint32 decimals)312 static int sql_get_double(void *ctx, double value, uint32 decimals) {
313   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
314   DBUG_TRACE;
315   uint row = pctx->num_rows;
316   uint col = pctx->current_col;
317   pctx->current_col++;
318 
319   size_t len = snprintf(pctx->sql_str_value[row][col],
320                         sizeof(pctx->sql_str_value[row][col]), "%3.7g", value);
321 
322   pctx->sql_str_len[row][col] = len;
323 
324   pctx->sql_double_value[row][col] = value;
325   pctx->sql_double_decimals[row][col] = decimals;
326 
327   return false;
328 }
329 
sql_get_date(void * ctx,const MYSQL_TIME * value)330 static int sql_get_date(void *ctx, const MYSQL_TIME *value) {
331   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
332   DBUG_TRACE;
333   uint row = pctx->num_rows;
334   uint col = pctx->current_col;
335   pctx->current_col++;
336 
337   size_t len =
338       snprintf(pctx->sql_str_value[row][col],
339                sizeof(pctx->sql_str_value[row][col]), "%s%4d-%02d-%02d",
340                value->neg ? "-" : "", value->year, value->month, value->day);
341   pctx->sql_str_len[row][col] = len;
342 
343   pctx->sql_date_value[row][col].year = value->year;
344   pctx->sql_date_value[row][col].month = value->month;
345   pctx->sql_date_value[row][col].day = value->day;
346 
347   pctx->sql_date_value[row][col].hour = value->hour;
348   pctx->sql_date_value[row][col].minute = value->minute;
349   pctx->sql_date_value[row][col].second = value->second;
350   pctx->sql_date_value[row][col].second_part = value->second_part;
351   pctx->sql_date_value[row][col].neg = value->neg;
352 
353   return false;
354 }
355 
sql_get_time(void * ctx,const MYSQL_TIME * value,uint decimals)356 static int sql_get_time(void *ctx, const MYSQL_TIME *value, uint decimals) {
357   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
358   DBUG_TRACE;
359   uint row = pctx->num_rows;
360   uint col = pctx->current_col;
361   pctx->current_col++;
362 
363   size_t len = snprintf(
364       pctx->sql_str_value[row][col], sizeof(pctx->sql_str_value[row][col]),
365       "%s%02d:%02d:%02d", value->neg ? "-" : "",
366       value->day ? (value->day * 24 + value->hour) : value->hour, value->minute,
367       value->second);
368 
369   pctx->sql_str_len[row][col] = len;
370 
371   pctx->sql_time_value[row][col].year = value->year;
372   pctx->sql_time_value[row][col].month = value->month;
373   pctx->sql_time_value[row][col].day = value->day;
374 
375   pctx->sql_time_value[row][col].hour = value->hour;
376   pctx->sql_time_value[row][col].minute = value->minute;
377   pctx->sql_time_value[row][col].second = value->second;
378   pctx->sql_time_value[row][col].second_part = value->second_part;
379   pctx->sql_time_value[row][col].neg = value->neg;
380   pctx->sql_time_decimals[row][col] = decimals;
381 
382   return false;
383 }
384 
sql_get_datetime(void * ctx,const MYSQL_TIME * value,uint decimals)385 static int sql_get_datetime(void *ctx, const MYSQL_TIME *value, uint decimals) {
386   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
387   DBUG_TRACE;
388   uint row = pctx->num_rows;
389   uint col = pctx->current_col;
390   pctx->current_col++;
391 
392   size_t len = snprintf(
393       pctx->sql_str_value[row][col], sizeof(pctx->sql_str_value[row][col]),
394       "%s%4d-%02d-%02d %02d:%02d:%02d", value->neg ? "-" : "", value->year,
395       value->month, value->day, value->hour, value->minute, value->second);
396 
397   pctx->sql_str_len[row][col] = len;
398 
399   pctx->sql_datetime_value[row][col].year = value->year;
400   pctx->sql_datetime_value[row][col].month = value->month;
401   pctx->sql_datetime_value[row][col].day = value->day;
402 
403   pctx->sql_datetime_value[row][col].hour = value->hour;
404   pctx->sql_datetime_value[row][col].minute = value->minute;
405   pctx->sql_datetime_value[row][col].second = value->second;
406   pctx->sql_datetime_value[row][col].second_part = value->second_part;
407   pctx->sql_datetime_value[row][col].neg = value->neg;
408   pctx->sql_datetime_decimals[row][col] = decimals;
409 
410   return false;
411 }
412 
sql_get_string(void * ctx,const char * const value,size_t length,const CHARSET_INFO * const)413 static int sql_get_string(void *ctx, const char *const value, size_t length,
414                           const CHARSET_INFO *const) {
415   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
416   DBUG_TRACE;
417   uint row = pctx->num_rows;
418   uint col = pctx->current_col;
419   pctx->current_col++;
420 
421   strncpy(pctx->sql_str_value[row][col], value, length);
422   pctx->sql_str_len[row][col] = length;
423 
424   return false;
425 }
426 
sql_handle_ok(void * ctx,uint server_status,uint statement_warn_count,ulonglong affected_rows,ulonglong last_insert_id,const char * const message)427 static void sql_handle_ok(void *ctx, uint server_status,
428                           uint statement_warn_count, ulonglong affected_rows,
429                           ulonglong last_insert_id, const char *const message) {
430   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
431   DBUG_TRACE;
432   /* This could be an EOF */
433   if (!pctx->num_cols) pctx->num_rows = 0;
434   pctx->server_status = server_status;
435   pctx->warn_count = statement_warn_count;
436   pctx->affected_rows = affected_rows;
437   pctx->last_insert_id = last_insert_id;
438   if (message) strncpy(pctx->message, message, sizeof(pctx->message) - 1);
439   pctx->message[sizeof(pctx->message) - 1] = '\0';
440 }
441 
sql_handle_error(void * ctx,uint sql_errno,const char * const err_msg,const char * const sqlstate)442 static void sql_handle_error(void *ctx, uint sql_errno,
443                              const char *const err_msg,
444                              const char *const sqlstate) {
445   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
446   DBUG_TRACE;
447   pctx->sql_errno = sql_errno;
448   if (pctx->sql_errno) {
449     strcpy(pctx->err_msg, err_msg);
450     strcpy(pctx->sqlstate, sqlstate);
451   }
452   pctx->num_rows = 0;
453 }
454 
sql_shutdown(void *,int)455 static void sql_shutdown(void *, int) { DBUG_TRACE; }
456 
457 const struct st_command_service_cbs sql_cbs = {
458     sql_start_result_metadata,
459     sql_field_metadata,
460     sql_end_result_metadata,
461     sql_start_row,
462     sql_end_row,
463     sql_abort_row,
464     sql_get_client_capabilities,
465     sql_get_null,
466     sql_get_integer,
467     sql_get_longlong,
468     sql_get_decimal,
469     sql_get_double,
470     sql_get_date,
471     sql_get_time,
472     sql_get_datetime,
473     sql_get_string,
474     sql_handle_ok,
475     sql_handle_error,
476     sql_shutdown,
477 };
478 
fieldtype2str(enum enum_field_types type)479 static const char *fieldtype2str(enum enum_field_types type) {
480   switch (type) {
481     case MYSQL_TYPE_BIT:
482       return "BIT";
483     case MYSQL_TYPE_BLOB:
484       return "BLOB";
485     case MYSQL_TYPE_DATE:
486       return "DATE";
487     case MYSQL_TYPE_DATETIME:
488       return "DATETIME";
489     case MYSQL_TYPE_NEWDECIMAL:
490       return "NEWDECIMAL";
491     case MYSQL_TYPE_DECIMAL:
492       return "DECIMAL";
493     case MYSQL_TYPE_DOUBLE:
494       return "DOUBLE";
495     case MYSQL_TYPE_ENUM:
496       return "ENUM";
497     case MYSQL_TYPE_FLOAT:
498       return "FLOAT";
499     case MYSQL_TYPE_GEOMETRY:
500       return "GEOMETRY";
501     case MYSQL_TYPE_INT24:
502       return "INT24";
503     case MYSQL_TYPE_LONG:
504       return "LONG";
505     case MYSQL_TYPE_LONGLONG:
506       return "LONGLONG";
507     case MYSQL_TYPE_LONG_BLOB:
508       return "LONG_BLOB";
509     case MYSQL_TYPE_MEDIUM_BLOB:
510       return "MEDIUM_BLOB";
511     case MYSQL_TYPE_NEWDATE:
512       return "NEWDATE";
513     case MYSQL_TYPE_NULL:
514       return "NULL";
515     case MYSQL_TYPE_SET:
516       return "SET";
517     case MYSQL_TYPE_SHORT:
518       return "SHORT";
519     case MYSQL_TYPE_STRING:
520       return "STRING";
521     case MYSQL_TYPE_TIME:
522       return "TIME";
523     case MYSQL_TYPE_TIMESTAMP:
524       return "TIMESTAMP";
525     case MYSQL_TYPE_TINY:
526       return "TINY";
527     case MYSQL_TYPE_TINY_BLOB:
528       return "TINY_BLOB";
529     case MYSQL_TYPE_VARCHAR:
530       return "VARCHAR";
531     case MYSQL_TYPE_VAR_STRING:
532       return "VAR_STRING";
533     case MYSQL_TYPE_YEAR:
534       return "YEAR";
535     default:
536       return "?-unknown-?";
537   }
538 }
539 
get_data_integer(void * ctx)540 static void get_data_integer(void *ctx) {
541   char buffer[STRING_BUFFER_SIZE];
542   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
543 
544   /* start metadata */
545   WRITE_VAL("num_cols : %d\n", pctx->num_cols);
546   WRITE_VAL("nb rows  : %d\n", pctx->num_rows);
547 
548   /* get values */
549   for (uint col = 0; col < pctx->num_cols; col++) {
550     WRITE_VAL("%s ", pctx->sql_field[col].col_name);
551     WRITE_VAL2("%s(%u)\t", fieldtype2str(pctx->sql_field[col].type),
552                pctx->sql_field[col].type);
553   }
554   WRITE_STR("\n");
555   for (uint row = 0; row < pctx->num_rows; row++) {
556     for (uint col = 0; col < pctx->num_cols; col++) {
557       WRITE_VAL("%d\t\t", pctx->sql_int_value[row][col]);
558     }
559     WRITE_STR("\n");
560   }
561   WRITE_STR("\n");
562 }
563 
handle_error(void * ctx)564 static void handle_error(void *ctx) {
565   char buffer[STRING_BUFFER_SIZE];
566   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
567 
568   /* handle_ok/error */
569   if (pctx->sql_errno) {
570     WRITE_VAL("error     : %d\n", pctx->sql_errno);
571     WRITE_VAL("error msg : %s\n", pctx->err_msg);
572   } else {
573     WRITE_VAL("affected rows : %d\n", pctx->affected_rows);
574     WRITE_VAL("server status : %d\n", pctx->server_status);
575     WRITE_VAL("warn count    : %d\n", pctx->warn_count);
576   }
577 }
578 
exec_test_cmd(MYSQL_SESSION session,const char * test_cmd,void * p MY_ATTRIBUTE ((unused)),void * ctx)579 static void exec_test_cmd(MYSQL_SESSION session, const char *test_cmd,
580                           void *p MY_ATTRIBUTE((unused)), void *ctx) {
581   char buffer[STRING_BUFFER_SIZE];
582   COM_DATA cmd;
583   struct st_plugin_ctx *pctx = (struct st_plugin_ctx *)ctx;
584 
585   WRITE_VAL("%s\n", test_cmd);
586   pctx->reset();
587   cmd.com_query.query = test_cmd;
588   cmd.com_query.length = strlen(cmd.com_query.query);
589   int fail = command_service_run_command(session, COM_QUERY, &cmd,
590                                          &my_charset_utf8_general_ci, &sql_cbs,
591                                          CS_BINARY_REPRESENTATION, ctx);
592 
593   if (fail)
594     LogPluginErrMsg(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
595                     "test_sql_2_sessions: ret code: %d", 0);
596   else {
597     if (pctx->num_cols) get_data_integer(ctx);
598     handle_error(ctx);
599   }
600 }
601 
test_sql(void * p)602 static void test_sql(void *p) {
603   char buffer[STRING_BUFFER_SIZE];
604 
605   DBUG_TRACE;
606   struct st_plugin_ctx *plugin_ctx = new st_plugin_ctx();
607 
608   /* Open Session 1 */
609   WRITE_STR("Opening Session 1\n");
610   MYSQL_SESSION session = srv_session_open(NULL, plugin_ctx);
611   if (!session)
612     LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "Open Session 1 failed.");
613 
614   WRITE_SEP();
615 
616   for (uint i = 0; i < 5; i++) {
617     WRITE_VAL("\nQuery %02d: ", i + 1);
618     exec_test_cmd(session, "SELECT * FROM test.t_int ORDER BY c1", p,
619                   plugin_ctx);
620     WRITE_STR("\nDetach Session 1\n");
621     if (srv_session_detach(session))
622       LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "Detach Session 1 failed.");
623   }
624 
625   /* Close Session 1 */
626   WRITE_SEP();
627   WRITE_STR("Close Session 1\n");
628   if (srv_session_close(session))
629     LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG, "Close Session 1 failed.");
630 
631   delete plugin_ctx;
632 }
633 
634 struct test_thread_context {
635   my_thread_handle thread;
636   void *p;
637   bool thread_finished;
638   void (*test_function)(void *);
639 };
640 
test_sql_threaded_wrapper(void * param)641 static void *test_sql_threaded_wrapper(void *param) {
642   char buffer[STRING_BUFFER_SIZE];
643   struct test_thread_context *context = (struct test_thread_context *)param;
644 
645   WRITE_SEP();
646   WRITE_STR("init thread\n");
647   if (srv_session_init_thread(context->p))
648     LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
649                  "srv_session_init_thread failed.");
650 
651   context->test_function(context->p);
652 
653   WRITE_STR("deinit thread\n");
654   srv_session_deinit_thread();
655 
656   context->thread_finished = true;
657   return nullptr;
658 }
659 
create_log_file(const char * log_name)660 static void create_log_file(const char *log_name) {
661   char filename[FN_REFLEN];
662 
663   fn_format(filename, log_name, "", ".log",
664             MY_REPLACE_EXT | MY_UNPACK_FILENAME);
665   unlink(filename);
666   outfile = my_open(filename, O_CREAT | O_RDWR, MYF(0));
667 }
668 
test_in_spawned_thread(void * p,void (* test_function)(void *))669 static void test_in_spawned_thread(void *p, void (*test_function)(void *)) {
670   my_thread_attr_t attr; /* Thread attributes */
671   my_thread_attr_init(&attr);
672   (void)my_thread_attr_setdetachstate(&attr, MY_THREAD_CREATE_JOINABLE);
673 
674   struct test_thread_context context;
675 
676   context.p = p;
677   context.thread_finished = false;
678   context.test_function = test_function;
679 
680   /* now create the thread and call test_session within the thread. */
681   if (my_thread_create(&(context.thread), &attr, test_sql_threaded_wrapper,
682                        &context) != 0)
683     LogPluginErr(ERROR_LEVEL, ER_LOG_PRINTF_MSG,
684                  "Could not create test session thread");
685   else
686     my_thread_join(&context.thread, nullptr);
687 }
688 
test_sql_service_plugin_init(void * p)689 static int test_sql_service_plugin_init(void *p) {
690   char buffer[STRING_BUFFER_SIZE];
691   DBUG_TRACE;
692   if (init_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs)) return 1;
693   LogPluginErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "Installation.");
694 
695   create_log_file(log_filename);
696 
697   WRITE_SEP();
698   WRITE_STR("Test in a server thread\n");
699   test_sql(p);
700 
701   /* Test in a new thread */
702   WRITE_STR("Follows threaded run\n");
703   test_in_spawned_thread(p, test_sql);
704 
705   my_close(outfile, MYF(0));
706 
707   return 0;
708 }
709 
test_sql_service_plugin_deinit(void * p MY_ATTRIBUTE ((unused)))710 static int test_sql_service_plugin_deinit(void *p MY_ATTRIBUTE((unused))) {
711   DBUG_TRACE;
712   LogPluginErr(INFORMATION_LEVEL, ER_LOG_PRINTF_MSG, "Uninstallation.");
713   deinit_logging_service_for_plugin(&reg_srv, &log_bi, &log_bs);
714   return 0;
715 }
716 
717 struct st_mysql_daemon test_sql_service_plugin = {
718     MYSQL_DAEMON_INTERFACE_VERSION};
719 
720 /*
721   Plugin library descriptor
722 */
723 
mysql_declare_plugin(test_daemon)724 mysql_declare_plugin(test_daemon){
725     MYSQL_DAEMON_PLUGIN,
726     &test_sql_service_plugin,
727     "test_session_detach",
728     PLUGIN_AUTHOR_ORACLE,
729     "Test session detach",
730     PLUGIN_LICENSE_GPL,
731     test_sql_service_plugin_init,   /* Plugin Init      */
732     nullptr,                        /* Plugin Check uninstall    */
733     test_sql_service_plugin_deinit, /* Plugin Deinit    */
734     0x0100,                         /* 1.0              */
735     nullptr,                        /* status variables */
736     nullptr,                        /* system variables */
737     nullptr,                        /* config options   */
738     0,                              /* flags            */
739 } mysql_declare_plugin_end;
740