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/service_srv_session_info.h>
28 #include "sql_string.h" /* STRING_PSI_MEMORY_KEY */
29 
30 #define STRING_BUFFER 256
31 
32 static const char *sep = "======================================================\n";
33 
34 #define WRITE_SEP() my_write(outfile, (uchar*)sep, strlen(sep), MYF(0))
35 
36 static File outfile;
37 
WRITE_STR(const char * format)38 void WRITE_STR(const char *format)
39 {
40   char buffer[STRING_BUFFER];
41   my_snprintf(buffer,sizeof(buffer),format);
42   my_write(outfile,(uchar*)buffer,strlen(buffer),MYF(0));
43 }
44 
45 template<typename T>
WRITE_VAL(const char * format,T value)46 void WRITE_VAL(const char *format, T value)
47 {
48   char buffer[STRING_BUFFER];
49   my_snprintf(buffer,sizeof(buffer),format,value);
50   my_write(outfile,(uchar*)buffer,strlen(buffer),MYF(0));
51 }
52 
53 template<typename T1, typename T2>
WRITE_VAL(const char * format,T1 value1,T2 value2)54 void WRITE_VAL(const char *format, T1 value1, T2 value2)
55 {
56   char buffer[STRING_BUFFER];
57   my_snprintf(buffer,sizeof(buffer),format,value1,value2);
58   my_write(outfile,(uchar*)buffer,strlen(buffer),MYF(0));
59 }
60 
61 
62 static const char *user_localhost = "localhost";
63 static const char *user_local = "127.0.0.1";
64 static const char *user_db= "";
65 static const char *user_privileged= "root";
66 static const char *user_ordinary= "ordinary";
67 
switch_user(MYSQL_SESSION session,const char * user)68 static void switch_user(MYSQL_SESSION session, const char *user)
69 {
70   MYSQL_SECURITY_CONTEXT sc;
71   thd_get_security_context(srv_session_info_get_thd(session), &sc);
72   security_context_lookup(sc, user, user_localhost, user_local, user_db);
73 }
74 
75 
ensure_api_ok(const char * function,int result)76 static void ensure_api_ok(const char *function, int result)
77 {
78   if (result != 0)
79   {
80     WRITE_VAL("ERROR calling %s: returned %i\n", function, result);
81   }
82 }
83 
ensure_api_not_null(const char * function,void * result)84 static void ensure_api_not_null(const char *function, void *result)
85 {
86   if (!result)
87   {
88     WRITE_VAL("ERROR calling %s: returned NULL\n", function);
89   }
90 }
91 #define ENSURE_API_OK(call) ensure_api_ok(__FUNCTION__, (call));
92 #define ENSURE_API_NOT_NULL(call) ensure_api_not_null(__FUNCTION__, (call));
93 
94 
95 struct Callback_data
96 {
97   int err;
98   std::string errmsg;
99   std::string sqlstate;
100   bool error_called;
101 
102   int server_status;
103   uint warn_count;
104   uint affected_rows;
105   uint last_insert_id;
106   std::string message;
107 
108   int shutdown;
109   bool shutdown_called;
110 
Callback_dataCallback_data111   Callback_data() { reset(); }
112 
resetCallback_data113   void reset()
114   {
115     error_called = false;
116     errmsg.clear();
117     sqlstate.clear();
118     message.clear();
119     err = 0;
120     server_status = 0;
121     warn_count = 0;
122     affected_rows = 0;
123     last_insert_id = 0;
124     shutdown = 0;
125     shutdown_called = 0;
126   }
127 };
128 
129 
130 struct st_send_field_n
131 {
132   char db_name[256];
133   char table_name[256];
134   char org_table_name[256];
135   char col_name[256];
136   char org_col_name[256];
137   unsigned long length;
138   unsigned int charsetnr;
139   unsigned int flags;
140   unsigned int decimals;
141   enum_field_types type;
142 };
143 
144 const CHARSET_INFO *sql_resultcs= NULL;
145 uint sql_num_meta_rows= 0;
146 uint sql_num_rows= 0;
147 uint col_count= 0;
148 uint sql_num_cols= 0;
149 uint sql_flags= 0;
150 st_send_field_n sql_field[64][64];
151 
152 int row_count= 0;
153 
154 
sql_start_result_metadata(void * ctx,uint num_cols,uint flags,const CHARSET_INFO * resultcs)155 static int sql_start_result_metadata(void *ctx, uint num_cols, uint flags,
156                                      const CHARSET_INFO *resultcs)
157 {
158   DBUG_ENTER("sql_start_result_metadata");
159   DBUG_PRINT("info",("resultcs->number: %d", resultcs->number));
160   DBUG_PRINT("info",("resultcs->csname: %s", resultcs->csname));
161   DBUG_PRINT("info",("resultcs->name: %s", resultcs->name));
162   row_count= 0;
163   sql_num_cols= num_cols;
164   sql_resultcs= resultcs;
165   DBUG_RETURN(false);
166 }
167 
sql_field_metadata(void * ctx,struct st_send_field * field,const CHARSET_INFO * charset)168 static int sql_field_metadata(void *ctx, struct st_send_field *field,
169                               const CHARSET_INFO *charset)
170 {
171   DBUG_ENTER("sql_field_metadata");
172   DBUG_PRINT("info",("field->db_name: %s", field->db_name));
173   DBUG_PRINT("info",("field->table_name: %s", field->table_name));
174   DBUG_PRINT("info",("field->org_table_name: %s", field->org_table_name));
175   DBUG_PRINT("info",("field->col_name: %s", field->col_name));
176   DBUG_PRINT("info",("field->org_col_name: %s", field->org_col_name));
177   DBUG_PRINT("info",("field->length: %d", (int)field->length));
178   DBUG_PRINT("info",("field->charsetnr: %d", (int)field->charsetnr));
179   DBUG_PRINT("info",("field->flags: %d", (int)field->flags));
180   DBUG_PRINT("info",("field->decimals: %d", (int)field->decimals));
181   DBUG_PRINT("info",("field->type: %d", (int)field->type));
182   strcpy(sql_field[col_count][row_count].db_name,(char*)field->db_name);
183   strcpy(sql_field[col_count][row_count].table_name,(char*)field->table_name);
184   strcpy(sql_field[col_count][row_count].org_table_name,(char*)field->org_table_name);
185   strcpy(sql_field[col_count][row_count].col_name,(char*)field->col_name);
186   strcpy(sql_field[col_count][row_count].org_col_name,(char*)field->org_col_name);
187   sql_field[col_count][row_count].length= field->length;
188   sql_field[col_count][row_count].charsetnr= field->charsetnr;
189   sql_field[col_count][row_count].flags= field->flags;
190   sql_field[col_count][row_count].decimals= field->decimals;
191   sql_field[col_count][row_count].type= field->type;
192   DBUG_RETURN(false);
193 }
194 
sql_end_result_metadata(void * ctx,uint server_status,uint warn_count)195 static int sql_end_result_metadata(void *ctx, uint server_status, uint warn_count)
196 {
197   DBUG_ENTER("sql_end_result_metadata");
198   sql_num_meta_rows= row_count;
199   row_count= 0;
200   DBUG_RETURN(false);
201 }
202 
sql_start_row(void * ctx)203 static int sql_start_row(void *ctx)
204 {
205   DBUG_ENTER("sql_start_row");
206   col_count= 0;
207   DBUG_RETURN(false);
208 }
209 
sql_end_row(void * ctx)210 static int sql_end_row(void *ctx)
211 {
212   DBUG_ENTER("sql_end_row");
213   row_count++;
214   DBUG_RETURN(false);
215 }
216 
sql_abort_row(void * ctx)217 static void sql_abort_row(void *ctx)
218 {
219   DBUG_ENTER("sql_abort_row");
220   DBUG_VOID_RETURN;
221 }
222 
sql_get_client_capabilities(void * ctx)223 static ulong sql_get_client_capabilities(void *ctx)
224 {
225   DBUG_ENTER("sql_get_client_capabilities");
226   DBUG_RETURN(0);
227 }
228 
sql_get_null(void * ctx)229 static int sql_get_null(void *ctx)
230 {
231   DBUG_ENTER("sql_get_null");
232   DBUG_RETURN(false);
233 }
234 
sql_get_integer(void * ctx,longlong value)235 static int sql_get_integer(void * ctx, longlong value)
236 {
237   DBUG_ENTER("sql_get_integer");
238   DBUG_RETURN(false);
239 }
240 
sql_get_longlong(void * ctx,longlong value,uint is_unsigned)241 static int sql_get_longlong(void * ctx, longlong value, uint is_unsigned)
242 {
243   DBUG_ENTER("sql_get_longlong");
244   DBUG_RETURN(false);
245 }
246 
sql_get_decimal(void * ctx,const decimal_t * value)247 static int sql_get_decimal(void * ctx, const decimal_t * value)
248 {
249   DBUG_ENTER("sql_get_decimal");
250   DBUG_RETURN(false);
251 }
252 
sql_get_double(void * ctx,double value,uint32 decimals)253 static int sql_get_double(void * ctx, double value, uint32 decimals)
254 {
255   DBUG_ENTER("sql_get_double");
256   DBUG_RETURN(false);
257 }
258 
sql_get_date(void * ctx,const MYSQL_TIME * value)259 static int sql_get_date(void * ctx, const MYSQL_TIME * value)
260 {
261   DBUG_ENTER("sql_get_date");
262   DBUG_RETURN(false);
263 }
264 
sql_get_time(void * ctx,const MYSQL_TIME * value,uint decimals)265 static int sql_get_time(void * ctx, const MYSQL_TIME * value, uint decimals)
266 {
267   DBUG_ENTER("sql_get_time");
268   DBUG_RETURN(false);
269 }
270 
sql_get_datetime(void * ctx,const MYSQL_TIME * value,uint decimals)271 static int sql_get_datetime(void * ctx, const MYSQL_TIME * value, uint decimals)
272 {
273   DBUG_ENTER("sql_get_datetime");
274   DBUG_RETURN(false);
275 }
276 
277 char sql_str_value[64][64][256];
278 size_t sql_str_len[64][64];
279 
sql_get_string(void * ctx,const char * const value,size_t length,const CHARSET_INFO * const valuecs)280 static int sql_get_string(void * ctx,
281 	                        const char * const value, size_t length,
282 		                      const CHARSET_INFO * const valuecs)
283 {
284   DBUG_ENTER("sql_get_string");
285   strncpy(sql_str_value[col_count][row_count],value,length);
286   sql_str_len[col_count][row_count]= length;
287   col_count++;
288   DBUG_RETURN(false);
289 }
290 
sql_handle_ok(void * ctx,uint server_status,uint statement_warn_count,ulonglong affected_rows,ulonglong last_insert_id,const char * const message)291 static void sql_handle_ok(void * ctx,
292                           uint server_status, uint statement_warn_count,
293                           ulonglong affected_rows, ulonglong last_insert_id,
294                           const char * const message)
295 {
296   DBUG_ENTER("sql_handle_ok");
297 
298   Callback_data *cbd = (Callback_data*)ctx;
299 
300   cbd->server_status = server_status;
301   cbd->warn_count = statement_warn_count;
302   cbd->affected_rows = affected_rows;
303   cbd->last_insert_id = last_insert_id;
304   cbd->message = message ? message : "";
305 
306   DBUG_VOID_RETURN;
307 }
308 
sql_handle_error(void * ctx,uint sql_errno,const char * const err_msg,const char * const sqlstate)309 static void sql_handle_error(void * ctx, uint sql_errno, const char * const err_msg,
310 	                           const char * const sqlstate)
311 {
312   DBUG_ENTER("sql_handle_error");
313   Callback_data *cbd = (Callback_data*)ctx;
314   WRITE_VAL("ERROR %i %s\n", sql_errno, err_msg);
315   cbd->error_called = true;
316   cbd->err = sql_errno;
317   cbd->errmsg = err_msg ? err_msg : "";
318   cbd->sqlstate = sqlstate ? sqlstate : "";
319   DBUG_VOID_RETURN;
320 }
321 
sql_shutdown(void * ctx,int shutdown_server)322 static void sql_shutdown(void *ctx, int shutdown_server)
323 {
324   DBUG_ENTER("sql_shutdown");
325   Callback_data *cbd = (Callback_data*)ctx;
326 
327   cbd->shutdown = shutdown_server;
328   cbd->shutdown_called = true;
329   DBUG_VOID_RETURN;
330 }
331 
332 const struct st_command_service_cbs sql_cbs= {
333   sql_start_result_metadata,
334   sql_field_metadata,
335   sql_end_result_metadata,
336   sql_start_row,
337   sql_end_row,
338   sql_abort_row,
339   sql_get_client_capabilities,
340   sql_get_null,
341   sql_get_integer,
342   sql_get_longlong,
343   sql_get_decimal,
344   sql_get_double,
345   sql_get_date,
346   sql_get_time,
347   sql_get_datetime,
348   sql_get_string,
349   sql_handle_ok,
350   sql_handle_error,
351   sql_shutdown,
352 };
353 
354 /****************************************************************************************/
355 
test_com_query(void * p)356 static void test_com_query(void *p)
357 {
358   DBUG_ENTER("test_com_query");
359 
360   /* Session declarations */
361   MYSQL_SESSION st_session;
362   void *plugin_ctx=NULL;
363   bool session_ret= false;
364   my_bool fail= false;
365   COM_DATA cmd;
366   Callback_data cbd;
367 
368   WRITE_STR("COM_QUERY");
369 
370   /* Open session 1: Must pass */
371   st_session= srv_session_open(NULL,plugin_ctx);
372   if (!st_session) {
373     my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_open failed.");
374   }
375   else
376     switch_user(st_session, user_privileged);
377 
378   WRITE_STR("-----------------------------------------------------------------\n");
379   memset(&sql_str_value, 0, 64 * 64 * 256 * sizeof(char));
380   memset(&sql_str_len, 0, 64 * 64 * sizeof(size_t));
381   cmd.com_query.query= "SELECT id,info FROM information_schema.processlist";
382   cmd.com_query.length= strlen(cmd.com_query.query);
383   WRITE_VAL("%s\n", cmd.com_query.query);
384   fail= command_service_run_command(st_session,
385                                     COM_QUERY,
386                                     &cmd,
387                                     &my_charset_utf8_general_ci,
388                                     &sql_cbs,
389                                     CS_TEXT_REPRESENTATION,
390                                     &cbd);
391   if (fail)
392     my_plugin_log_message(&p, MY_ERROR_LEVEL, "sql_simple ret code: %d\n", fail);
393   else
394   {
395     /* get values */
396     WRITE_STR("-----------------------------------------------------------------\n");
397     WRITE_VAL("%s\t\%s\n",sql_field[0][0].col_name,
398                 sql_field[0][1].col_name);
399     for (uint row= 0; row < sql_num_rows; row++)
400     {
401       for (uint col=0; col < sql_num_cols; col++)
402       {
403         WRITE_VAL("%s\n",sql_str_value[col][row]);
404       }
405     }
406     /* start metadata */
407     WRITE_VAL("num_cols: %d\n", sql_num_cols);
408     /* end metadata */
409     if (cbd.err)
410     {
411       WRITE_VAL("error: %d\n", cbd.err);
412       WRITE_VAL("error msg: %s\n", cbd.errmsg.c_str());
413     }
414     else
415     {
416       WRITE_VAL("server status: %d\n", cbd.server_status);
417       WRITE_VAL("warn count: %d\n", cbd.warn_count);
418       //           WRITE_VAL("messsage: %s\n",msg);
419     }
420   }
421 
422   /* 2. statement */
423   WRITE_STR("-----------------------------------------------------------------\n");
424   memset(&sql_str_value, 0, 64 * 64 * 256 * sizeof(char));
425   memset(&sql_str_len, 0, 64 * 64 * sizeof(size_t));
426   cmd.com_query.query= "SELECT * FROM information_schema.global_variables WHERE variable_name LIKE 'INNODB_READ_IO_THREADS'";
427   cmd.com_query.length= strlen(cmd.com_query.query);
428   WRITE_VAL("%s\n", cmd.com_query.query);
429   cbd.reset();
430   fail= command_service_run_command(st_session,
431                                     COM_QUERY,
432                                     &cmd,
433                                     &my_charset_utf8_general_ci,
434                                     &sql_cbs, CS_TEXT_REPRESENTATION, &cbd);
435   if (fail)
436     my_plugin_log_message(&p, MY_ERROR_LEVEL, "sql_simple ret code: %d\n", fail);
437   else
438   {
439     /* get values */
440     WRITE_STR("-----------------------------------------------------------------\n");
441     WRITE_VAL("%s\t\%s\n", sql_field[0][0].col_name, sql_field[0][1].col_name);
442     for (uint row=0; row < sql_num_rows; row++)
443     {
444       for (uint col=0; col < sql_num_cols; col+=2)
445       {
446         WRITE_VAL("%s\t\%s\n",sql_str_value[col][row], sql_str_value[col + 1][row]);
447       }
448     }
449   }
450   /* start metadata */
451   WRITE_VAL("num_cols: %d\n",sql_num_cols);
452   /* end metadata */
453   if (cbd.err)
454   {
455     WRITE_VAL("error: %d\n",cbd.err);
456     WRITE_VAL("error msg: %s\n",cbd.errmsg.c_str());
457   }
458   else
459   {
460     WRITE_VAL("server status: %d\n",cbd.server_status);
461     WRITE_VAL("warn count: %d\n",cbd.warn_count);
462   }
463 
464   // 3. statement must fail
465   cbd.reset();
466   cmd.com_query.query = "garbage";
467   cmd.com_query.length = strlen(cmd.com_query.query);
468 
469   ENSURE_API_OK(command_service_run_command(st_session,COM_QUERY,&cmd,&my_charset_utf8_general_ci,
470                                             &sql_cbs, CS_TEXT_REPRESENTATION, &cbd));
471 
472   WRITE_VAL("error after bad SQL: %i: %s\n", cbd.err, cbd.errmsg.c_str());
473 
474   /* close session 1: Must pass */
475   WRITE_STR("srv_session_close.\n");
476   session_ret= srv_session_close(st_session);
477   if (session_ret)
478     my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_close failed.");
479 
480   DBUG_VOID_RETURN;
481 }
482 
483 
test_com_init_db(void * p)484 static int test_com_init_db(void *p)
485 {
486   DBUG_ENTER("test_com_init_db");
487 
488   MYSQL_SESSION st_session;
489 
490   ENSURE_API_NOT_NULL(st_session = srv_session_open(NULL, p));
491 
492   if (st_session)
493      switch_user(st_session, user_privileged);
494   COM_DATA cmd;
495 
496   LEX_CSTRING db_name= srv_session_info_get_current_db(st_session);
497   WRITE_VAL("current_db before init_db : %s\n", db_name.str);
498 
499   cmd.com_init_db.db_name = "mysql";
500   cmd.com_init_db.length = strlen("mysql");
501   Callback_data cbd;
502   ENSURE_API_OK(command_service_run_command(st_session, COM_INIT_DB, &cmd,
503                                             &my_charset_utf8_general_ci,
504                                             &sql_cbs, CS_TEXT_REPRESENTATION,
505                                             &cbd));
506 
507   db_name= srv_session_info_get_current_db(st_session);
508   WRITE_VAL("current_db after init_db  : %s\n", db_name.str);
509 
510   ENSURE_API_OK(srv_session_close(st_session));
511 
512   DBUG_RETURN(0);
513 }
514 
515 
516 /*
517 static int test_com_list_fields(void *p)
518 {
519   DBUG_ENTER("test_com_list_fields");
520 
521   MYSQL_SESSION st_session;
522 
523   ENSURE_API_NOT_NULL(st_session = srv_session_open(NULL, p));
524 
525   COM_DATA cmd;
526 
527   cmd.com_init_db.db_name = "mysql";
528   cmd.com_init_db.length = strlen("mysql");
529   ENSURE_API_OK(command_service_run_command(st_session, COM_INIT_DB, &cmd, &my_charset_utf8_general_ci,
530                                             &sql_cbs, CS_TEXT_REPRESENTATION, p));
531 
532   WRITE_VAL("switched default db to: %s\n", srv_session_info_get_current_db(st_session));
533 
534 
535   WRITE_STR("field_list\n");
536   cmd.com_field_list.table_name = (unsigned char*)"user";
537   cmd.com_field_list.table_name_length = strlen((const char*)cmd.com_field_list.table_name);
538   cmd.com_field_list.query = (unsigned char*)"%";
539   cmd.com_field_list.query_length = strlen((const char*)cmd.com_field_list.query);
540   ENSURE_API_OK(command_service_run_command(st_session, COM_FIELD_LIST, &cmd, &my_charset_utf8_general_ci,
541                                             &sql_cbs, CS_TEXT_REPRESENTATION, p));
542 
543   WRITE_STR("-----------------------------------------------------------------\n");
544   for (uint row_count=0;row_count < sql_num_rows;row_count++){
545     for (uint col_count=0;col_count < sql_num_cols;col_count+=2){
546       WRITE_VAL("%s\t\%s\n",sql_str_value[col_count][row_count],
547                   sql_str_value[col_count+1][row_count]);
548     }
549   }
550 
551   ENSURE_API_OK(srv_session_close(st_session));
552 
553   DBUG_RETURN(0);
554 }
555 */
556 
557 
558 struct Test_data
559 {
560   void *p;
561   MYSQL_SESSION session;
562   native_mutex_t mutex;
563   native_cond_t cond;
564   int ready;
565 
Test_dataTest_data566   Test_data()
567   {
568     ready = 0;
569     native_cond_init(&cond);
570     native_mutex_init(&mutex, NULL);
571   }
572 
~Test_dataTest_data573   ~Test_data()
574   {
575     native_cond_destroy(&cond);
576     native_mutex_destroy(&mutex);
577   }
578 
waitTest_data579   void wait(int value)
580   {
581     native_mutex_lock(&mutex);
582     while (ready < value)
583         native_cond_wait(&cond, &mutex);
584     native_mutex_unlock(&mutex);
585   }
586 
goTest_data587   void go()
588   {
589     native_mutex_lock(&mutex);
590     ready++;
591     native_cond_signal(&cond);
592     native_mutex_unlock(&mutex);
593   }
594 };
595 
test_session_thread(Test_data * tdata)596 static void* test_session_thread(Test_data *tdata)
597 {
598   COM_DATA cmd;
599   Callback_data cbdata;
600 
601   if (srv_session_init_thread(tdata->p))
602     my_plugin_log_message(&tdata->p, MY_ERROR_LEVEL, "srv_session_init_thread failed.");
603 
604   WRITE_VAL("session is dead? %i\n", thd_killed(srv_session_info_get_thd(tdata->session)));
605 
606   cmd.com_query.query = "select sleep(10)";
607   cmd.com_query.length = strlen("select sleep(10)");
608 
609   WRITE_VAL("Executing %s\n", cmd.com_query.query);
610 
611   tdata->go();
612 
613   int r = command_service_run_command(tdata->session, COM_QUERY, &cmd, &my_charset_utf8_general_ci,
614                                       &sql_cbs, CS_TEXT_REPRESENTATION, &cbdata);
615   WRITE_VAL("Killed run_command return value: %i\n", r);
616 
617   WRITE_VAL("thread shutdown: %i (%s)\n", cbdata.shutdown, cbdata.shutdown_called ? "yes":"no");
618   WRITE_VAL("thread error: %i\n", cbdata.err);
619   WRITE_VAL("thread error msg: %s\n", cbdata.errmsg.c_str());
620 
621   WRITE_VAL("session is dead (after)? %i\n", thd_killed(srv_session_info_get_thd(tdata->session)));
622 
623   srv_session_detach(tdata->session);
624 
625   srv_session_deinit_thread();
626 
627   return NULL;
628 }
629 
630 
session_error_cb(void * ctx,unsigned int sql_errno,const char * err_msg)631 static void session_error_cb(void *ctx, unsigned int sql_errno, const char *err_msg)
632 {
633   WRITE_STR("default error handler called\n");
634   WRITE_VAL("sql_errno = %i\n", sql_errno);
635   WRITE_VAL("errmsg = %s\n", err_msg);
636 }
637 
638 
test_query_kill(void * p)639 static int test_query_kill(void *p)
640 {
641   DBUG_ENTER("test_query_kill");
642 
643   MYSQL_SESSION st_session;
644 
645   WRITE_STR("test_query_kill\n");
646 
647   ENSURE_API_NOT_NULL(st_session = srv_session_open(NULL, p));
648 
649   switch_user(st_session, user_privileged);
650   MYSQL_SESSION st_session_victim;
651   ENSURE_API_NOT_NULL(st_session_victim = srv_session_open(session_error_cb, p));
652 
653   Test_data tdata;
654 
655   tdata.p= p;
656   tdata.session = st_session_victim;
657 
658   my_thread_handle thread_handle;
659   {
660     my_thread_attr_t attr;
661 
662     my_thread_attr_init(&attr);
663     (void) my_thread_attr_setdetachstate(&attr, MY_THREAD_CREATE_JOINABLE);
664 
665     if (my_thread_create(&thread_handle, &attr, (void *(*)(void *))test_session_thread, &tdata) != 0)
666     {
667       WRITE_STR("Could not create test services thread!\n");
668       exit(1);
669     }
670   }
671 
672   // wait for thread to be ready
673   tdata.wait(1);
674 
675   COM_DATA cmd;
676   Callback_data cbd;
677 
678   sleep(1);
679   char buffer[200];
680   my_snprintf(buffer, sizeof(buffer), "kill query %i", srv_session_info_get_session_id(st_session_victim));
681   WRITE_STR("run KILL QUERY\n");
682   cmd.com_query.query = buffer;
683   cmd.com_query.length = strlen(buffer);
684   ENSURE_API_OK(command_service_run_command(st_session, COM_QUERY, &cmd, &my_charset_utf8_general_ci,
685                                            &sql_cbs, CS_TEXT_REPRESENTATION, &cbd));
686 
687   void *ret;
688   my_thread_join(&thread_handle, &ret);
689   WRITE_STR("OK\n");
690 
691   ENSURE_API_OK(srv_session_close(st_session));
692   ENSURE_API_OK(srv_session_close(st_session_victim));
693 
694   DBUG_RETURN(0);
695 }
696 
697 
test_com_process_kill(void * p)698 static int test_com_process_kill(void *p)
699 {
700   DBUG_ENTER("test_com_process_kill");
701 
702   MYSQL_SESSION st_session;
703   Callback_data cbd;
704 
705   WRITE_STR("COM_KILL\n");
706 
707   ENSURE_API_NOT_NULL(st_session = srv_session_open(NULL, p));
708 
709   switch_user(st_session, user_privileged);
710   MYSQL_SESSION st_session_victim;
711   ENSURE_API_NOT_NULL(st_session_victim = srv_session_open(session_error_cb, p));
712 
713   WRITE_VAL("session is dead? %i\n", thd_killed(srv_session_info_get_thd(st_session_victim)));
714 
715   COM_DATA cmd;
716 
717   cmd.com_kill.id = srv_session_info_get_session_id(st_session_victim);
718   ENSURE_API_OK(command_service_run_command(st_session, COM_PROCESS_KILL, &cmd, &my_charset_utf8_general_ci,
719                                             &sql_cbs, CS_TEXT_REPRESENTATION, &cbd));
720 
721   WRITE_VAL("session is dead now? %i\n", thd_killed(srv_session_info_get_thd(st_session_victim)));
722 
723   ENSURE_API_OK(srv_session_close(st_session));
724   ENSURE_API_OK(srv_session_close(st_session_victim));
725 
726   DBUG_RETURN(0);
727 }
728 
729 
test_priv(void * p)730 static int test_priv(void *p)
731 {
732   DBUG_ENTER("test_priv");
733 
734   MYSQL_SESSION root_session;
735   Callback_data cbd;
736   COM_DATA cmd;
737 
738   WRITE_STR("COM_QUERY with priv\n");
739 
740   ENSURE_API_NOT_NULL(root_session = srv_session_open(NULL, p));
741 
742   switch_user(root_session, user_privileged);
743 
744   cmd.com_query.query = "create user ordinary@localhost";
745   cmd.com_query.length = strlen(cmd.com_query.query);
746   ENSURE_API_OK(command_service_run_command(root_session, COM_QUERY, &cmd, &my_charset_utf8_general_ci,
747                                             &sql_cbs, CS_TEXT_REPRESENTATION, &cbd));
748   WRITE_VAL("create user as root: %i %s\n", cbd.err, cbd.errmsg.c_str());
749 
750   WRITE_STR("now try as ordinary user\n");
751   {
752     MYSQL_SESSION ordinary_session;
753     ENSURE_API_NOT_NULL(ordinary_session = srv_session_open(NULL, p));
754     switch_user(ordinary_session, user_ordinary);
755 
756     cbd.reset();
757     cmd.com_query.query = "create user bogus@localhost";
758     cmd.com_query.length = strlen(cmd.com_query.query);
759     ENSURE_API_OK(command_service_run_command(ordinary_session, COM_QUERY, &cmd, &my_charset_utf8_general_ci,
760                                             &sql_cbs, CS_TEXT_REPRESENTATION, &cbd));
761 
762     WRITE_VAL("create user supposed to fail: %i %s\n", cbd.err, cbd.errmsg.c_str());
763 
764     ENSURE_API_OK(srv_session_close(ordinary_session));
765   }
766 
767   cbd.reset();
768   cmd.com_query.query = "drop user ordinary@localhost";
769   cmd.com_query.length = strlen(cmd.com_query.query);
770   ENSURE_API_OK(command_service_run_command(root_session, COM_QUERY, &cmd, &my_charset_utf8_general_ci,
771                                             &sql_cbs, CS_TEXT_REPRESENTATION, &cbd));
772   WRITE_VAL("drop user as root: %i %s\n", cbd.err, cbd.errmsg.c_str());
773 
774 
775   ENSURE_API_OK(srv_session_close(root_session));
776 
777   DBUG_RETURN(0);
778 }
779 
780 
test_sql(void * p)781 static void test_sql(void *p)
782 {
783   DBUG_ENTER("test_sql");
784   my_plugin_log_message(&p, MY_INFORMATION_LEVEL, "Installation.");
785 
786   WRITE_SEP();
787   test_com_query(p);
788   WRITE_SEP();
789   test_com_init_db(p);
790   WRITE_SEP();
791 //  test_com_list_fields(p);
792 //  WRITE_SEP();
793   test_com_process_kill(p);
794   WRITE_SEP();
795   test_query_kill(p);
796   WRITE_SEP();
797   test_priv(p);
798 
799   DBUG_VOID_RETURN;
800 }
801 
create_log_file(const char * log_name)802 static void create_log_file(const char * log_name)
803 {
804   char filename[FN_REFLEN];
805 
806   fn_format(filename, log_name, "", ".log",
807             MY_REPLACE_EXT | MY_UNPACK_FILENAME);
808   unlink(filename);
809   outfile= my_open(filename, O_CREAT|O_RDWR, MYF(0));
810 }
811 
812 static const char *log_filename= "test_sql_cmds_1";
813 
814 
test_sql_service_plugin_init(void * p)815 static int test_sql_service_plugin_init(void *p)
816 {
817   DBUG_ENTER("test_sql_service_plugin_init");
818   my_plugin_log_message(&p, MY_INFORMATION_LEVEL, "Installation.");
819 
820   create_log_file(log_filename);
821 
822   /* Test of service: sql */
823   test_sql(p);
824 
825   my_close(outfile, MYF(0));
826   DBUG_RETURN(0);
827 }
828 
829 
test_sql_service_plugin_deinit(void * p)830 static int test_sql_service_plugin_deinit(void *p)
831 {
832   DBUG_ENTER("test_sql_service_plugin_deinit");
833   my_plugin_log_message(&p, MY_INFORMATION_LEVEL, "Uninstallation.");
834   DBUG_RETURN(0);
835 }
836 
837 struct st_mysql_daemon test_sql_service_plugin=
838 { MYSQL_DAEMON_INTERFACE_VERSION  };
839 
840 /*
841   Plugin library descriptor
842 */
843 
mysql_declare_plugin(test_daemon)844 mysql_declare_plugin(test_daemon)
845 {
846   MYSQL_DAEMON_PLUGIN,
847   &test_sql_service_plugin,
848   "test_sql_cmds_1",
849   "Horst Hunger, Andrey Hristov",
850   "Test sql service commands",
851   PLUGIN_LICENSE_GPL,
852   test_sql_service_plugin_init, /* Plugin Init */
853   test_sql_service_plugin_deinit, /* Plugin Deinit */
854   0x0100 /* 1.0 */,
855   NULL,                       /* status variables                */
856   NULL,                       /* system variables                */
857   NULL,                       /* config options                  */
858   0,                          /* flags                           */
859 }
860 mysql_declare_plugin_end;
861