1 /* Copyright (c) 2015, 2021, Oracle and/or its affiliates.
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 <stdlib.h>
24 #include <my_global.h>
25 #include "my_sys.h"                             // my_write, my_malloc
26 #include <mysql/plugin.h>
27 #include "mysql_com.h"
28 #include "m_string.h"
29 #include "sql_plugin.h"                         // st_plugin_int
30 
31 #define STRING_BUFFER_SIZE 256
32 
33 static const char *sep = "======================================================\n";
34 
35 #define WRITE_SEP() my_write(outfile, (uchar*)sep, strlen(sep), MYF(0))
36 
37 static File outfile;
38 
WRITE_STR(const char * format)39 void WRITE_STR(const char *format)
40 {
41   char buffer[STRING_BUFFER_SIZE];
42   my_snprintf(buffer,sizeof(buffer),format);
43   my_write(outfile,(uchar*)buffer,strlen(buffer),MYF(0));
44 }
45 
46 
47 template<typename T>
WRITE_VAL(const char * format,T value)48 void WRITE_VAL(const char *format, T value)
49 {
50   char buffer[STRING_BUFFER_SIZE];
51   my_snprintf(buffer,sizeof(buffer),format,value);
52   my_write(outfile,(uchar*)buffer,strlen(buffer),MYF(0));
53 }
54 
55 
56 template<typename T1, typename T2>
WRITE_VAL(const char * format,T1 value1,T2 value2)57 void WRITE_VAL(const char *format, T1 value1, T2 value2)
58 {
59   char buffer[STRING_BUFFER_SIZE];
60   my_snprintf(buffer,sizeof(buffer),format,value1,value2);
61   my_write(outfile,(uchar*)buffer,strlen(buffer),MYF(0));
62 }
63 
64 
65 struct st_send_field_n
66 {
67   char db_name[256];
68   char table_name[256];
69   char org_table_name[256];
70   char col_name[256];
71   char org_col_name[256];
72   unsigned long length;
73   unsigned int charsetnr;
74   unsigned int flags;
75   unsigned int decimals;
76   enum_field_types type;
77 };
78 
79 
80 struct st_decimal_n {
81   int    intg, frac, len;
82   my_bool sign;
83   decimal_digit_t buf[256];
84 };
85 
86 
87 struct st_plugin_ctx
88 {
89   const CHARSET_INFO *resultcs;
90   uint meta_server_status;
91   uint meta_warn_count;
92   uint current_col;
93   uint num_cols;
94   uint num_rows;
95   st_send_field_n sql_field[64];
96   char sql_str_value[64][64][256];
97   size_t sql_str_len[64][64];
98   longlong sql_int_value[64][64];
99   longlong sql_longlong_value[64][64];
100   uint sql_is_unsigned[64][64];
101   st_decimal_n sql_decimal_value[64][64];
102   double sql_double_value[64][64];
103   uint32 sql_double_decimals[64][64];
104   MYSQL_TIME sql_date_value[64][64];
105   MYSQL_TIME sql_time_value[64][64];
106   uint sql_time_decimals[64][64];
107   MYSQL_TIME sql_datetime_value[64][64];
108   uint sql_datetime_decimals[64][64];
109 
110   uint server_status;
111   uint warn_count;
112   uint affected_rows;
113   uint last_insert_id;
114   char message[1024];
115 
116   uint sql_errno;
117   char err_msg[1024];
118   char sqlstate[6];
st_plugin_ctxst_plugin_ctx119   st_plugin_ctx()
120   {
121     reset();
122   }
123 
resetst_plugin_ctx124   void reset()
125   {
126     resultcs= NULL;
127     server_status= 0;
128     current_col= 0;
129     warn_count= 0;
130     num_cols= 0;
131     num_rows= 0;
132     memset(&sql_field, 0, 64 * sizeof(st_send_field_n));
133     memset(&sql_str_value, 0, 64 * 64 * 256 * sizeof(char));
134     memset(&sql_str_len, 0, 64 * 64 * sizeof(size_t));
135     memset(&sql_int_value, 0, 64 * 64 * sizeof(longlong));
136     memset(&sql_longlong_value, 0, 64 * 64 * sizeof(longlong));
137     memset(&sql_is_unsigned, 0, 64 * 64 * sizeof(uint));
138     memset(&sql_decimal_value, 0, 64 * 64 * sizeof(st_decimal_n));
139     memset(&sql_double_value, 0, 64 * 64 * sizeof(double));
140     memset(&sql_double_decimals, 0, 64 * 64 * sizeof(uint32));
141     memset(&sql_date_value, 0, 64 * 64 * sizeof(MYSQL_TIME));
142     memset(&sql_time_value, 0, 64 * 64 * sizeof(MYSQL_TIME));
143     memset(&sql_time_decimals, 0, 64 * 64 * sizeof(uint));
144     memset(&sql_datetime_value, 0, 64 * 64 * sizeof(MYSQL_TIME));
145     memset(&sql_datetime_decimals, 0, 64 * 64 * sizeof(uint));
146 
147     server_status= 0;
148     warn_count= 0;
149     affected_rows= 0;
150     last_insert_id= 0;
151     memset(&message, 0, sizeof(message));
152 
153     sql_errno= 0;
154     memset(&err_msg, 0, sizeof(err_msg));
155     memset(&sqlstate, 0, sizeof(sqlstate));
156   }
157 };
158 
159 
sql_start_result_metadata(void * ctx,uint num_cols,uint flags,const CHARSET_INFO * resultcs)160 static int sql_start_result_metadata(void *ctx, uint num_cols, uint flags,
161                                      const CHARSET_INFO *resultcs)
162 {
163   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
164   DBUG_ENTER("sql_start_result_metadata");
165   DBUG_PRINT("info",("resultcs->number: %d", resultcs->number));
166   DBUG_PRINT("info",("resultcs->csname: %s", resultcs->csname));
167   DBUG_PRINT("info",("resultcs->name: %s", resultcs->name));
168   pctx->num_cols= num_cols;
169   pctx->resultcs= resultcs;
170   pctx->current_col= 0;
171   DBUG_RETURN(false);
172 }
173 
174 
sql_field_metadata(void * ctx,struct st_send_field * field,const CHARSET_INFO * charset)175 static int sql_field_metadata(void *ctx, struct st_send_field *field,
176                               const CHARSET_INFO *charset)
177 {
178   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
179   st_send_field_n *cfield= &pctx->sql_field[pctx->current_col];
180   DBUG_ENTER("sql_field_metadata");
181   DBUG_PRINT("info",("field->db_name: %s", field->db_name));
182   DBUG_PRINT("info",("field->table_name: %s", field->table_name));
183   DBUG_PRINT("info",("field->org_table_name: %s", field->org_table_name));
184   DBUG_PRINT("info",("field->col_name: %s", field->col_name));
185   DBUG_PRINT("info",("field->org_col_name: %s", field->org_col_name));
186   DBUG_PRINT("info",("field->length: %d", (int)field->length));
187   DBUG_PRINT("info",("field->charsetnr: %d", (int)field->charsetnr));
188   DBUG_PRINT("info",("field->flags: %d", (int)field->flags));
189   DBUG_PRINT("info",("field->decimals: %d", (int)field->decimals));
190   DBUG_PRINT("info",("field->type: %d", (int)field->type));
191 
192   strcpy(cfield->db_name,        (char*)field->db_name);
193   strcpy(cfield->table_name,     (char*)field->table_name);
194   strcpy(cfield->org_table_name, (char*)field->org_table_name);
195   strcpy(cfield->col_name,       (char*)field->col_name);
196   strcpy(cfield->org_col_name,   (char*)field->org_col_name);
197   cfield->length=    field->length;
198   cfield->charsetnr= field->charsetnr;
199   cfield->flags=     field->flags;
200   cfield->decimals=  field->decimals;
201   cfield->type=      field->type;
202 
203   pctx->current_col++;
204   DBUG_RETURN(false);
205 }
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 {
211   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
212   DBUG_ENTER("sql_end_result_metadata");
213   pctx->meta_server_status= server_status;
214   pctx->meta_warn_count= warn_count;
215   pctx->num_rows= 0;
216   DBUG_RETURN(false);
217 }
218 
219 
sql_start_row(void * ctx)220 static int sql_start_row(void *ctx)
221 {
222   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
223   DBUG_ENTER("sql_start_row");
224   pctx->current_col= 0;
225   DBUG_RETURN(false);
226 }
227 
228 
sql_end_row(void * ctx)229 static int sql_end_row(void *ctx)
230 {
231   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
232   DBUG_ENTER("sql_end_row");
233   pctx->num_rows++;
234   DBUG_RETURN(false);
235 }
236 
237 
sql_abort_row(void * ctx)238 static void sql_abort_row(void *ctx)
239 {
240   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
241   DBUG_ENTER("sql_abort_row");
242   pctx->current_col= 0;
243   DBUG_VOID_RETURN;
244 };
245 
246 
sql_get_client_capabilities(void * ctx)247 static ulong sql_get_client_capabilities(void *ctx){
248   DBUG_ENTER("sql_get_client_capabilities");
249   DBUG_RETURN(0);
250 }
251 
252 
sql_get_null(void * ctx)253 static int sql_get_null(void *ctx)
254 {
255   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
256   DBUG_ENTER("sql_get_null");
257   uint row= pctx->num_rows;
258   uint col= pctx->current_col;
259   pctx->current_col++;
260 
261   strcpy(pctx->sql_str_value[row][col], "[NULL]");
262   pctx->sql_str_len[row][col]=  sizeof("[NULL]")-1;
263 
264   DBUG_RETURN(false);
265 }
266 
267 
sql_get_integer(void * ctx,longlong value)268 static int sql_get_integer(void * ctx, longlong value)
269 {
270   char buffer[1024];
271   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
272   DBUG_ENTER("sql_get_integer");
273   uint row= pctx->num_rows;
274   uint col= pctx->current_col;
275   pctx->current_col++;
276 
277   size_t len= my_snprintf(buffer, sizeof(buffer), "%d", value);
278 
279   strncpy(pctx->sql_str_value[row][col], buffer, len);
280   pctx->sql_str_len[row][col]= len;
281   pctx->sql_int_value[row][col]= value;
282 
283   DBUG_RETURN(false);
284 }
285 
286 
sql_get_longlong(void * ctx,longlong value,uint is_unsigned)287 static int sql_get_longlong(void * ctx, longlong value, uint is_unsigned)
288 {
289   char buffer[1024];
290   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
291   DBUG_ENTER("sql_get_longlong");
292   uint row= pctx->num_rows;
293   uint col= pctx->current_col;
294   pctx->current_col++;
295 
296   size_t len= my_snprintf(buffer, sizeof(buffer),
297                           is_unsigned? "%llu":"%lld", value);
298 
299   strncpy(pctx->sql_str_value[row][col], buffer, len);
300   pctx->sql_str_len[row][col]= len;
301   pctx->sql_longlong_value[row][col]= value;
302   pctx->sql_is_unsigned[row][col]= is_unsigned;
303 
304   DBUG_RETURN(false);
305 }
306 
307 
sql_get_decimal(void * ctx,const decimal_t * value)308 static int sql_get_decimal(void * ctx, const decimal_t * value)
309 {
310   char buffer[1024];
311   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
312   DBUG_ENTER("sql_get_decimal");
313   uint row= pctx->num_rows;
314   uint col= pctx->current_col;
315   pctx->current_col++;
316 
317   size_t len= my_snprintf(buffer, sizeof(buffer),
318                           "%s%d.%d(%d)[%s]",
319                           value->sign? "+":"-",
320                           value->intg, value->frac, value->len,
321                           value->buf);
322 
323   strncpy(pctx->sql_str_value[row][col], buffer, len);
324 
325   pctx->sql_str_len[row][col]= len;
326   pctx->sql_decimal_value[row][col].intg= value->intg;
327   pctx->sql_decimal_value[row][col].frac= value->frac;
328   pctx->sql_decimal_value[row][col].len = value->len ;
329   pctx->sql_decimal_value[row][col].sign=  value->sign;
330   memset((void*)pctx->sql_decimal_value[row][col].buf, '\0',(int)value->len);
331   memcpy((void*)pctx->sql_decimal_value[row][col].buf, (void*)value->buf,(int)value->len);
332 
333   DBUG_RETURN(false);
334 }
335 
336 
sql_get_double(void * ctx,double value,uint32 decimals)337 static int sql_get_double(void * ctx, double value, uint32 decimals)
338 {
339   char buffer[1024];
340   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
341   DBUG_ENTER("sql_get_double");
342   uint row= pctx->num_rows;
343   uint col= pctx->current_col;
344   pctx->current_col++;
345 
346   size_t len= my_snprintf(buffer, sizeof(buffer), "%3.7g", value);
347 
348   strncpy(pctx->sql_str_value[row][col], buffer, len);
349   pctx->sql_str_len[row][col]= len;
350 
351   pctx->sql_double_value[row][col]= value;
352   pctx->sql_double_decimals[row][col]= decimals;
353 
354   DBUG_RETURN(false);
355 }
356 
357 
sql_get_date(void * ctx,const MYSQL_TIME * value)358 static int sql_get_date(void * ctx, const MYSQL_TIME * value)
359 {
360   char buffer[1024];
361   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
362   DBUG_ENTER("sql_get_date");
363   uint row= pctx->num_rows;
364   uint col= pctx->current_col;
365   pctx->current_col++;
366 
367   size_t len= my_snprintf(buffer, sizeof(buffer),
368                           "%s%4d-%02d-%02d",
369                           value->neg? "-":"",
370                           value->year, value->month, value->day);
371 
372   strncpy(pctx->sql_str_value[row][col], buffer, len);
373   pctx->sql_str_len[row][col]= len;
374 
375   pctx->sql_date_value[row][col].year=        value->year;
376   pctx->sql_date_value[row][col].month=       value->month;
377   pctx->sql_date_value[row][col].day=         value->day;
378 
379   pctx->sql_date_value[row][col].hour=        value->hour;
380   pctx->sql_date_value[row][col].minute=      value->minute;
381   pctx->sql_date_value[row][col].second=      value->second;
382   pctx->sql_date_value[row][col].second_part= value->second_part;
383   pctx->sql_date_value[row][col].neg=         value->neg;
384 
385   DBUG_RETURN(false);
386 }
387 
388 
sql_get_time(void * ctx,const MYSQL_TIME * value,uint decimals)389 static int sql_get_time(void * ctx, const MYSQL_TIME * value, uint decimals)
390 {
391   char buffer[1024];
392   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
393   DBUG_ENTER("sql_get_time");
394   uint row= pctx->num_rows;
395   uint col= pctx->current_col;
396   pctx->current_col++;
397 
398   size_t len= my_snprintf(buffer, sizeof(buffer),
399                           "%s%02d:%02d:%02d",
400                           value->neg? "-":"",
401                           value->day? (value->day*24 + value->hour):value->hour,
402                           value->minute, value->second);
403 
404   strncpy(pctx->sql_str_value[row][col], buffer, len);
405   pctx->sql_str_len[row][col]= len;
406 
407   pctx->sql_time_value[row][col].year=        value->year;
408   pctx->sql_time_value[row][col].month=       value->month;
409   pctx->sql_time_value[row][col].day=         value->day;
410 
411   pctx->sql_time_value[row][col].hour=        value->hour;
412   pctx->sql_time_value[row][col].minute=      value->minute;
413   pctx->sql_time_value[row][col].second=      value->second;
414   pctx->sql_time_value[row][col].second_part= value->second_part;
415   pctx->sql_time_value[row][col].neg=         value->neg;
416   pctx->sql_time_decimals[row][col]=          decimals;
417 
418   DBUG_RETURN(false);
419 }
420 
421 
sql_get_datetime(void * ctx,const MYSQL_TIME * value,uint decimals)422 static int sql_get_datetime(void * ctx, const MYSQL_TIME * value, uint decimals)
423 {
424   char buffer[1024];
425   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
426   DBUG_ENTER("sql_get_datetime");
427   uint row= pctx->num_rows;
428   uint col= pctx->current_col;
429   pctx->current_col++;
430 
431   size_t len= my_snprintf(buffer, sizeof(buffer),
432                           "%s%4d-%02d-%02d %02d:%02d:%02d",
433                           value->neg? "-":"",
434                           value->year, value->month, value->day,
435                           value->hour, value->minute, value->second);
436 
437   strncpy(pctx->sql_str_value[row][col], buffer, len);
438   pctx->sql_str_len[row][col]= len;
439 
440   pctx->sql_datetime_value[row][col].year=        value->year;
441   pctx->sql_datetime_value[row][col].month=       value->month;
442   pctx->sql_datetime_value[row][col].day=         value->day;
443 
444   pctx->sql_datetime_value[row][col].hour=        value->hour;
445   pctx->sql_datetime_value[row][col].minute=      value->minute;
446   pctx->sql_datetime_value[row][col].second=      value->second;
447   pctx->sql_datetime_value[row][col].second_part= value->second_part;
448   pctx->sql_datetime_value[row][col].neg=         value->neg;
449   pctx->sql_datetime_decimals[row][col]=          decimals;
450 
451   DBUG_RETURN(false);
452 }
453 
454 
sql_get_string(void * ctx,const char * const value,size_t length,const CHARSET_INFO * const valuecs)455 static int sql_get_string(void * ctx, const char * const value, size_t length,
456                           const CHARSET_INFO * const valuecs)
457 {
458   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
459   DBUG_ENTER("sql_get_string");
460   uint row= pctx->num_rows;
461   uint col= pctx->current_col;
462   pctx->current_col++;
463 
464   strncpy(pctx->sql_str_value[row][col], value, length);
465   pctx->sql_str_len[row][col]= length;
466 
467   DBUG_RETURN(false);
468 };
469 
470 
sql_handle_ok(void * ctx,uint server_status,uint statement_warn_count,ulonglong affected_rows,ulonglong last_insert_id,const char * const message)471 static void sql_handle_ok(void * ctx,
472                           uint server_status, uint statement_warn_count,
473                           ulonglong affected_rows, ulonglong last_insert_id,
474                           const char * const message)
475 {
476   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
477   DBUG_ENTER("sql_handle_ok");
478   /* This could be an EOF */
479   if (!pctx->num_cols)
480     pctx->num_rows= 0;
481   pctx->server_status=  server_status;
482   pctx->warn_count=     statement_warn_count;
483   pctx->affected_rows=  affected_rows;
484   pctx->last_insert_id= last_insert_id;
485   if (message)
486     strncpy(pctx->message, message, sizeof(pctx->message));
487 
488   DBUG_VOID_RETURN;
489 }
490 
491 
sql_handle_error(void * ctx,uint sql_errno,const char * const err_msg,const char * const sqlstate)492 static void sql_handle_error(void * ctx, uint sql_errno,
493                              const char * const err_msg,
494                              const char * const sqlstate)
495 {
496   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
497   DBUG_ENTER("sql_handle_error");
498   pctx->sql_errno=sql_errno;
499   if (pctx->sql_errno)
500   {
501     strcpy(pctx->err_msg, err_msg);
502     strcpy(pctx->sqlstate, sqlstate);
503   }
504   pctx->num_rows= 0;
505   DBUG_VOID_RETURN;
506 }
507 
508 
sql_shutdown(void * ctx,int shutdown_server)509 static void sql_shutdown(void *ctx, int shutdown_server)
510 {
511   DBUG_ENTER("sql_shutdown");
512   int *crashme = NULL;
513   *crashme = 0;
514   if (shutdown_server)
515     WRITE_STR("SERVER IS SHUTTING DOWN!!!\n");
516 
517   DBUG_VOID_RETURN;
518 }
519 
520 
521 const struct st_command_service_cbs sql_cbs= {
522   sql_start_result_metadata,
523   sql_field_metadata,
524   sql_end_result_metadata,
525   sql_start_row,
526   sql_end_row,
527   sql_abort_row,
528   sql_get_client_capabilities,
529   sql_get_null,
530   sql_get_integer,
531   sql_get_longlong,
532   sql_get_decimal,
533   sql_get_double,
534   sql_get_date,
535   sql_get_time,
536   sql_get_datetime,
537   sql_get_string,
538   sql_handle_ok,
539   sql_handle_error,
540   sql_shutdown,
541 };
542 
543 
get_data_integer(void * ctx)544 static void get_data_integer(void * ctx)
545 {
546   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
547 
548   /* get values */
549   if ((pctx->num_rows > 0) && (pctx->num_cols > 0))
550   {
551     for (uint col_count= 0; col_count < pctx->num_cols; col_count++)
552     {
553       WRITE_VAL("%s  ", pctx->sql_field[col_count].col_name);
554     }
555     WRITE_STR("\n");
556 
557     for (uint row_count= 0; row_count < pctx->num_rows; row_count++)
558     {
559       for (uint col_count= 0; col_count < pctx->num_cols; col_count++)
560       {
561         WRITE_VAL("%s  ", pctx->sql_str_value[row_count][col_count]);
562       }
563       WRITE_STR("\n");
564     }
565     WRITE_STR("\n");
566 
567     /* Metadata */
568     WRITE_VAL("num_cols      : %d\n", pctx->num_cols);
569     WRITE_VAL("nb rows       : %d\n", pctx->num_rows);
570   }
571 }
572 
573 
handle_error(void * ctx)574 static void handle_error(void * ctx)
575 {
576   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
577 
578   /* handle_ok/error */
579   if (pctx->sql_errno)
580   {
581      WRITE_VAL("error: %d\n",pctx->sql_errno);
582      WRITE_VAL("error msg: %s\n",pctx->err_msg);
583   }
584   else
585   {
586      WRITE_VAL("affected rows : %d\n",pctx->affected_rows);
587      WRITE_VAL("server status : %d\n",pctx->server_status);
588      WRITE_VAL("warn count    : %d\n",pctx->warn_count);
589   }
590 }
591 
592 static bool callback_called= false;
593 
error_callback(void * ctx,unsigned int sql_errno,const char * err_msg)594 static void error_callback(void *ctx, unsigned int sql_errno, const char *err_msg)
595 {
596   WRITE_VAL("ERROR %d : %s\n", sql_errno, err_msg);
597   callback_called= true;
598 }
599 
600 
exec_test_cmd(MYSQL_SESSION session,const char * test_cmd,void * p,void * ctx)601 static void exec_test_cmd(MYSQL_SESSION session, const char *test_cmd, void *p, void * ctx)
602 {
603   WRITE_VAL("%s\n",test_cmd);
604   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
605   COM_DATA cmd;
606 
607   pctx->reset();
608   cmd.com_query.query= (char *)test_cmd;
609   cmd.com_query.length= strlen(cmd.com_query.query);
610   int fail= command_service_run_command(session,COM_QUERY,&cmd, &my_charset_utf8_general_ci,
611                                         &sql_cbs, CS_BINARY_REPRESENTATION, ctx);
612   if (fail)
613   {
614     if (callback_called == false)
615       my_plugin_log_message(&p, MY_ERROR_LEVEL, "Callback not called");
616     else
617       callback_called= false;
618   }
619   else
620   {
621     get_data_integer(ctx);
622     handle_error(ctx);
623   }
624 }
625 
626 
627 enum enum_init_cycle
628 {
629   FIRST_INIT= 1,
630   SECOND_INIT= 2
631 };
632 
633 static enum enum_init_cycle plugin_init_cycle= FIRST_INIT;
634 
test_sql(void * p)635 static void test_sql(void *p)
636 {
637   DBUG_ENTER("test_sql");
638   struct st_plugin_ctx *plugin_ctx= new st_plugin_ctx();
639   MYSQL_SESSION session;
640 
641   /* Opening a Session */
642   WRITE_SEP();
643   WRITE_STR("Opening a Session\n");
644   session= srv_session_open(error_callback, plugin_ctx);
645   if (!session)
646   {
647     /*
648       We hope that this will always be okay. During the restart we will get here
649       before the server has started fully
650     */
651     if (srv_session_server_is_available() || !callback_called)
652     {
653       my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_open failed");
654       delete plugin_ctx;
655       DBUG_VOID_RETURN;
656     }
657 
658     while (!srv_session_server_is_available())
659       my_sleep(500);
660     plugin_init_cycle= SECOND_INIT;
661   }
662 
663   WRITE_SEP();
664   if (plugin_init_cycle == FIRST_INIT)
665   {
666     while (srv_session_server_is_available())
667       my_sleep(500);
668 
669     WRITE_STR("Apparently the server is shutting down\n");
670   }
671   exec_test_cmd(session, "SELECT 1, 2, 3 FROM DUAL", p, plugin_ctx);
672 
673   /* Close Session */
674   WRITE_VAL("\nClosing Session. Plugin init cycle = %d\n\n", plugin_init_cycle);
675 
676   srv_session_close(session);
677   delete plugin_ctx;
678 
679   DBUG_VOID_RETURN;
680 }
681 
682 
683 struct test_services_context
684 {
685   my_thread_handle test_services_thread;
686   void *p;
687 };
688 
689 
test_sql_threaded_wrapper(void * param)690 static void* test_sql_threaded_wrapper(void *param)
691 {
692   struct test_services_context *context= (struct test_services_context*) param;
693 
694   if (srv_session_init_thread(context->p))
695     my_plugin_log_message(&context->p, MY_ERROR_LEVEL, "srv_session_init_thread failed.");
696 
697   test_sql(context->p);
698 
699   srv_session_deinit_thread();
700 
701   return NULL;
702 }
703 
704 
create_log_file(const char * log_name)705 static void create_log_file(const char * log_name)
706 {
707   char filename[FN_REFLEN];
708 
709   fn_format(filename, log_name, "", ".log",
710             MY_REPLACE_EXT | MY_UNPACK_FILENAME);
711   unlink(filename);
712   outfile= my_open(filename, O_CREAT|O_RDWR, MYF(0));
713 }
714 
715 
test_sql_service_plugin_init(void * p)716 static int test_sql_service_plugin_init(void *p)
717 {
718   DBUG_ENTER("test_sql_service_plugin_init");
719   my_plugin_log_message(&p, MY_INFORMATION_LEVEL, "Installation.");
720 
721   create_log_file("test_sql_shutdown");
722 
723   struct test_services_context *context;
724   my_thread_attr_t attr;          /* Thread attributes */
725   my_thread_attr_init(&attr);
726   (void) my_thread_attr_setdetachstate(&attr, MY_THREAD_CREATE_JOINABLE);
727 
728   context= (struct test_services_context *)
729         my_malloc(PSI_INSTRUMENT_ME,
730         sizeof(struct test_services_context), MYF(0));
731   context->p= p;
732 
733   callback_called= false;
734   /* now create the thread and call test_session within the thread. */
735   if (my_thread_create(&context->test_services_thread, &attr, test_sql_threaded_wrapper, context) != 0)
736     my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_init_thread failed.");
737   else
738     ((struct st_plugin_int *) p)->data= (void*) context;
739 
740   /* Now we can shut down the server */
741   DBUG_RETURN(0);
742 }
743 
744 
test_sql_service_plugin_deinit(void * p)745 static int test_sql_service_plugin_deinit(void *p)
746 {
747   struct test_services_context *con= (struct test_services_context *) ((struct st_plugin_int *) p)->data;
748   DBUG_ENTER("test_sql_service_plugin_deinit");
749 
750   my_plugin_log_message(&p, MY_INFORMATION_LEVEL, "Uninstallation.");
751 
752   WRITE_STR("SERVER SHUTDOWN\n");
753 
754   if (con)
755   {
756     void *dummy_retval;
757     my_thread_cancel(&con->test_services_thread);
758     my_thread_join(&con->test_services_thread, &dummy_retval);
759   }
760   my_close(outfile, MYF(0));
761   my_free(con);
762   DBUG_RETURN(0);
763 }
764 
765 
766 struct st_mysql_daemon test_sql_service_plugin=
767 { MYSQL_DAEMON_INTERFACE_VERSION };
768 
769 
770 /*
771   Plugin library descriptor
772 */
773 
mysql_declare_plugin(test_daemon)774 mysql_declare_plugin(test_daemon)
775 {
776   MYSQL_DAEMON_PLUGIN,
777   &test_sql_service_plugin,
778   "test_sql_shutdown",
779   "Horst Hunger, Andrey Hristov",
780   "Test SQL shutdown",
781   PLUGIN_LICENSE_GPL,
782   test_sql_service_plugin_init,   /* Plugin Init      */
783   test_sql_service_plugin_deinit, /* Plugin Deinit    */
784   0x0100,                         /* 1.0              */
785   NULL,                           /* status variables */
786   NULL,                           /* system variables */
787   NULL,                           /* config options   */
788   0,                              /* flags            */
789 }
790 mysql_declare_plugin_end;
791