1# ==== Purpose ====
2#
3# Evaluate an expression, possibly containing arbitrary
4# sub-expressions from different connections.
5#
6# The expression is parsed before executed. The following constructs
7# are supported:
8#
9#   [SQL_STATEMENT, COLUMN, ROW]
10#     The square bracket is replaced by the result from SQL_STATEMENT,
11#     in the given COLUMN and ROW.
12#
13#     Optionally, SQL_STATEMENT may have the form:
14#       connection:SQL_STATEMENT
15#     In this case, SQL_STATEMENT is executed on the named connection.
16#     All other queries executed by this script will be executed on
17#     the connection that was in use when this script was started.
18#     The current connection will also be restored at the end of this
19#     script.
20#
21#     It is also possible to nest sub-statements on this form, like:
22#       [master:SHOW BINLOG EVENTS FROM
23#        [slave:SHOW SLAVE STATUS, Master_Log_Pos, 1],
24#        1, Position]
25#
26#   [SQL_STATEMENT]
27#     Shortcut to the above form, usable when the result has only one
28#     row and one column.
29#
30#   <1>
31#     This is a shorthand for the result of the first executed square
32#     bracket. <2> is a shorthand for the second executed square
33#     bracket, and so on.
34#
35# ==== Usage ====
36#
37# --let $eval_expr= [SHOW SLAVE STATUS, Relay_Log_Pos, 1] + 47
38# [--let $eval_no_result= 1]
39# [--let $rpl_debug= 1]
40# --source include/eval.inc
41# --echo Result was '$eval_result'
42#
43# Parameters:
44#
45# $eval_expr
46#   Expression to evaluate.  See above for details about the format.  The
47#   expression will be executed as `SELECT $eval_expr`.
48#
49#   Both $eval_expr and the result from any substatement on the
50#   form [SQL_STATEMENT, COLUMN, ROW] will be used in SQL statements,
51#   inside single quotes (as in '$eval_expr').  So any single quotes
52#   in these texts must be escaped or replaced by double quotes.
53#
54# $eval_no_result
55#   By default, the expression is evaluated inside 'SELECT' and the
56#   result is stored in $eval_result.  If this variable is set, the
57#   expression is instead evaluated as it is and the result is not
58#   stored anywhere.
59#
60# $rpl_debug
61#   Print extra debug info.
62#
63# Return value:
64#   The result is stored in $eval_result.
65
66--let $include_filename= eval.inc
67--source include/begin_include_file.inc
68
69if ($rpl_debug)
70{
71  --echo # debug: eval_expr='$eval_expr' eval_no_result='$eval_no_result'
72}
73
74--let $_eval_old_connection= $CURRENT_CONNECTION
75
76# Evaluate square brackets in expr.
77--let $_eval_substmt_number= 1
78--let $_eval_expr_interp= '$eval_expr'
79--let $_eval_rbracket= `SELECT LOCATE(']', $_eval_expr_interp)`
80while ($_eval_rbracket)
81{
82  # Get position of right bracket
83  --let $_eval_lbracket= `SELECT $_eval_rbracket - LENGTH(SUBSTRING_INDEX(SUBSTR($_eval_expr_interp, 1, $_eval_rbracket), '[', -1))`
84  if ($_eval_lbracket == 0)
85  {
86    --echo BUG IN TEST: Mismatching square brackets in eval_expr.
87    --echo Original eval_expr='$eval_expr'
88    --echo Interpolated eval_expr=$_eval_expr_interp
89    --die BUG IN TEST: Mismatching square brackets in $eval_expr
90  }
91
92  # Get sub-statement from statement. Preserve escapes for single quotes.
93  --let $_eval_full_substmt= `SELECT QUOTE(SUBSTRING($_eval_expr_interp, $_eval_lbracket + 1, $_eval_rbracket - $_eval_lbracket - 1))`
94
95  # Get connection from sub-statement
96  --let $_eval_colon= `SELECT IF($_eval_full_substmt REGEXP '^[a-zA-Z_][a-zA-Z_0-9]*:', LOCATE(':', $_eval_full_substmt), 0)`
97  --let $_eval_connection=
98  --let $_eval_substmt= $_eval_full_substmt
99  if ($_eval_colon)
100  {
101    --let $_eval_connection= `SELECT SUBSTRING($_eval_substmt, 1, $_eval_colon - 1)`
102    # Preserve escapes for single quotes.
103    --let $_eval_substmt= `SELECT QUOTE(SUBSTRING($_eval_substmt, $_eval_colon + 1))`
104  }
105
106  # Interpolate escapes before using expression outside string context.
107  --let $_eval_substmt_interp= `SELECT $_eval_substmt`
108
109  # Change connection
110  if ($_eval_connection)
111  {
112    if ($rpl_debug)
113    {
114      --echo # debug: connection='$_eval_connection' sub-statement=$_eval_substmt
115    }
116    --let $rpl_connection_name= $_eval_connection
117    --source include/rpl_connection.inc
118  }
119  if (!$_eval_connection)
120  {
121    if ($rpl_debug)
122    {
123      --echo # debug: old connection, sub-statement=$_eval_substmt
124    }
125  }
126
127  # Execute and get result from sub-statement.
128  # Can't use dollar to denote end of string because mtr will try to
129  # interpolate it.
130  --let $selected_row_col= `SELECT CONCAT($_eval_substmt, 'ZZENDZZ') REGEXP '[a-zA-Z_][a-zA-Z0-9_]* *, *[0-9][0-9]* *ZZENDZZ'`
131
132  if ($selected_row_col)
133  {
134    --let $_eval_substmt_result= query_get_value($_eval_substmt_interp)
135  }
136  if (!$selected_row_col)
137  {
138    --let $_eval_substmt_result= `$_eval_substmt_interp`
139  }
140
141  # Change back connection
142  if ($_eval_connection)
143  {
144    --let $rpl_connection_name= $_eval_old_connection
145    --source include/rpl_connection.inc
146  }
147
148  if ($rpl_debug)
149  {
150    --echo # debug: result of sub-statement='$_eval_substmt_result'
151  }
152
153  # Replace sub-statement by its result
154  --let $_eval_expr_interp= `SELECT QUOTE(REPLACE($_eval_expr_interp, CONCAT('[', $_eval_full_substmt, ']'), '$_eval_substmt_result'))`
155  # Replace result references by result
156  --let $_eval_expr_interp= `SELECT QUOTE(REPLACE($_eval_expr_interp, '<$_eval_substmt_number>', '$_eval_substmt_result'))`
157
158  --let $_eval_rbracket= `SELECT LOCATE(']', $_eval_expr_interp)`
159
160  --inc $_eval_substmt_number
161}
162
163# Interpolate escapes before using expression outside string context.
164--let $_eval_expr_interp= `SELECT $_eval_expr_interp`
165
166if ($rpl_debug)
167{
168  --echo # debug: interpolated_expr='$_eval_expr_interp'
169}
170
171# Execute.
172if ($eval_no_result)
173{
174  --eval $_eval_expr_interp
175}
176if (!$eval_no_result)
177{
178  --let $eval_result= `SELECT $_eval_expr_interp`
179}
180
181if ($rpl_debug)
182{
183  --echo # debug: result='$eval_result'
184}
185
186--let $include_filename= eval.inc
187--source include/end_include_file.inc
188