1 /* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
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 <my_global.h>
24 #include <stdlib.h>
25 #include <ctype.h>
26 #include <mysql_version.h>
27 #include <mysql/plugin.h>
28 #include <my_dir.h>
29 #include "my_sys.h"                             // my_write, my_malloc
30 #include "m_string.h"                           // strlen
31 #include "sql_plugin.h"                         // st_plugin_int
32 
33 static const char *log_filename= "test_session_in_thd";
34 
35 #define MAX_SESSIONS 500
36 
37 #define STRING_BUFFER_SIZE 512
38 
39 #define WRITE_STR(format) my_snprintf(buffer,sizeof(buffer),format); \
40                                       my_write(outfile,(uchar*)buffer,strlen(buffer),MYF(0))
41 #define WRITE_VAL(format,value) my_snprintf(buffer,sizeof(buffer),format,value); \
42                                             my_write(outfile,(uchar*)buffer,strlen(buffer),MYF(0))
43 static const char *sep = "============================================================================================\n";
44 
45 #define WRITE_SEP() my_write(outfile, (uchar*)sep, strlen(sep), MYF(0))
46 
47 static File outfile;
48 
49 struct test_services_context
50 {
51   my_thread_handle test_services_thread;
52 };
53 
54 /* SQL (system) variable to control number of sessions                    */
55 /* Only effective at start of mysqld by setting it as option --loose-...  */
56 int nb_sessions;
57 static MYSQL_SYSVAR_INT (nb_sessions, nb_sessions, PLUGIN_VAR_RQCMDARG,
58 		        "number of sessions", NULL, NULL, 1, 1, 500, 0);
59 
60 static struct st_mysql_sys_var *test_services_sysvars[]= {
61   MYSQL_SYSVAR(nb_sessions),
62   NULL
63 };
64 
test_session(void * p)65 static void test_session(void *p)
66 {
67   char buffer[STRING_BUFFER_SIZE];
68   DBUG_ENTER("test_session");
69 
70   MYSQL_SESSION sessions[MAX_SESSIONS];
71   bool session_ret= false;
72   void *plugin_ctx= p;
73 
74   /* Open session 1: Must pass */
75   for (int i= 0; i < nb_sessions; i++)
76   {
77     WRITE_VAL("sql open session %d.\n", i);
78     sessions[i]= srv_session_open(NULL, plugin_ctx);
79     if (!sessions[i])
80       my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_open_%d failed.", i);
81   }
82 
83   /* close session 1: Must pass i*/
84   WRITE_VAL("close following nb of sessions: %d\n",nb_sessions);
85   for (int i= 0; i < nb_sessions; i++)
86   {
87     WRITE_VAL("sql session close session %d.\n", nb_sessions-1-i);
88     session_ret= srv_session_close(sessions[nb_sessions-1-i]);
89     if (session_ret)
90       my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_close_%d failed.", nb_sessions-1-i);
91   }
92 
93   /* Open session 1: Must pass */
94   for (int i= 0; i < nb_sessions; i++)
95   {
96     WRITE_VAL("sql open session %d.\n", i);
97     sessions[i]= srv_session_open(NULL, plugin_ctx);
98     if (!sessions[i])
99       my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_open_%d failed.", i);
100   }
101 
102   /* close session 1: Must pass */
103   WRITE_VAL("close following nb of sessions: %d\n",nb_sessions);
104   for (int i= 0; i < nb_sessions; i++)
105   {
106     WRITE_VAL("sql session close session %d.\n", i);
107     session_ret= srv_session_close(sessions[i]);
108     if (session_ret)
109       my_plugin_log_message(&p, MY_ERROR_LEVEL, "srv_session_close_%d failed.", i);
110   }
111 
112   DBUG_VOID_RETURN;
113 }
114 
115 
116 struct test_thread_context
117 {
118   my_thread_handle thread;
119   void *p;
120   bool thread_finished;
121   void (*test_function)(void *);
122 };
123 
124 
test_sql_threaded_wrapper(void * param)125 static void* test_sql_threaded_wrapper(void *param)
126 {
127   char buffer[STRING_BUFFER_SIZE];
128   struct test_thread_context *context= (struct test_thread_context*) param;
129 
130   WRITE_SEP();
131   WRITE_STR("init thread\n");
132   if (srv_session_init_thread(context->p))
133     my_plugin_log_message(&context->p, MY_ERROR_LEVEL, "srv_session_init_thread failed.");
134 
135   context->test_function(context->p);
136 
137   WRITE_STR("deinit thread\n");
138   srv_session_deinit_thread();
139 
140   context->thread_finished= true;
141   return NULL;
142 }
143 
144 
create_log_file(const char * log_name)145 static void create_log_file(const char * log_name)
146 {
147   char filename[FN_REFLEN];
148 
149   fn_format(filename, log_name, "", ".log",
150             MY_REPLACE_EXT | MY_UNPACK_FILENAME);
151   unlink(filename);
152   outfile= my_open(filename, O_CREAT|O_RDWR, MYF(0));
153 }
154 
155 
test_in_spawned_thread(void * p,void (* test_function)(void *))156 static void test_in_spawned_thread(void *p, void (*test_function)(void *))
157 {
158   my_thread_attr_t attr;          /* Thread attributes */
159   my_thread_attr_init(&attr);
160   (void) my_thread_attr_setdetachstate(&attr, MY_THREAD_CREATE_JOINABLE);
161 
162   struct test_thread_context context;
163 
164   context.p= p;
165   context.thread_finished= false;
166   context.test_function= test_function;
167 
168   /* now create the thread and call test_session within the thread. */
169   if (my_thread_create(&(context.thread), &attr, test_sql_threaded_wrapper, &context) != 0)
170     my_plugin_log_message(&p, MY_ERROR_LEVEL, "Could not create test session thread");
171   else
172     my_thread_join(&context.thread, NULL);
173 }
174 
test_sql_service_plugin_init(void * p)175 static int test_sql_service_plugin_init(void *p)
176 {
177   char buffer[STRING_BUFFER_SIZE];
178   DBUG_ENTER("test_sql_service_plugin_init");
179   my_plugin_log_message(&p, MY_INFORMATION_LEVEL, "Installation.");
180 
181   create_log_file(log_filename);
182 
183   WRITE_STR("Follows threaded run\n");
184   test_in_spawned_thread(p, test_session);
185 
186   my_close(outfile, MYF(0));
187 
188   DBUG_RETURN(0);
189 }
190 
191 
test_sql_service_plugin_deinit(void * p)192 static int test_sql_service_plugin_deinit(void *p)
193 {
194   DBUG_ENTER("test_sql_service_plugin_deinit");
195   DBUG_RETURN(0);
196 }
197 
198 
199 struct st_mysql_daemon test_session_service_plugin=
200 { MYSQL_DAEMON_INTERFACE_VERSION };
201 
202 
203 /*
204   Plugin library descriptor
205 */
206 
mysql_declare_plugin(test_daemon)207 mysql_declare_plugin(test_daemon)
208 {
209   MYSQL_DAEMON_PLUGIN,
210   &test_session_service_plugin,
211   "test_session_in_thd",
212   "Horst Hunger, Andrey Hristov",
213   "Test sessions in thread",
214   PLUGIN_LICENSE_GPL,
215   test_sql_service_plugin_init,   /* Plugin Init      */
216   test_sql_service_plugin_deinit, /* Plugin Deinit    */
217   0x0100,                             /* 1.0              */
218   NULL,                               /* status variables */
219   test_services_sysvars,              /* system variables */
220   NULL,                               /* config options   */
221   0,                                  /* flags            */
222 }
223 mysql_declare_plugin_end;
224