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