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