1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
#ifndef OPT_TRACE_INCLUDED
#define OPT_TRACE_INCLUDED
/* This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
#include "opt_trace_context.h" // Opt_trace_context
#include "sql_lex.h"
#include "my_json_writer.h"
#include "sql_select.h"
class Item;
class THD;
struct TABLE_LIST;
/*
User-visible information about a trace.
*/
struct Opt_trace_info
{
/**
String containing trace.
If trace has been end()ed, this is 0-terminated, which is only to aid
debugging or unit testing; this property is not relied upon in normal
server usage.
If trace has not been ended, this is not 0-terminated. That rare case can
happen when a substatement reads OPTIMIZER_TRACE (at that stage, the top
statement is still executing so its trace is not ended yet, but may still
be read by the sub-statement).
*/
const char *trace_ptr;
size_t trace_length;
//// String containing original query.
const char *query_ptr;
size_t query_length;
const CHARSET_INFO *query_charset; ///< charset of query string
/**
How many bytes this trace is missing (for traces which were truncated
because of @@@@optimizer-trace-max-mem-size).
The trace is not extended beyond trace-max-mem-size.
*/
size_t missing_bytes;
/*
Whether user lacks privilege to see this trace.
If this is set to TRUE, then we return an empty trace
*/
bool missing_priv;
};
/**
Instantiate this class to start tracing a THD's actions (generally at a
statement's start), and to set the "original" query (not transformed, as
sent by client) for the new trace. Destructor will end the trace.
@param thd the THD
@param tbl list of tables read/written by the statement.
@param sql_command SQL command being prepared or executed
@param set_vars what variables are set by this command (only used if
sql_command is SQLCOM_SET_OPTION)
@param query query
@param length query's length
@param charset charset which was used to encode this query
*/
class Opt_trace_start
{
public:
Opt_trace_start(THD *thd_arg): ctx(&thd_arg->opt_trace), traceable(false) {}
void init(THD *thd, TABLE_LIST *tbl,
enum enum_sql_command sql_command,
List<set_var_base> *set_vars,
const char *query,
size_t query_length,
const CHARSET_INFO *query_charset);
~Opt_trace_start();
private:
Opt_trace_context *const ctx;
/*
True: the query will be traced
False: otherwise
*/
bool traceable;
};
/**
Prints SELECT query to optimizer trace. It is not the original query (as in
@c Opt_trace_context::set_query()) but a printout of the parse tree
(Item-s).
@param thd the THD
@param select_lex query's parse tree
@param trace_object Json_writer object to which the query will be added
*/
void opt_trace_print_expanded_query(THD *thd, SELECT_LEX *select_lex,
Json_writer_object *trace_object);
void add_table_scan_values_to_trace(THD *thd, JOIN_TAB *tab);
void trace_plan_prefix(JOIN *join, uint idx, table_map join_tables);
void print_final_join_order(JOIN *join);
void print_best_access_for_table(THD *thd, POSITION *pos,
enum join_type type);
void trace_condition(THD * thd, const char *name, const char *transform_type,
Item *item, const char *table_name= nullptr);
/*
Security related (need to add a proper comment here)
*/
/**
If the security context is not that of the connected user, inform the trace
system that a privilege is missing. With one exception: see below.
@param thd
This serves to eliminate the following issue.
Any information readable by a SELECT may theoretically end up in
the trace. And a SELECT may read information from other places than tables:
- from views (reading their bodies)
- from stored routines (reading their bodies)
- from files (reading their content), with LOAD_FILE()
- from the list of connections (reading their queries...), with
I_S.PROCESSLIST.
If the connected user has EXECUTE privilege on a routine which does a
security context change, the routine can retrieve information internally
(if allowed by the SUID context's privileges), and present only a portion
of it to the connected user. But with tracing on, all information is
possibly in the trace. So the connected user receives more information than
the routine's definer intended to provide. Fixing this issue would require
adding, near many privilege checks in the server, a new
optimizer-trace-specific check done against the connected user's context,
to verify that the connected user has the right to see the retrieved
information.
Instead, our chosen simpler solution is that if we see a security context
change where SUID user is not the connected user, we disable tracing. With
only one safe exception: if the connected user has all global privileges
(because then she/he can find any information anyway). By "all global
privileges" we mean everything but WITH GRANT OPTION (that latter one isn't
related to information gathering).
Read access to I_S.OPTIMIZER_TRACE by another user than the connected user
is restricted: @see fill_optimizer_trace_info().
*/
void opt_trace_disable_if_no_security_context_access(THD *thd);
void opt_trace_disable_if_no_tables_access(THD *thd, TABLE_LIST *tbl);
/**
If tracing is on, checks additional privileges for a view, to make sure
that the user has the right to do SHOW CREATE VIEW. For that:
- this function checks SHOW VIEW
- SELECT is tested in opt_trace_disable_if_no_tables_access()
- SELECT + SHOW VIEW is sufficient for SHOW CREATE VIEW.
We also check underlying tables.
If a privilege is missing, notifies the trace system.
This function should be called when the view's underlying tables have not
yet been merged.
@param thd THD context
@param view view to check
@param underlying_tables underlying tables/views of 'view'
*/
void opt_trace_disable_if_no_view_access(THD *thd, TABLE_LIST *view,
TABLE_LIST *underlying_tables);
/**
If tracing is on, checks additional privileges on a stored routine, to make
sure that the user has the right to do SHOW CREATE PROCEDURE/FUNCTION. For
that, we use the same checks as in those SHOW commands.
If a privilege is missing, notifies the trace system.
This function is not redundant with
opt_trace_disable_if_no_security_context_access().
Indeed, for a SQL SECURITY INVOKER routine, there is no context change, but
we must still verify that the invoker can do SHOW CREATE.
For triggers, see note in sp_head::execute_trigger().
@param thd
@param sp routine to check
*/
void opt_trace_disable_if_no_stored_proc_func_access(THD *thd, sp_head *sp);
/**
Fills information_schema.OPTIMIZER_TRACE with rows (one per trace)
@retval 0 ok
@retval 1 error
*/
int fill_optimizer_trace_info(THD *thd, TABLE_LIST *tables, Item *);
#define OPT_TRACE_TRANSFORM(thd, object_level0, object_level1, \
select_number, from, to) \
Json_writer_object object_level0(thd); \
Json_writer_object object_level1(thd, "transformation"); \
object_level1.add_select_number(select_number).add("from", from).add("to", to);
#define OPT_TRACE_VIEWS_TRANSFORM(thd, object_level0, object_level1, \
derived, name, select_number, algorithm) \
Json_writer_object trace_wrapper(thd); \
Json_writer_object trace_derived(thd, derived); \
trace_derived.add("table", name).add_select_number(select_number) \
.add("algorithm", algorithm);
#endif
|