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_string.h" /* STRING_PSI_MEMORY_KEY */
30 
31 static const char *log_filename= "test_sql_stored_procedures_functions";
32 
33 struct st_test_statement
34 {
35   const char *db;
36   bool generates_result_set;
37   const char *query;
38 };
39 
40 static struct st_test_statement test_query_plan[]=
41 {
42   /* DB   RESULT   QUERY */
43   { NULL , true , "SELECT 'first complex command' as a"},
44   // Procedure
45   {"test", false, "CALL test.proc()" },
46   {"test", false, "CALL test.sp_no_results()" },
47   {"test", false, "CALL test.sp_one_result()" },
48   {"test", false, "CALL test.sp_two_results()" },
49   {"test", false, "CALL test.sp_no_results_out(@a)" },
50   {"test", false, "CALL test.sp_one_result_out(@a)" },
51   {"test", false, "CALL test.sp_two_results_out(@a)" },
52   {"test", true , "CALL test.sp_with_error()" },
53   {"test", true , "CALL test.sp_result_error()" },
54   { NULL , true , "SELECT SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'PROCEDURE' AND ROUTINE_NAME = 'proc'" },
55   // Function
56   {"test", true , "SELECT avg_func(10, 20)" },
57   { NULL , true , "SELECT SPECIFIC_NAME FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_TYPE = 'FUNCTION' AND ROUTINE_NAME = 'avg_func'" }
58 };
59 
60 #define STRING_BUFFER_SIZE 512
61 
62 #define WRITE_STR(format) \
63   { \
64     const size_t blen= my_snprintf(buffer, sizeof(buffer), (format)); \
65     my_write(outfile, (uchar*) buffer, blen, MYF(0)); \
66     /*pctx->log.append(buffer, blen); */ \
67   }
68 
69 
70 #define WRITE_VAL(format,value) \
71   { \
72     const size_t blen= my_snprintf(buffer, sizeof(buffer), (format), (value)); \
73     my_write(outfile,(uchar*)buffer, blen, MYF(0)); \
74    /* pctx->log.append(buffer, blen); */ \
75   }
76 
77 #define WRITE_VAL2(format,value1, value2) \
78   { \
79     const size_t blen= my_snprintf(buffer, sizeof(buffer), (format), (value1), (value2)); \
80     my_write(outfile,(uchar*) buffer, blen, MYF(0)); \
81     /* pctx->log.append(buffer, blen); */ \
82   }
83 
84 static const char *sep = "========================================================================\n";
85 
86 #define WRITE_SEP() my_write(outfile, (uchar*)sep, strlen(sep), MYF(0))
87 
88 static const char *user_localhost = "localhost";
89 static const char *user_local = "127.0.0.1";
90 static const char *user_db= "";
91 static const char *user_privileged= "root";
92 //static const char *user_ordinary= "ordinary";
93 
switch_user(MYSQL_SESSION session,const char * user)94 static void switch_user(MYSQL_SESSION session, const char *user)
95 {
96   MYSQL_SECURITY_CONTEXT sc;
97   thd_get_security_context(srv_session_info_get_thd(session), &sc);
98   security_context_lookup(sc, user, user_localhost, user_local, user_db);
99 }
100 
101 
102 static File outfile;
103 
104 struct st_send_field_n
105 {
106   char db_name[256];
107   char table_name[256];
108   char org_table_name[256];
109   char col_name[256];
110   char org_col_name[256];
111   unsigned long length;
112   unsigned int charsetnr;
113   unsigned int flags;
114   unsigned int decimals;
115   enum_field_types type;
116 };
117 
118 #define SIZEOF_SQL_STR_VALUE 256
119 
120 struct st_plugin_ctx
121 {
122   const CHARSET_INFO *resultcs;
123   uint meta_server_status;
124   uint meta_warn_count;
125   uint current_col;
126   uint num_cols;
127   uint num_rows;
128   st_send_field_n sql_field[64];
129   char sql_str_value[64][64][SIZEOF_SQL_STR_VALUE];
130   size_t sql_str_len[64][64];
131 
132   uint server_status;
133   uint warn_count;
134   uint affected_rows;
135   uint last_insert_id;
136   char message[1024];
137 
138   uint sql_errno;
139   char err_msg[1024];
140   char sqlstate[6];
141 
142   std::string log;
st_plugin_ctxst_plugin_ctx143   st_plugin_ctx()
144   {
145     reset();
146   }
147 
resetst_plugin_ctx148   void reset()
149   {
150     resultcs= NULL;
151     meta_server_status = 0;
152     meta_warn_count = 0;
153     server_status= 0;
154     current_col= 0;
155     warn_count= 0;
156     num_cols= 0;
157     num_rows= 0;
158     memset(&sql_field, 0, 64 * sizeof(st_send_field_n));
159     memset(&sql_str_value, 0, 64 * 64 * SIZEOF_SQL_STR_VALUE * sizeof(char));
160     memset(&sql_str_len, 0, 64 * 64 * sizeof(size_t));
161 
162     server_status= 0;
163     warn_count= 0;
164     affected_rows= 0;
165     last_insert_id= 0;
166     memset(&message, 0, sizeof(message));
167 
168     sql_errno= 0;
169     memset(&err_msg, 0, sizeof(err_msg));
170     memset(&sqlstate, 0, sizeof(sqlstate));
171 
172     log.clear();
173   }
174 };
175 
176 
sql_start_result_metadata(void * ctx,uint num_cols,uint flags,const CHARSET_INFO * resultcs)177 static int sql_start_result_metadata(void *ctx, uint num_cols, uint flags,
178                                      const CHARSET_INFO *resultcs)
179 {
180   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
181   DBUG_ENTER("sql_start_result_metadata");
182   DBUG_PRINT("info",("resultcs->number: %d", resultcs->number));
183   DBUG_PRINT("info",("resultcs->csname: %s", resultcs->csname));
184   DBUG_PRINT("info",("resultcs->name: %s", resultcs->name));
185   pctx->num_cols= num_cols;
186   pctx->resultcs= resultcs;
187   pctx->current_col= 0;
188 
189   DBUG_RETURN(false);
190 }
191 
192 
sql_field_metadata(void * ctx,struct st_send_field * field,const CHARSET_INFO * charset)193 static int sql_field_metadata(void *ctx, struct st_send_field *field,
194                               const CHARSET_INFO *charset)
195 {
196   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
197   st_send_field_n *cfield= &pctx->sql_field[pctx->current_col];
198   DBUG_ENTER("sql_field_metadata");
199   DBUG_PRINT("info",("field->db_name: %s", field->db_name));
200   DBUG_PRINT("info",("field->table_name: %s", field->table_name));
201   DBUG_PRINT("info",("field->org_table_name: %s", field->org_table_name));
202   DBUG_PRINT("info",("field->col_name: %s", field->col_name));
203   DBUG_PRINT("info",("field->org_col_name: %s", field->org_col_name));
204   DBUG_PRINT("info",("field->length: %d", (int)field->length));
205   DBUG_PRINT("info",("field->charsetnr: %d", (int)field->charsetnr));
206   DBUG_PRINT("info",("field->flags: %d", (int)field->flags));
207   DBUG_PRINT("info",("field->decimals: %d", (int)field->decimals));
208   DBUG_PRINT("info",("field->type: %d", (int)field->type));
209 
210   strcpy(cfield->db_name,        (char*)field->db_name);
211   strcpy(cfield->table_name,     (char*)field->table_name);
212   strcpy(cfield->org_table_name, (char*)field->org_table_name);
213   strcpy(cfield->col_name,       (char*)field->col_name);
214   strcpy(cfield->org_col_name,   (char*)field->org_col_name);
215   cfield->length=    field->length;
216   cfield->charsetnr= field->charsetnr;
217   cfield->flags=     field->flags;
218   cfield->decimals=  field->decimals;
219   cfield->type=      field->type;
220 
221   pctx->current_col++;
222   DBUG_RETURN(false);
223 }
224 
225 
sql_end_result_metadata(void * ctx,uint server_status,uint warn_count)226 static int sql_end_result_metadata(void *ctx, uint server_status,
227                                    uint warn_count)
228 {
229   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
230   DBUG_ENTER("sql_end_result_metadata");
231   pctx->meta_server_status= server_status;
232   pctx->meta_warn_count= warn_count;
233   pctx->num_rows= 0;
234   DBUG_RETURN(false);
235 }
236 
237 
sql_start_row(void * ctx)238 static int sql_start_row(void *ctx)
239 {
240   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;//  WRITE_STR("sql_start_row\n");
241   DBUG_ENTER("sql_start_row");
242   pctx->current_col= 0;
243   DBUG_RETURN(false);
244 }
245 
246 
sql_end_row(void * ctx)247 static int sql_end_row(void *ctx)
248 {
249   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
250   DBUG_ENTER("sql_end_row");
251   pctx->num_rows++;
252   DBUG_RETURN(false);
253 }
254 
255 
sql_abort_row(void * ctx)256 static void sql_abort_row(void *ctx)
257 {
258   DBUG_ENTER("sql_abort_row");
259   DBUG_VOID_RETURN;
260 }
261 
262 
sql_get_client_capabilities(void * ctx)263 static ulong sql_get_client_capabilities(void *ctx)
264 {
265   DBUG_ENTER("sql_get_client_capabilities");
266   DBUG_RETURN(CLIENT_MULTI_RESULTS);
267 }
268 
269 
sql_get_null(void * ctx)270 static int sql_get_null(void *ctx)
271 {
272   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
273   DBUG_ENTER("sql_get_null");
274   uint row= pctx->num_rows;
275   uint col= pctx->current_col;
276   pctx->current_col++;
277 
278   strcpy(pctx->sql_str_value[row][col], "[NULL]");
279   pctx->sql_str_len[row][col]=  sizeof("[NULL]")-1;
280 
281   DBUG_RETURN(false);
282 }
283 
284 
sql_get_integer(void * ctx,longlong value)285 static int sql_get_integer(void * ctx, longlong value)
286 {
287   char buffer[STRING_BUFFER_SIZE];
288   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
289   DBUG_ENTER("sql_get_integer");
290   uint row= pctx->num_rows;
291   uint col= pctx->current_col;
292   pctx->current_col++;
293 
294   size_t len= my_snprintf(buffer, sizeof(buffer), "%lld", value);
295 
296   strncpy(pctx->sql_str_value[row][col], buffer, len);
297   pctx->sql_str_len[row][col]= len;
298 
299   DBUG_RETURN(false);
300 }
301 
302 
sql_get_longlong(void * ctx,longlong value,uint is_unsigned)303 static int sql_get_longlong(void * ctx, longlong value, uint is_unsigned)
304 {
305   char buffer[STRING_BUFFER_SIZE];
306   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
307   DBUG_ENTER("sql_get_longlong");
308   uint row= pctx->num_rows;
309   uint col= pctx->current_col;
310   pctx->current_col++;
311 
312   size_t len= my_snprintf(buffer, sizeof(buffer),
313                           is_unsigned? "%llu":"%lld", value);
314 
315   strncpy(pctx->sql_str_value[row][col], buffer, len);
316   pctx->sql_str_len[row][col]= len;
317 
318   DBUG_RETURN(false);
319 }
320 
321 
test_decimal_as_string(char * buff,const decimal_t * val,int * length)322 static const char *test_decimal_as_string(char *buff, const decimal_t *val, int *length)
323 {
324   if (!val)
325     return "NULL";
326   (void)decimal2string(val, buff, length, 0,0,0);
327   return buff;
328 }
329 
330 
sql_get_decimal(void * ctx,const decimal_t * value)331 static int sql_get_decimal(void * ctx, const decimal_t * value)
332 {
333   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
334   DBUG_ENTER("sql_get_decimal");
335   uint row= pctx->num_rows;
336   uint col= pctx->current_col;
337   pctx->current_col++;
338 
339   int len= SIZEOF_SQL_STR_VALUE;
340   test_decimal_as_string(pctx->sql_str_value[row][col], value, &len);
341   pctx->sql_str_len[row][col]= len;
342 
343   DBUG_RETURN(false);
344 }
345 
346 
sql_get_double(void * ctx,double value,uint32 decimals)347 static int sql_get_double(void * ctx, double value, uint32 decimals)
348 {
349   char buffer[STRING_BUFFER_SIZE];
350   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
351   DBUG_ENTER("sql_get_double");
352   uint row= pctx->num_rows;
353   uint col= pctx->current_col;
354   pctx->current_col++;
355 
356   size_t len= my_snprintf(buffer, sizeof(buffer), "%3.7g", value);
357 
358   strncpy(pctx->sql_str_value[row][col], buffer, len);
359   pctx->sql_str_len[row][col]= len;
360 
361   DBUG_RETURN(false);
362 }
363 
364 
sql_get_date(void * ctx,const MYSQL_TIME * value)365 static int sql_get_date(void * ctx, const MYSQL_TIME * value)
366 {
367   char buffer[STRING_BUFFER_SIZE];
368   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
369   DBUG_ENTER("sql_get_date");
370   uint row= pctx->num_rows;
371   uint col= pctx->current_col;
372   pctx->current_col++;
373 
374   size_t len= my_snprintf(buffer, sizeof(buffer),
375                           "%s%4d-%02d-%02d",
376                           value->neg? "-":"",
377                           value->year, value->month, value->day);
378 
379   strncpy(pctx->sql_str_value[row][col], buffer, len);
380   pctx->sql_str_len[row][col]= len;
381 
382   DBUG_RETURN(false);
383 }
384 
385 
sql_get_time(void * ctx,const MYSQL_TIME * value,uint decimals)386 static int sql_get_time(void * ctx, const MYSQL_TIME * value, uint decimals)
387 {
388   char buffer[STRING_BUFFER_SIZE];
389   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
390   DBUG_ENTER("sql_get_time");
391   uint row= pctx->num_rows;
392   uint col= pctx->current_col;
393   pctx->current_col++;
394 
395   size_t len= my_snprintf(buffer, sizeof(buffer),
396                           "%s%02d:%02d:%02d",
397                           value->neg? "-":"",
398                           value->day? (value->day*24 + value->hour):value->hour,
399                           value->minute, value->second);
400 
401   strncpy(pctx->sql_str_value[row][col], buffer, len);
402   pctx->sql_str_len[row][col]= len;
403   DBUG_RETURN(false);
404 }
405 
406 
sql_get_datetime(void * ctx,const MYSQL_TIME * value,uint decimals)407 static int sql_get_datetime(void * ctx, const MYSQL_TIME * value, uint decimals)
408 {
409   char buffer[STRING_BUFFER_SIZE];
410   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
411   DBUG_ENTER("sql_get_datetime");
412   uint row= pctx->num_rows;
413   uint col= pctx->current_col;
414   pctx->current_col++;
415 
416   size_t len= my_snprintf(buffer, sizeof(buffer),
417                           "%s%4d-%02d-%02d %02d:%02d:%02d",
418                           value->neg? "-":"",
419                           value->year, value->month, value->day,
420                           value->hour, value->minute, value->second);
421 
422   strncpy(pctx->sql_str_value[row][col], buffer, len);
423   pctx->sql_str_len[row][col]= len;
424 
425 
426   DBUG_RETURN(false);
427 }
428 
429 
sql_get_string(void * ctx,const char * const value,size_t length,const CHARSET_INFO * const valuecs)430 static int sql_get_string(void * ctx, const char * const value, size_t length,
431                           const CHARSET_INFO * const valuecs)
432 {
433   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
434   DBUG_ENTER("sql_get_string");
435   uint row= pctx->num_rows;
436   uint col= pctx->current_col;
437   pctx->current_col++;
438 
439   strncpy(pctx->sql_str_value[row][col], value, length);
440   pctx->sql_str_len[row][col]= length;
441 
442   DBUG_RETURN(false);
443 }
444 
445 
sql_handle_ok(void * ctx,uint server_status,uint statement_warn_count,ulonglong affected_rows,ulonglong last_insert_id,const char * const message)446 static void sql_handle_ok(void * ctx,
447                           uint server_status, uint statement_warn_count,
448                           ulonglong affected_rows, ulonglong last_insert_id,
449                           const char * const message)
450 {
451   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
452   DBUG_ENTER("sql_handle_ok");
453   /* This could be an EOF */
454   if (!pctx->num_cols)
455     pctx->num_rows= 0;
456   pctx->server_status=  server_status;
457   pctx->warn_count=     statement_warn_count;
458   pctx->affected_rows=  affected_rows;
459   pctx->last_insert_id= last_insert_id;
460   if (message)
461     strncpy(pctx->message, message, sizeof(pctx->message));
462 
463   DBUG_VOID_RETURN;
464 }
465 
466 
sql_handle_error(void * ctx,uint sql_errno,const char * const err_msg,const char * const sqlstate)467 static void sql_handle_error(void * ctx, uint sql_errno,
468                              const char * const err_msg,
469                              const char * const sqlstate)
470 {
471   char buffer[STRING_BUFFER_SIZE];
472   struct st_plugin_ctx *pctx= (struct st_plugin_ctx*) ctx;
473   DBUG_ENTER("sql_handle_error");
474   WRITE_VAL2("[%u][%s]", sql_errno, sqlstate);
475   WRITE_VAL("[%s]\n", err_msg);
476   pctx->num_rows= 0;
477   DBUG_VOID_RETURN;
478 }
479 
480 
sql_shutdown(void * ctx,int shutdown_server)481 static void sql_shutdown(void *ctx, int shutdown_server)
482 {
483   DBUG_ENTER("sql_shutdown");
484   DBUG_VOID_RETURN;
485 }
486 
487 
488 const struct st_command_service_cbs protocol_callbacks=
489 {
490   sql_start_result_metadata,
491   sql_field_metadata,
492   sql_end_result_metadata,
493   sql_start_row,
494   sql_end_row,
495   sql_abort_row,
496   sql_get_client_capabilities,
497   sql_get_null,
498   sql_get_integer,
499   sql_get_longlong,
500   sql_get_decimal,
501   sql_get_double,
502   sql_get_date,
503   sql_get_time,
504   sql_get_datetime,
505   sql_get_string,
506   sql_handle_ok,
507   sql_handle_error,
508   sql_shutdown,
509 };
510 
511 #define WRITE_DASHED_LINE() \
512   WRITE_STR("------------------------------------------------------------------\n");
513 
fieldtype2str(enum enum_field_types type)514 static const char *fieldtype2str(enum enum_field_types type)
515 {
516   switch (type) {
517     case MYSQL_TYPE_BIT:         return "BIT";
518     case MYSQL_TYPE_BLOB:        return "BLOB";
519     case MYSQL_TYPE_DATE:        return "DATE";
520     case MYSQL_TYPE_DATETIME:    return "DATETIME";
521     case MYSQL_TYPE_NEWDECIMAL:  return "NEWDECIMAL";
522     case MYSQL_TYPE_DECIMAL:     return "DECIMAL";
523     case MYSQL_TYPE_DOUBLE:      return "DOUBLE";
524     case MYSQL_TYPE_ENUM:        return "ENUM";
525     case MYSQL_TYPE_FLOAT:       return "FLOAT";
526     case MYSQL_TYPE_GEOMETRY:    return "GEOMETRY";
527     case MYSQL_TYPE_INT24:       return "INT24";
528     case MYSQL_TYPE_LONG:        return "LONG";
529     case MYSQL_TYPE_LONGLONG:    return "LONGLONG";
530     case MYSQL_TYPE_LONG_BLOB:   return "LONG_BLOB";
531     case MYSQL_TYPE_MEDIUM_BLOB: return "MEDIUM_BLOB";
532     case MYSQL_TYPE_NEWDATE:     return "NEWDATE";
533     case MYSQL_TYPE_NULL:        return "NULL";
534     case MYSQL_TYPE_SET:         return "SET";
535     case MYSQL_TYPE_SHORT:       return "SHORT";
536     case MYSQL_TYPE_STRING:      return "STRING";
537     case MYSQL_TYPE_TIME:        return "TIME";
538     case MYSQL_TYPE_TIMESTAMP:   return "TIMESTAMP";
539     case MYSQL_TYPE_TINY:        return "TINY";
540     case MYSQL_TYPE_TINY_BLOB:   return "TINY_BLOB";
541     case MYSQL_TYPE_VARCHAR:     return "VARCHAR";
542     case MYSQL_TYPE_VAR_STRING:  return "VAR_STRING";
543     case MYSQL_TYPE_YEAR:        return "YEAR";
544     default:                     return "?-unknown-?";
545   }
546 }
547 
548 
fieldflags2str(uint f)549 static char *fieldflags2str(uint f)
550 {
551   static char buf[1024];
552   char *s=buf;
553   *s=0;
554 #define ff2s_check_flag(X)  if (f & X ## _FLAG) { s=my_stpcpy(s, # X " "); f &= ~ X ## _FLAG; }
555   ff2s_check_flag(NOT_NULL);
556   ff2s_check_flag(PRI_KEY);
557   ff2s_check_flag(UNIQUE_KEY);
558   ff2s_check_flag(MULTIPLE_KEY);
559   ff2s_check_flag(BLOB);
560   ff2s_check_flag(UNSIGNED);
561   ff2s_check_flag(ZEROFILL);
562   ff2s_check_flag(BINARY);
563   ff2s_check_flag(ENUM);
564   ff2s_check_flag(AUTO_INCREMENT);
565   ff2s_check_flag(TIMESTAMP);
566   ff2s_check_flag(SET);
567   ff2s_check_flag(NO_DEFAULT_VALUE);
568   ff2s_check_flag(NUM);
569   ff2s_check_flag(PART_KEY);
570   ff2s_check_flag(GROUP);
571   ff2s_check_flag(UNIQUE);
572   ff2s_check_flag(BINCMP);
573   ff2s_check_flag(ON_UPDATE_NOW);
574 #undef ff2s_check_flag
575   if (f)
576     sprintf(s, " unknown=0x%04x", f);
577   return buf;
578 }
579 
580 
dump_meta_field(st_send_field_n field)581 static void dump_meta_field(st_send_field_n field)
582 {
583   char buffer[STRING_BUFFER_SIZE];
584 
585   WRITE_VAL("\t\t\t[meta][field] db name: %s\n",   field.db_name);
586   WRITE_VAL("\t\t\t[meta][field] table name: %s\n",field.table_name);
587   WRITE_VAL("\t\t\t[meta][field] org table name: %s\n",field.org_table_name);
588   WRITE_VAL("\t\t\t[meta][field] col name: %s\n",   field.col_name);
589   WRITE_VAL("\t\t\t[meta][field] org col name: %s\n",field.org_col_name);
590   WRITE_VAL("\t\t\t[meta][field] length: %u\n",(uint)field.length);
591   WRITE_VAL("\t\t\t[meta][field] charsetnr: %u\n", field.charsetnr);
592 
593   WRITE_VAL("\t\t\t[meta][field] flags: %u", field.flags);
594   if (field.flags)
595     WRITE_VAL(" (%s)", fieldflags2str(field.flags));
596   WRITE_STR("\n");
597 
598   WRITE_VAL("\t\t\t[meta][field] decimals: %u\n",  field.decimals);
599 
600   WRITE_VAL2("\t\t\t[meta][field] type: %s (%u)\n",
601              fieldtype2str(field.type), field.type);
602 }
603 
604 
dump_cs_info(const CHARSET_INFO * cs)605 static void dump_cs_info(const CHARSET_INFO *cs)
606 {
607   char buffer[STRING_BUFFER_SIZE];
608   if (!cs)
609   {
610     WRITE_STR("\t\t[meta] no charset\n");
611     return;
612   }
613 
614   WRITE_VAL("\t\t[meta][charset result] number: %d\n",   cs->number);
615   WRITE_VAL("\t\t[meta][charset result] name: %s\n",     cs->csname);
616   WRITE_VAL("\t\t[meta][charset result] collation: %s\n",cs->name);
617   WRITE_VAL("\t\t[meta][charset result] sort order: %s\n", cs->sort_order);
618 }
619 
620 
dump_decoded_server_status(const char * prefix,uint server_status)621 static void dump_decoded_server_status(const char *prefix, uint server_status)
622 {
623   char buffer[STRING_BUFFER_SIZE];
624   WRITE_STR(prefix);
625   WRITE_VAL("%u\n", server_status);
626   WRITE_STR(prefix);
627   for (int i= 0; i < 30; i++)
628   {
629     uint flag= 1 << i;
630     if (server_status & flag)
631     {
632 #define FLAG_DELIMITER " "
633       switch (flag) {
634       case SERVER_STATUS_IN_TRANS:          WRITE_STR("IN_TRANS" FLAG_DELIMITER);break;
635       case SERVER_STATUS_AUTOCOMMIT:        WRITE_STR("AUTOCOMMIT" FLAG_DELIMITER);break;
636       case SERVER_MORE_RESULTS_EXISTS:      WRITE_STR("MORE_RESULTS_EXISTS" FLAG_DELIMITER);break;
637       case SERVER_QUERY_NO_GOOD_INDEX_USED: WRITE_STR("QUERY_NO_GOOD_INDEX_USED" FLAG_DELIMITER);break;
638       case SERVER_QUERY_NO_INDEX_USED:      WRITE_STR("QUERY_NO_INDEX_USED" FLAG_DELIMITER);break;
639       case SERVER_STATUS_CURSOR_EXISTS:     WRITE_STR("CURSOR_EXISTS" FLAG_DELIMITER);break;
640       case SERVER_STATUS_LAST_ROW_SENT:     WRITE_STR("LAST_ROW_SENT" FLAG_DELIMITER);break;
641       case SERVER_STATUS_DB_DROPPED:        WRITE_STR("DB_DROPPED" FLAG_DELIMITER);break;
642       case SERVER_STATUS_NO_BACKSLASH_ESCAPES:WRITE_STR("NO_BACKSLASH_ESCAPES" FLAG_DELIMITER);break;
643       case SERVER_STATUS_METADATA_CHANGED:  WRITE_STR("METADATA_CHANGED" FLAG_DELIMITER);break;
644       case SERVER_QUERY_WAS_SLOW:           WRITE_STR("QUERY_WAS_SLOW" FLAG_DELIMITER);break;
645       case SERVER_PS_OUT_PARAMS:            WRITE_STR("PS_OUT_PARAMS" FLAG_DELIMITER);break;
646       case SERVER_STATUS_IN_TRANS_READONLY: WRITE_STR("IN_TRANS_READONLY" FLAG_DELIMITER);break;
647       case SERVER_SESSION_STATE_CHANGED:    WRITE_STR("STATE_CHANGED" FLAG_DELIMITER);break;
648       default:
649         // Add a new flag defined in mysql_com.h above to fix this
650         WRITE_VAL("UNKNOWN_%u\n", flag);
651       }
652 #undef FLAG_DELIMITER
653     }
654   }
655   WRITE_STR("\n");
656 }
657 
658 
dump_meta_info(struct st_plugin_ctx * ctx)659 static void dump_meta_info(struct st_plugin_ctx *ctx)
660 {
661   char buffer[STRING_BUFFER_SIZE];
662 
663   WRITE_VAL("\t\t[meta] rows: %u\n", ctx->num_rows);
664   WRITE_VAL("\t\t[meta] cols: %u\n", ctx->num_cols);
665   dump_decoded_server_status("\t\t[meta] server status: ", ctx->meta_server_status);
666   WRITE_VAL("\t\t[meta] warning count: %u\n", ctx->meta_warn_count);
667   WRITE_STR("\n");
668 
669   if (!ctx->num_cols)
670   {
671     WRITE_STR("\t\t[meta] no columns\n");
672   }
673   else
674     for (uint col= 0; col < ctx->num_cols; col++)
675     {
676       dump_meta_field(ctx->sql_field[col]);
677       WRITE_STR("\n");
678     }
679 
680   WRITE_STR("\n");
681 
682   dump_cs_info(ctx->resultcs);
683 }
684 
685 
dump_result_set(struct st_plugin_ctx * ctx)686 static void dump_result_set(struct st_plugin_ctx *ctx)
687 {
688   char buffer[STRING_BUFFER_SIZE];
689 
690   if (!ctx->num_rows)
691     WRITE_STR("\t\t[data] no rows\n");
692 
693   for (uint row=0; row < ctx->num_rows; row++)
694   {
695     if (row)
696       WRITE_STR("\n");
697     for (uint col=0; col < ctx->num_cols; col++)
698     {
699       WRITE_VAL2("\t\t[data][%s.%s]", ctx->sql_field[col].table_name,
700                                        ctx->sql_field[col].col_name);
701       WRITE_VAL2("[%3u][%s]\n", (uint) ctx->sql_str_len[row][col],
702                                        ctx->sql_str_value[row][col]);
703     }
704   }
705 }
706 
707 
dump_closing_ok(struct st_plugin_ctx * ctx)708 static void dump_closing_ok(struct st_plugin_ctx *ctx)
709 {
710   char buffer[STRING_BUFFER_SIZE];
711 
712   dump_decoded_server_status("\t\t[end] server status: ", ctx->server_status);
713   WRITE_VAL("\t\t[end] warning count: %u\n", ctx->warn_count);
714   WRITE_VAL("\t\t[end] affected rows: %u\n", ctx->affected_rows);
715   WRITE_VAL("\t\t[end] insert id    : %u\n", ctx->last_insert_id);
716   WRITE_VAL("\t\t[end] message: %s\n", ctx->message);
717 }
718 
719 
set_query_in_com_data(const char * query,union COM_DATA * cmd)720 static void set_query_in_com_data(const char *query, union COM_DATA *cmd)
721 {
722   char buffer[STRING_BUFFER_SIZE];
723 
724   cmd->com_query.query= (char *) query;
725   cmd->com_query.length= strlen(query);
726   WRITE_VAL2("EXECUTING:[%u][%s]\n", cmd->com_query.length, query);
727 }
728 
729 
run_statement(MYSQL_SESSION session,const char * query,struct st_plugin_ctx * ctx,bool generates_result_set,void * p)730 static void run_statement(MYSQL_SESSION session, const char *query,
731                           struct st_plugin_ctx *ctx,
732                           bool generates_result_set, void *p)
733 {
734   char buffer[STRING_BUFFER_SIZE];
735   COM_DATA cmd;
736 
737   WRITE_DASHED_LINE();
738   set_query_in_com_data(query, &cmd);
739 
740   enum cs_text_or_binary txt_or_bin= CS_TEXT_REPRESENTATION;
741 
742   WRITE_STR("[CS_TEXT_REPRESENTATION]\n");
743 again:
744   ctx->reset();
745   int fail= command_service_run_command(session, COM_QUERY, &cmd,
746                                         &my_charset_utf8_general_ci,
747                                         &protocol_callbacks, txt_or_bin, ctx);
748   if (fail)
749   {
750     my_plugin_log_message(&p, MY_ERROR_LEVEL, "run_statement code: %d\n", fail);
751     return;
752   }
753 
754   dump_meta_info(ctx);    WRITE_STR("\n");
755 
756   dump_result_set(ctx);   WRITE_STR("\n");
757 
758   dump_closing_ok(ctx);
759 
760   if (generates_result_set && txt_or_bin == CS_TEXT_REPRESENTATION)
761   {
762     txt_or_bin= CS_BINARY_REPRESENTATION;
763     WRITE_STR("[CS_BINARY_REPRESENTATION]\n");
764     goto again;
765   }
766 }
767 
768 
change_current_db(MYSQL_SESSION session,const char * db,struct st_plugin_ctx * ctx,void * p)769 void static change_current_db(MYSQL_SESSION session, const char * db,
770                               struct st_plugin_ctx *ctx, void *p)
771 {
772   char buffer[STRING_BUFFER_SIZE];
773   COM_DATA cmd;
774   cmd.com_init_db.db_name= db;
775   cmd.com_init_db.length= strlen(db);
776 
777   WRITE_DASHED_LINE();
778   WRITE_VAL("EXECUTING:[COM_INIT_DB][%s]\n",db);
779   ctx->reset();
780   int fail= command_service_run_command(session, COM_INIT_DB, &cmd,
781                                         &my_charset_utf8_general_ci,
782                                         &protocol_callbacks,
783                                         CS_TEXT_REPRESENTATION,
784                                         ctx);
785   if (fail)
786     my_plugin_log_message(&p, MY_ERROR_LEVEL, "change db code: %d\n", fail);
787 }
788 
789 
test_selects(MYSQL_SESSION session,void * p)790 void test_selects(MYSQL_SESSION session, void *p)
791 {
792   DBUG_ENTER("test_selects");
793   char buffer[STRING_BUFFER_SIZE];
794 
795   struct st_plugin_ctx *plugin_ctx= new st_plugin_ctx();
796 
797   const char *last_db= NULL;
798   size_t stmt_count= sizeof(test_query_plan) / sizeof(test_query_plan[0]);
799   for (size_t i= 0; i < stmt_count; i++)
800   {
801     /* Change current DB if needed */
802     if (last_db != test_query_plan[i].db)
803     {
804       last_db= test_query_plan[i].db;
805 
806       change_current_db(session, last_db? last_db:"", plugin_ctx, p);
807     }
808     run_statement(session, test_query_plan[i].query, plugin_ctx,
809                   test_query_plan[i].generates_result_set, p);
810 
811     last_db= NULL;
812   }
813 
814   WRITE_DASHED_LINE();
815 
816   delete plugin_ctx;
817   DBUG_VOID_RETURN;
818 }
819 
820 
test_sql(void * p)821 static void test_sql(void *p)
822 {
823   char buffer[STRING_BUFFER_SIZE];
824   DBUG_ENTER("test_sql");
825 
826   WRITE_STR("[srv_session_open]\n");
827   MYSQL_SESSION session= srv_session_open(NULL, NULL);
828   if (!session)
829   {
830     my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_open failed.");
831     DBUG_VOID_RETURN;
832   }
833 
834   switch_user(session, user_privileged);
835 
836   test_selects(session, p);
837 
838   /* close session 1: Must pass */
839   WRITE_STR("[srv_session_close]\n");
840   if (srv_session_close(session))
841     my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_close failed.");
842 
843   DBUG_VOID_RETURN;
844 }
845 
846 
847 struct test_thread_context
848 {
849   my_thread_handle thread;
850   void *p;
851   bool thread_finished;
852   void (*test_function)(void *);
853 };
854 
855 
test_sql_threaded_wrapper(void * param)856 static void* test_sql_threaded_wrapper(void *param)
857 {
858   char buffer[STRING_BUFFER_SIZE];
859   struct test_thread_context *context= (struct test_thread_context*) param;
860 
861   WRITE_SEP();
862   WRITE_STR("init thread\n");
863   if (srv_session_init_thread(context->p))
864     my_plugin_log_message(&context->p, MY_ERROR_LEVEL, "srv_session_init_thread failed.");
865 
866   context->test_function(context->p);
867 
868   WRITE_STR("deinit thread\n");
869   srv_session_deinit_thread();
870 
871   context->thread_finished= true;
872   return NULL;
873 }
874 
875 
create_log_file(const char * log_name)876 static void create_log_file(const char * log_name)
877 {
878   char filename[FN_REFLEN];
879 
880   fn_format(filename, log_name, "", ".log",
881             MY_REPLACE_EXT | MY_UNPACK_FILENAME);
882   unlink(filename);
883   outfile= my_open(filename, O_CREAT|O_RDWR, MYF(0));
884 }
885 
886 
test_in_spawned_thread(void * p,void (* test_function)(void *))887 static void test_in_spawned_thread(void *p, void (*test_function)(void *))
888 {
889   my_thread_attr_t attr;          /* Thread attributes */
890   my_thread_attr_init(&attr);
891   (void) my_thread_attr_setdetachstate(&attr, MY_THREAD_CREATE_JOINABLE);
892 
893   struct test_thread_context context;
894 
895   context.p= p;
896   context.thread_finished= false;
897   context.test_function= test_function;
898 
899   /* now create the thread and call test_session within the thread. */
900   if (my_thread_create(&(context.thread), &attr, test_sql_threaded_wrapper, &context) != 0)
901     my_plugin_log_message(&p, MY_ERROR_LEVEL, "Could not create test session thread");
902   else
903     my_thread_join(&context.thread, NULL);
904 }
905 
test_sql_service_plugin_init(void * p)906 static int test_sql_service_plugin_init(void *p)
907 {
908   char buffer[STRING_BUFFER_SIZE];
909   DBUG_ENTER("test_sql_service_plugin_init");
910   my_plugin_log_message(&p, MY_INFORMATION_LEVEL, "Installation.");
911 
912   create_log_file(log_filename);
913 
914   WRITE_SEP();
915   WRITE_STR("Test in a server thread\n");
916   test_sql(p);
917 
918   /* Test in a new thread */
919   WRITE_STR("Follows threaded run\n");
920   test_in_spawned_thread(p, test_sql);
921 
922   my_close(outfile, MYF(0));
923 
924   DBUG_RETURN(0);
925 }
926 
927 
test_sql_service_plugin_deinit(void * p)928 static int test_sql_service_plugin_deinit(void *p)
929 {
930   DBUG_ENTER("test_sql_service_plugin_deinit");
931   my_plugin_log_message(&p, MY_INFORMATION_LEVEL, "Uninstallation.");
932   DBUG_RETURN(0);
933 }
934 
935 
936 static struct st_mysql_daemon test_sql_service_plugin=
937 { MYSQL_DAEMON_INTERFACE_VERSION };
938 
939 
940 /*
941   Plugin library descriptor
942 */
943 
mysql_declare_plugin(test_daemon)944 mysql_declare_plugin(test_daemon)
945 {
946   MYSQL_DAEMON_PLUGIN,
947   &test_sql_service_plugin,
948   "test_sql_stored_procedures_functions",
949   "Pavan Naik, Andrey Hristov",
950   "Test SQL stored procedures/functions",
951   PLUGIN_LICENSE_GPL,
952   test_sql_service_plugin_init,   /* Plugin Init      */
953   test_sql_service_plugin_deinit, /* Plugin Deinit    */
954   0x0100,                         /* 1.0              */
955   NULL,                           /* status variables */
956   NULL,                           /* system variables */
957   NULL,                           /* config options   */
958   0,                              /* flags            */
959 }
960 mysql_declare_plugin_end;
961