File: opt_trace.h

package info (click to toggle)
mysql-8.0 8.0.44-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,272,892 kB
  • sloc: cpp: 4,685,345; ansic: 412,712; pascal: 108,395; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; python: 21,816; sh: 17,285; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,083; makefile: 1,793; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (1078 lines) | stat: -rw-r--r-- 42,704 bytes parent folder | download
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
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
/* Copyright (c) 2011, 2025, Oracle and/or its affiliates.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License, version 2.0,
   as published by the Free Software Foundation.

   This program is designed to work with certain software (including
   but not limited to OpenSSL) that is licensed under separate terms,
   as designated in a particular file or component or in included license
   documentation.  The authors of MySQL hereby grant you an additional
   permission to link the program and your derivative works with the
   separately licensed software that they have either included with
   the program or referenced in the documentation.

   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, version 2.0, 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  */

#ifndef OPT_TRACE_INCLUDED
#define OPT_TRACE_INCLUDED

#include <limits.h>
#include <string.h>
#include <sys/types.h>

#include "my_compiler.h"
#include "my_inttypes.h"
#include "my_sqlcommand.h"          // enum_sql_command
#include "sql/opt_trace_context.h"  // Opt_trace_context

class Cost_estimate;
class Item;
class THD;
class set_var_base;
class sp_head;
class sp_printable;
struct CHARSET_INFO;
class Table_ref;
template <class T>
class List;

/**
  @file sql/opt_trace.h
  API for the Optimizer trace (WL#5257)
*/

/**
  @page PAGE_OPT_TRACE The Optimizer Trace

  @section INTRODUCTION Introduction

  This optimizer trace is aimed at producing output, which is readable by
  humans and by programs, to aid understanding of decisions and actions taken
  by the MySQL Optimizer.

  @section OUTPUT_FORMAT Output format

  The chosen output format is JSON (JavaScript Object Notation).
  In JSON there are:
  @li "objects" (unordered set of key-value pairs); equivalent to Python's
  dictionary or Perl's associative array or hash or STL's hash_map.
  @li "arrays" (ordered set of values); equivalent to Python's and Perl's list
  or STL's vector.
  @li "values": a value can be a string, number, boolean, null,
  which we all call "scalars", or be an object, array.

  For example (explanations after "<<" are not part of output):
@verbatim
  {                           << start of top object
    "first_name": "Gustave",  << key/value pair (value is string)
    "last_name": "Eiffel",    << key/value pair (value is string)
    "born": 1832,             << key/value pair (value is integer)
    "contributions_to": [     << key/value pair (value is array)
       {                      << 1st item of array is an object (a building)
         "name": "Eiffel tower",
         "location": Paris
       },                     << end of 1st item of array
       {
         "name": "Liberty statue",
         "location": "New York"
       }                      << end of 2nd item of array
    ]                         << end of array
  }                           << end of top object
@endverbatim
  For more details, have a look at the syntax at json.org.
  Note that indentation and newlines are superfluous, useful only for
  human-readability.
  Note also that there is nothing like a "named object": an object, array or
  value has no name; but if it is the value of a key/value pair in an
  enclosing, outer object, then the key can be seen as the inner object's
  "name".

  @section USER_ENABLE_TRACING How a user enables/views the trace

@verbatim
  SET SESSION OPTIMIZER_TRACE="enabled=on"; # enable tracing
  <statement to trace>; # like SELECT, EXPLAIN SELECT, UPDATE, DELETE...
  SELECT * FROM information_schema.OPTIMIZER_TRACE;
  [ repeat last two steps at will ]
  SET SESSION OPTIMIZER_TRACE="enabled=off"; # disable tracing
@endverbatim

  @c SELECT and @c EXPLAIN SELECT produce the same trace. But there are
  exceptions regarding subqueries because the two commands treat subqueries
  differently, for example in
@verbatim
  SELECT ... WHERE x IN (subq1) AND y IN (subq2)
@endverbatim
  SELECT terminates after executing the first subquery if the related IN
  predicate is false, so we won't see @c JOIN::optimize() tracing for subq2;
  whereas EXPLAIN SELECT analyzes all subqueries (see loop at the end of
  @c select_describe()).

  @section USER_SELECT_TRACING_STATEMENTS How a user traces only certain
statements

  When tracing is in force, each SQL statement generates a trace; more
  exactly, so does any of
  SELECT,
  EXPLAIN SELECT,
  INSERT or REPLACE ( with VALUES or SELECT),
  UPDATE/DELETE and their multi-table variants,
  SET (unless it manipulates @@@@optimizer_trace),
  DO,
  DECLARE/CASE/IF/RETURN (stored routines language elements),
  CALL.
  If a command above is prepared and executed in separate steps, preparation
  and execution are separately traced.
  By default each new trace overwrites the previous trace. Thus, if a
  statement contains sub-statements (example: invokes stored procedures,
  stored functions, triggers), the top statement and sub-statements each
  generate traces, but at the execution's end only the last sub-statement's
  trace is visible.
  If the user wants to see the trace of another sub-statement, she/he can
  enable/disable tracing around the desired sub-statement, but this requires
  editing the routine's code, which may not be possible. Another solution is
  to use
@verbatim
  SET optimizer_trace_offset=<OFFSET>, optimizer_trace_limit=<LIMIT>
@endverbatim
  where OFFSET is a signed integer, and LIMIT is a positive integer.
  The effect of this SET is the following:

  @li all remembered traces are cleared

  @li a later SELECT on OPTIMIZER_TRACE returns the first LIMIT traces of
  the OFFSET oldest remembered traces (if OFFSET >= 0), or the first LIMIT
  traces of the -OFFSET newest remembered traces (if OFFSET < 0).

  For example,
  a combination of OFFSET=-1 and LIMIT=1 will make the last trace be shown (as
  is default), OFFSET=-2 and LIMIT=1 will make the next-to-last be shown,
  OFFSET=-5 and LIMIT=5 will make the last five traces be shown. Such negative
  OFFSET can be useful when one knows that the interesting sub-statements are
  the few last ones of a stored routine, like this:
@verbatim
  SET optimizer_trace_offset=-5, optimizer_trace_limit=5;
  CALL stored_routine(); # more than 5 sub-statements in this routine
  SELECT * FROM information_schema.OPTIMIZER_TRACE; # see only last 5 traces
@endverbatim
  On the opposite, a positive OFFSET can be useful when one knows that the
  interesting sub-statements are the few first ones of a stored routine.

  The more those two variables are accurately adjusted, the less memory is
  used. For example, OFFSET=0 and LIMIT=5 will use memory to remember 5
  traces, so if only the three first are needed, OFFSET=0 and LIMIT=3 is
  better (tracing stops after the LIMITth trace, so the 4th and 5th trace are
  not created and don't take up memory). A stored routine may have a loop
  which executes many sub-statements and thus generates many traces, which
  would use a lot of memory; proper OFFSET and LIMIT can restrict tracing to
  one iteration of the loop for example. This also gains speed, as tracing a
  sub-statement impacts performance.

  If OFFSET>=0, only LIMIT traces are kept in memory. If OFFSET<0, that is not
  true: instead, (-OFFSET) traces are kept in memory; indeed even if LIMIT is
  smaller than (-OFFSET), so excludes the last statement, the last statement
  must still be traced because it will be inside LIMIT after executing one
  more statement (remember than OFFSET<0 is counted from the end: the "window"
  slides as more statements execute).

  Such memory and speed gains are the reason why optimizer_trace_offset/limit,
  which are restrictions at the trace producer level, are offered. They are
  better than using
@verbatim
  SELECT * FROM OPTIMIZER_TRACE LIMIT <LIMIT> OFFSET <OFFSET>;
@endverbatim
  which is a restriction on the trace consumer level, which saves almost
  nothing.

  @section USER_SELECT_TRACING_FEATURES How a user traces only certain
  optimizer features

@verbatim
  SET OPTIMIZER_TRACE_FEATURES="feature1=on|off,...";
@endverbatim
  where "feature1" is one optimizer feature. For example "greedy_search": a
  certain Opt_trace_array at the start of @c
  Optimize_table_order::choose_table_order() has a flag "GREEDY_SEARCH" passed
  to its constructor: this means that if the user has turned tracing of greedy
  search off, this array will not be written to the I_S trace, neither will
  any children structures. All this disabled "trace chunk" will be replaced by
  an ellipsis "...".

  @section DEV_ADDS_TRACING How a developer adds tracing to a function

  Check @c Opt_trace* usage in @c advance_sj_state():

@verbatim
  Opt_trace_array trace_choices(trace, "semijoin_strategy_choice");
@endverbatim

  This creates an array for key "semijoin_strategy_choice". We are going to
  list possible semijoin strategy choices.

@verbatim
  Opt_trace_object trace_one_strategy(trace);
@endverbatim

  This creates an object without key (normal, it's in an array). This
  object will describe one single strategy choice.

@verbatim
  trace_one_strategy.add_alnum("strategy", "FirstMatch");
@endverbatim

  This adds a key/value pair to the just-created object: key is "strategy",
  value is "FirstMatch". This is the strategy to be described in the
  just-created object.

@verbatim
  trace_one_strategy.add("cost", *current_read_time).
    add("records", *current_record_count);
  trace_one_strategy.add("chosen", (pos->sj_strategy == SJ_OPT_FIRST_MATCH));
@endverbatim

  This adds 3 key/value pairs: cost of strategy, number of records produced
  by this strategy, and whether this strategy is chosen.

  After that, there is similar code for other semijoin strategies.

  The resulting trace piece (seen in @c information_schema.OPTIMIZER_TRACE) is
@verbatim
          "semijoin_strategy_choice": [
            {
              "strategy": "FirstMatch",
              "cost": 1,
              "records": 1,
              "chosen": true
            },
            {
              "strategy": "DuplicatesWeedout",
              "cost": 1.1,
              "records": 1,
              "duplicate_tables_left": false,
              "chosen": false
            }
          ]
@endverbatim

  For more output examples, check result files of the opt_trace suite in
  @c mysql-test.

  Feature can be un-compiled with @code cmake -DOPTIMIZER_TRACE=0 @endcode.

  @section WITH_DBUG Interaction between trace and DBUG

  We don't want to have to duplicate code like this:
@verbatim
  DBUG_PRINT("info",("cost %g",cost));
  Opt_trace_object(thd->opt_trace).add("cost",cost);
@endverbatim

  Thus, any optimizer trace operation, *even* if tracing is run-time disabled,
  has an implicit DBUG_PRINT("opt",...) inside. This way, only the
  second line above is needed, and several DBUG_PRINT() could be removed from
  the Optimizer code.
  When tracing is run-time disabled, in a debug binary, traces are still
  created in order to catch the @c add() calls and write their text to DBUG,
  but those traces are not visible into INFORMATION_SCHEMA.OPTIMIZER_TRACE: we
  then say that they "don't support I_S".
  A debug binary without optimizer trace compiled in, will intentionally not
  compile.

  Because opening an object or array, or add()-ing to it, writes to DBUG
  immediately, a key/value pair and its outer object may be 100 lines
  apart in the DBUG log.

  @section ADDING_TRACING Guidelines for adding tracing

  @li Try to limit the number of distinct "words". For example, when
  describing an optimizer's decision, the words "chosen" (true/false value,
  tells whether we are choosing the said optimization), "cause" (free text
  value, tells why we are making this choice, when it's not obvious)
  can and should often be used. Having a restricted vocabulary helps
  consistency. Use "row" instead of "record". Use "tmp" instead of
  "temporary".

  @li Use only simple characters for key names: a-ZA-Z_#, and no space. '#'
  serves to denote a number, like in "select#" .

  @li Keep in mind than in an object, keys are not ordered; an application may
  parse the JSON output and output it again with keys order changed; thus
  when order matters, use an array (which may imply having anonymous objects
  as items of the array, with keys inside the anonymous objects, see how it's
  done in @c JOIN::optimize()). Keep in mind that in an object keys should
  be unique, an application may lose duplicate keys.

  @section OOM_HANDLING Handling of "out-of-memory" errors

  All memory allocations (with exceptions: see below) in the Optimizer trace
  use @c my_error() to report errors, which itself calls @c
  error_handler_hook. It is the responsibility of the API user to set up a
  proper @c error_handler_hook which will alert her/him of the OOM
  problem. When in the server, this is already the case (@c error_handler_hook
  is @c my_message_sql() which makes the statement fail).
  Note that the debug binary may crash if OOM (OOM can cause syntax
  errors...).

  @section TRACE_SECURITY Description of trace-induced security checks.

  A trace exposes information. For example if one does SELECT on a view, the
  trace contains the view's body. So, the user should be allowed to see the
  trace only if she/he has privilege to see the body, i.e. privilege to do
  SHOW CREATE VIEW.
  There are similar issues with stored procedures, functions, triggers.

  We implement this by doing additional checks on SQL objects when tracing is
  on:
  @li stored procedures, functions, triggers: checks are done when executing
  those objects
  @li base tables and views.

  Base tables or views are listed in some @c LEX::query_tables.
  The LEX may be of the executing statement (statement executed by
  @c mysql_execute_command(), or by
  @c sp_lex_keeper::reset_lex_and_exec_core()), we check this LEX in the
  constructor of Opt_trace_start.
  Or it may be a LEX describing a view, we check this LEX when
  opening the view (@c open_and_read_view()).

  Those checks are greatly simplified by disabling traces in case of security
  context changes. @see opt_trace_disable_if_no_security_context_access().

  Those checks must be done with the security context of the connected
  user. Checks with the SUID context would be useless: assume the design is
  that the basic user does not have DML privileges on tables, but only
  EXECUTE on SUID-highly-privileged routines (which implement _controlled_
  _approved_ DMLs): then the SUID context would successfully pass all
  additional privilege checks, routine would generate tracing, and the
  connected user would view the trace after the routine's execution, seeing
  secret information.

  @section NEXT What a developer should read next

  The documentation of those classes, in order
@code
  Opt_trace_context
  Opt_trace_context_impl
  Opt_trace_stmt
  Opt_trace_struct
  Opt_trace_object
  Opt_trace_array
@endcode
  and then @ref opt_trace.h as a whole.
*/

class Opt_trace_stmt;  // implementation detail local to opt_trace.cc

/**
   User-visible information about a trace. @sa Opt_trace_iterator.
*/
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;  ///< length of trace string
  //// String containing original query. 0-termination: like trace_ptr.
  const char *query_ptr;
  size_t query_length;                ///< length of query string
  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).
  */
  size_t missing_bytes;
  bool missing_priv;  ///< whether user lacks privilege to see this trace
};

/**
   Iterator over the list of remembered traces.
   @note due to implementation, the list must not change during an
   iterator's lifetime, or results may be unexpected (no crash though).
*/
class Opt_trace_iterator {
 public:
  /**
    @param  ctx  context
  */
  Opt_trace_iterator(Opt_trace_context *ctx);

  void next();  ///< Advances iterator to next trace.

  /**
     Provides information about the trace on which the iterator is
     positioned.
     @param[out]  info  information returned.
     The usage pattern is
     1) instantiate the iterator
     2) test at_end(), if false: call get_value() and then next()
     3) repeat (2) until at_end() is true.
  */
  void get_value(Opt_trace_info *info) const;

  /// @returns whether iterator is positioned to the end.
  bool at_end() const { return cursor == nullptr; }

 private:
  /// Pointer to context, from which traces are retrieved
  Opt_trace_context *ctx;
  const Opt_trace_stmt *cursor;  ///< trace which the iterator is positioned on
  long row_count;                ///< how many traces retrieved so far
};

/**
   Object and array are both "structured data" and have lots in common, so the
   Opt_trace_struct is a base class for them.
   When you want to add a structure to the trace, you create an instance of
   Opt_trace_object or Opt_trace_array, then you add information to it with
   add(), then the destructor closes the structure (we use RAII, Resource
   Acquisition Is Initialization).
*/

class Opt_trace_struct {
 protected:
  /**
     @param  ctx_arg  Optimizer trace context for this structure
     @param  requires_key_arg  whether this structure requires/forbids keys
                      for values put inside it (an object requires them, an
                      array forbids them)
     @param  key      key if this structure is the value of a key/value pair,
                      NULL otherwise. This pointer must remain constant and
                      valid until the object is destroyed (to support
                      @ref saved_key).
     @param  feature  optimizer feature to which this structure belongs

     This constructor is never called directly, only from subclasses.
  */
  Opt_trace_struct(Opt_trace_context *ctx_arg, bool requires_key_arg,
                   const char *key, Opt_trace_context::feature_value feature)
      : started(false) {
    // A first inlined test
    if (unlikely(ctx_arg->is_started())) {
      // Tracing enabled: must fully initialize the structure.
      do_construct(ctx_arg, requires_key_arg, key, feature);
    }
    /*
      Otherwise, just leave "started" to false, it marks that the structure is
      dummy.
    */
  }
  ~Opt_trace_struct() {
    if (unlikely(started)) do_destruct();
  }

 public:
  /**
    The exception to RAII: this function is an explicit way of ending a
    structure before it goes out of scope. Don't use it unless RAII mandates
    a new scope which mandates re-indenting lots of code lines.
  */
  void end() {
    if (unlikely(started)) do_destruct();
  }

  /**
     Adds a value (of string type) to the structure. A key is specified, so it
     adds the key/value pair (the structure must thus be an object).

     There are two "add_*" variants to add a string value.
     If the value is 0-terminated and each character
     - is ASCII 7-bit
     - has ASCII code >=32 and is neither '"' nor '\\'
     then add_alnum() should be used. That should be the case for all fixed
     strings like add_alnum("cause", "cost").
     Otherwise, add_utf8() should be used; it accepts any UTF8-encoded
     character in 'value' and will escape characters which JSON requires (and
     is thus slower than add_alnum()). It should be used for all strings which
     we get from the server's objects (indeed a table's name, a WHERE
     condition, may contain "strange" characters).

     @param  key    key
     @param  value  value
     @returns a reference to the structure, useful for chaining like this:
     @verbatim add(x,y).add(z,t).add(u,v) @endverbatim

     String-related add() variants are named add_[something]():
     - to avoid confusing the compiler between:
     add(const char *value, size_t    val_length) and
     add(const char *key,   ulonglong value)
     - and because String::length() returns uint32 and not size_t, so for
     add(str.ptr(), str.length())
     compiler may pick
     add(const char *key,   double value) instead of
     add(const char *value, size_t val_length).
  */
  Opt_trace_struct &add_alnum(const char *key, const char *value) {
    if (likely(!started)) return *this;
    return do_add(key, value, strlen(value), false);
  }

  /**
     Adds a value (of string type) to the structure. No key is specified, so
     it adds only the value (the structure must thus be an array).
     @param  value  value
     @returns a reference to the structure
  */
  Opt_trace_struct &add_alnum(const char *value) {
    if (likely(!started)) return *this;
    return do_add(nullptr, value, strlen(value), false);
  }

  /**
     Like add_alnum() but supports any UTF8 characters in 'value'.
     Will "escape" 'value' to be JSON-compliant.
     @param  key         key
     @param  value       value
     @param  val_length  length of string 'value'
  */
  Opt_trace_struct &add_utf8(const char *key, const char *value,
                             size_t val_length) {
    if (likely(!started)) return *this;
    return do_add(key, value, val_length, true);
  }

  /// Variant of add_utf8() for adding to an array (no key)
  Opt_trace_struct &add_utf8(const char *value, size_t val_length) {
    if (likely(!started)) return *this;
    return do_add(nullptr, value, val_length, true);
  }

  /// Variant of add_utf8() where 'value' is 0-terminated
  Opt_trace_struct &add_utf8(const char *key, const char *value) {
    if (likely(!started)) return *this;
    return do_add(key, value, strlen(value), true);
  }

  /// Variant of add_utf8() where 'value' is 0-terminated
  Opt_trace_struct &add_utf8(const char *value) {
    if (likely(!started)) return *this;
    return do_add(nullptr, value, strlen(value), true);
  }

  /**
     Add a value (of Item type) to the structure. The Item should be a
     condition (like a WHERE clause) which will be pretty-printed into the
     trace. This is useful for showing condition transformations (equality
     propagation etc).
     @param  key    key
     @param  item   the Item
     @return a reference to the structure
  */
  Opt_trace_struct &add(const char *key, Item *item) {
    if (likely(!started)) return *this;
    return do_add(key, item);
  }
  Opt_trace_struct &add(Item *item) {
    if (likely(!started)) return *this;
    return do_add(nullptr, item);
  }

 public:
  Opt_trace_struct &add(const char *key, bool value) {
    if (likely(!started)) return *this;
    return do_add(key, value);
  }
  Opt_trace_struct &add(bool value) {
    if (likely(!started)) return *this;
    return do_add(nullptr, value);
  }
  Opt_trace_struct &add(const char *key, int value) {
    if (likely(!started)) return *this;
    return do_add(key, static_cast<longlong>(value));
  }
  Opt_trace_struct &add(int value) {
    if (likely(!started)) return *this;
    return do_add(nullptr, static_cast<longlong>(value));
  }
  Opt_trace_struct &add(const char *key, uint value) {
    if (likely(!started)) return *this;
    return do_add(key, static_cast<ulonglong>(value));
  }
  Opt_trace_struct &add(uint value) {
    if (likely(!started)) return *this;
    return do_add(nullptr, static_cast<ulonglong>(value));
  }
  Opt_trace_struct &add(const char *key, ulong value) {
    if (likely(!started)) return *this;
    return do_add(key, static_cast<ulonglong>(value));
  }
  Opt_trace_struct &add(ulong value) {
    if (likely(!started)) return *this;
    return do_add(nullptr, static_cast<ulonglong>(value));
  }
  Opt_trace_struct &add(const char *key, longlong value) {
    if (likely(!started)) return *this;
    return do_add(key, value);
  }
  Opt_trace_struct &add(longlong value) {
    if (likely(!started)) return *this;
    return do_add(nullptr, value);
  }
  Opt_trace_struct &add(const char *key, ulonglong value) {
    if (likely(!started)) return *this;
    return do_add(key, value);
  }
  Opt_trace_struct &add(ulonglong value) {
    if (likely(!started)) return *this;
    return do_add(nullptr, value);
  }
  Opt_trace_struct &add(const char *key, double value) {
    if (likely(!started)) return *this;
    return do_add(key, value);
  }
  Opt_trace_struct &add(double value) {
    if (likely(!started)) return *this;
    return do_add(nullptr, value);
  }
  /// Adds a JSON null object (==Python's "None")
  Opt_trace_struct &add_null(const char *key) {
    if (likely(!started)) return *this;
    return do_add_null(key);
  }
  /**
     Helper to put the database/table name in an object.
     @param  tab  TABLE* pointer
  */
  Opt_trace_struct &add_utf8_table(const Table_ref *tab) {
    if (likely(!started)) return *this;
    return do_add_utf8_table(tab);
  }
  /**
     Helper to put the number of query_block in an object.
     @param  select_number  number of query_block
  */
  Opt_trace_struct &add_select_number(uint select_number) {
    return add("select#", select_number);
  }
  /**
     Add a value to the structure.
     @param  key    key
     @param  cost   the value of Cost_estimate
     @return a reference to the structure
  */
  Opt_trace_struct &add(const char *key, const Cost_estimate &cost) {
    if (likely(!started)) return *this;
    return do_add(key, cost);
  }

  /**
    Informs this structure that we are adding data (scalars, structures) to
    it.
    This is used only if sending to I_S.
    @returns whether the structure was empty so far.
    @note this is reserved for use by Opt_trace_stmt.
  */
  bool set_not_empty() {
    const bool old_empty = empty;
    empty = false;
    return old_empty;
  }
  /**
    Validates the key about to be added.
    @note this is reserved for use by Opt_trace_stmt.

    When adding a value (or array or object) to an array, or a key/value pair
    to an object, we need to know this outer array or object.

    It would be possible, when trying to add a key to an array, which is wrong
    in JSON, or similarly when trying to add a value without any key to an
    object, to catch it at compilation time, if the adder received, as
    function parameter, the type of the structure (like @c
    Opt_trace_array*). Then the @c add(key,val) call would not compile as
    Opt_trace_array wouldn't feature it.

    But as explained in comment of class Opt_trace_context we
    cannot pass down the object, have to maintain a "current object or
    array" in the Opt_trace_context context (pointer to an instance of
    Opt_trace_struct), and the adder grabs it from the context.

    As this current structure is of type "object or array", we cannot do
    compile-time checks that only suitable functions are used. A call to @c
    add(key,value) is necessarily legal for the compiler as the structure may
    be an object, though it will be wrong in case the structure is actually
    an array at run-time. Thus we have the risk of an untested particular
    situation where the current structure is not an object (but an array)
    though the code expected it to be one. This happens in practice, because
    subqueries are evaluated in many possible places of code, not all of them
    being known. Same happens, to a lesser extent, with calls to the range
    optimizer.
    So at run-time, in check_key(), we detect wrong usage, like adding a value
    to an object without specifying a key, and then remove the unnecessary
    key, or add an autogenerated key.
  */
  const char *check_key(const char *key);

 private:
  /// Not implemented, use add_alnum() instead.
  Opt_trace_struct &add(const char *key, const char *value);
  Opt_trace_struct &add(const char *key);

  /// Full initialization. @sa Opt_trace_struct::Opt_trace_struct
  void do_construct(Opt_trace_context *ctx, bool requires_key, const char *key,
                    Opt_trace_context::feature_value feature);
  /// Really does destruction
  void do_destruct();
  /**
     Really adds to the object. @sa add().

     @note add() has an up-front if(), hopefully inlined, so that in the
     common case - tracing run-time disabled - we have no function call. If
     tracing is enabled, we call do_add().
     In a 20-table plan search (as in BUG#50595), the execution time was
     decreased from 2.6 to 2.0 seconds thanks to this inlined-if trick.

     @param key         key
     @param value       value
     @param val_length  length of string 'value'
     @param escape      do JSON-compliant escaping of 'value'. If 'escape' is
     false, 'value' should be ASCII. Otherwise, should be UTF8.
  */
  Opt_trace_struct &do_add(const char *key, const char *value,
                           size_t val_length, bool escape);
  Opt_trace_struct &do_add(const char *key, Item *item);
  Opt_trace_struct &do_add(const char *key, bool value);
  Opt_trace_struct &do_add(const char *key, longlong value);
  Opt_trace_struct &do_add(const char *key, ulonglong value);
  Opt_trace_struct &do_add(const char *key, double value);
  Opt_trace_struct &do_add_null(const char *key);
  Opt_trace_struct &do_add_utf8_table(const Table_ref *tab);
  Opt_trace_struct &do_add(const char *key, const Cost_estimate &value);

  Opt_trace_struct(const Opt_trace_struct &);             ///< not defined
  Opt_trace_struct &operator=(const Opt_trace_struct &);  ///< not defined

  bool started;  ///< Whether the structure does tracing or is dummy

  /**
     Whether the structure requires/forbids keys for values inside it.
     true: this is an object. false: this is an array.

     @note The canonical way would be to not have such bool per instance, but
     rather have a pure virtual member function
     Opt_trace_struct::requires_key(), overloaded by Opt_trace_object
     (returning true) and by Opt_trace_array (returning false). But
     Opt_trace_object::requires_key() would not be accessible from
     Opt_trace_struct::do_construct() (which would complicate coding), whereas
     the bool is.
  */
  bool requires_key;

  /**
    Whether this structure caused tracing to be disabled in this statement
    because belonging to a not-traced optimizer feature, in accordance with
    the value of @@@@optimizer_trace_features.
  */
  bool has_disabled_I_S;
  bool empty;            ///< @see set_not_empty()
  Opt_trace_stmt *stmt;  ///< Trace owning the structure
  /// Key if the structure is the value of a key/value pair, NULL otherwise
  const char *saved_key;
#ifndef NDEBUG
  /**
     Fixed-length prefix of previous key in this structure, if this structure
     is an object. Serves to detect when adding two same consecutive keys to
     an object, which would be wrong.
  */
  char previous_key[25];
#endif
};

/**
   A JSON object (unordered set of key/value pairs).
   Defines only a constructor, all the rest is inherited from
   Opt_trace_struct.
*/
class Opt_trace_object : public Opt_trace_struct {
 public:
  /**
     Constructs an object. Key is specified, so the object is the value of a
     key/value pair.
     @param  ctx  context for this object
     @param  key  key
     @param  feature  optimizer feature to which this structure belongs
  */
  Opt_trace_object(
      Opt_trace_context *ctx, const char *key,
      Opt_trace_context::feature_value feature = Opt_trace_context::MISC)
      : Opt_trace_struct(ctx, true, key, feature) {}
  /**
     Constructs an object. No key is specified, so the object is just a value
     (serves for the single root object or for adding the object to an array).
     @param  ctx  context for this object
     @param  feature  optimizer feature to which this structure belongs
  */
  Opt_trace_object(
      Opt_trace_context *ctx,
      Opt_trace_context::feature_value feature = Opt_trace_context::MISC)
      : Opt_trace_struct(ctx, true, nullptr, feature) {}
};

/**
   A JSON array (ordered set of values).
   Defines only a constructor, all the rest in inherited from
   Opt_trace_struct.
*/
class Opt_trace_array : public Opt_trace_struct {
 public:
  /**
     Constructs an array. Key is specified, so the array is the value of a
     key/value pair.
     @param  ctx  context for this array
     @param  key  key
     @param  feature  optimizer feature to which this structure belongs
  */
  Opt_trace_array(
      Opt_trace_context *ctx, const char *key,
      Opt_trace_context::feature_value feature = Opt_trace_context::MISC)
      : Opt_trace_struct(ctx, false, key, feature) {}
  /**
     Constructs an array. No key is specified, so the array is just a value
     (serves for adding the object to an array).
     @param  ctx  context for this array
     @param  feature  optimizer feature to which this structure belongs
  */
  Opt_trace_array(
      Opt_trace_context *ctx,
      Opt_trace_context::feature_value feature = Opt_trace_context::MISC)
      : Opt_trace_struct(ctx, false, nullptr, feature) {}
};

/**
   Instantiate an instance of this class for specific cases where
   optimizer trace, in a certain section of Optimizer code, should write only
   to DBUG and not I_S. Example: see sql_select.cc.
   Note that this class should rarely be used; the "feature" parameter of
   Opt_trace_struct is a good alternative.
*/
class Opt_trace_disable_I_S {
 public:
  /**
     @param  ctx_arg      Context.
     @param  disable_arg  Whether the instance should really disable
                          anything. If false, the object is dummy. If true,
                          tracing-to-I_S is disabled at construction and
                          re-enabled at destruction.
     @details A dummy instance is there only for RAII reasons. Imagine we want
     to do this:
@verbatim
     {
       if (x) disable tracing;
       code;
     } // tracing should be re-enabled here
@endverbatim
     As we use RAII, we cannot put the instance declaration inside if(x):
@verbatim
     {
       if (x) Opt_trace_disable_I_S instance(ctx);
       code;
     }
@endverbatim
     because it would be destroyed as soon as the if() block is left, so
     tracing would be re-enabled before @c code;. It should rather be written
     as:
@verbatim
     {
       Opt_trace_disable_I_S instance(ctx, x); // if !x, does nothing
       code;
     } // re-enabling happens here, if x is true
@endverbatim
  */
  Opt_trace_disable_I_S(Opt_trace_context *ctx_arg, bool disable_arg) {
    if (disable_arg) {
      ctx = ctx_arg;
      ctx->disable_I_S_for_this_and_children();
    } else
      ctx = nullptr;
  }

  /// Destructor. Restores trace's "enabled" property to its previous value.
  ~Opt_trace_disable_I_S() {
    if (ctx != nullptr) ctx->restore_I_S();
  }

 private:
  /** Context. Non-NULL if and only if this instance really does disabling */
  Opt_trace_context *ctx;
  Opt_trace_disable_I_S(const Opt_trace_disable_I_S &);  // not defined
  Opt_trace_disable_I_S &operator=(
      const Opt_trace_disable_I_S &);  // not defined
};

/**
   @name Helpers connecting the optimizer trace to THD or Information Schema.
*/

//@{

class Opt_trace_start {
 public:
  /**
    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.

    If in a routine's instruction, there is no "query". To be helpful to the
    user, we craft a query using the instruction's print(). We don't leave this
    to the caller, because it would be inefficient if tracing is off.

    @param  thd_arg       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  query_length  query's length
    @param  instr         routine's instruction, if applicable; if so, 'query'
                          and 'query_length' above are ignored
    @param  query_charset charset which was used to encode this query

    @note Each sub-statement is responsible for ending the trace which it
    has started; this class achieves this by keeping some memory inside.
  */
  Opt_trace_start(THD *thd_arg, Table_ref *tbl,
                  enum enum_sql_command sql_command,
                  List<set_var_base> *set_vars, const char *query,
                  size_t query_length, sp_printable *instr,
                  const CHARSET_INFO *query_charset);
  ~Opt_trace_start();

 private:
  Opt_trace_context *const ctx;
  bool error;  ///< whether trace start() had an error
};

class Query_block;

/**
   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  query_block  query's parse tree
   @param  trace_object  Opt_trace_object to which the query will be added
*/
void opt_trace_print_expanded_query(const THD *thd, Query_block *query_block,
                                    Opt_trace_object *trace_object);

/**
   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         the 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);

/**
   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_ref *view,
                                         Table_ref *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 The 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_ref *tables, Item *);

//@}

/**
   Helper for defining query-transformation-related trace objects in one
   code line. This produces
   {
     "transformation": {
       "select#": @<select_number@>,
       "from": @<from@>,
       "to": @<to@>
   The objects are left open, so that one can add more to them (often a
   "chosen" property after making some computation). Objects get closed when
   going out of scope as usual.
   @param trace          optimizer trace
   @param object_level0  name of the outer Opt_trace_object C++ object
   @param object_level1  name of the inner Opt_trace_object C++ object
   @param select_number  number of the being-transformed Query_block
   @param from           description of the before-transformation state
   @param to             description of the after-transformation state
*/
#define OPT_TRACE_TRANSFORM(trace, object_level0, object_level1, \
                            select_number, from, to)             \
  Opt_trace_object object_level0(trace);                         \
  Opt_trace_object object_level1(trace, "transformation");       \
  object_level1.add_select_number(select_number);                \
  object_level1.add_alnum("from", from).add_alnum("to", to);

#endif /* OPT_TRACE_INCLUDED */