1 /*
2 * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2.0,
6 * as published by the Free Software Foundation.
7 *
8 * This program is also distributed with certain software (including
9 * but not limited to OpenSSL) that is licensed under separate terms,
10 * as designated in a particular file or component or in included license
11 * documentation. The authors of MySQL hereby grant you an additional
12 * permission to link the program and your derivative works with the
13 * separately licensed software that they have included with MySQL.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License, version 2.0, for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23 * 02110-1301 USA
24 */
25
26 #include "expect.h"
27 #include "xpl_error.h"
28 #include "ngs_common/protocol_protobuf.h"
29 //#include "expect_gtid.h"
30
31 using namespace xpl;
32
33 // :docnote:
34 // NO_ERROR means "enable exceptions", meaning any error that happens inside a block
35 // will cause all subsequent statements to fail until the matching close is found.
36 //
37 // Nesting behaviour:
38 //
39 // Case 1: No_error
40 //
41 // open(NO_ERROR) - ok
42 // stmt1 - ok
43 // stmt2 - error
44 // stmt3 - fail
45 // close() - fail
46 //
47 // Case 2: Plain
48 //
49 // open() - ok
50 // stmt1 - ok
51 // stmt2 - error
52 // stmt3 - ok
53 // close() - ok
54 //
55 // Case 3: No_error nested within no_error
56 //
57 // 3.1: error in outer block fails the whole thing
58 // open(NO_ERROR) - ok
59 // stmt1 - error
60 // open(NO_ERROR) - fail
61 // stmt2 - fail
62 // close() - fail
63 // stmt3 - fail
64 // close() - fail
65 //
66 // 3.2: error propagates up and fails the outer block
67 // open(NO_ERROR) - ok
68 // stmt1 - ok
69 // open(NO_ERROR) - ok
70 // stmt2 - error
71 // close() - fail
72 // stmt3 - fail
73 // close() - fail
74 //
75 // Case 4: Plain nested within no_error
76 //
77 // 4.1: same as 3.1
78 // open(NO_ERROR) - ok
79 // stmt1 - error
80 // open() - fail
81 // stmt2 - fail
82 // close() - fail
83 // stmt3 - fail
84 // close() - fail
85 //
86 // 4.2: plain block effectively "catches" the error and prevents it from failing the outer block
87 // open(NO_ERROR) - ok
88 // stmt1 - ok
89 // open() - ok
90 // stmt2 - error
91 // close() - ok
92 // stmt3 - ok
93 // close() - ok
94 //
95 // Case 5: No_error nested within Plain
96 //
97 // 5.1: trivial
98 // open() - ok
99 // stmt1 - error
100 // open(NO_ERROR) - ok
101 // stmt2 - ok
102 // close() - ok
103 // stmt3 - ok
104 // close() - ok
105 //
106 // 5.2: error propagates up, but is ignored by the outer block
107 // open() - ok
108 // stmt1 - ok
109 // open(NO_ERROR) - ok
110 // stmt2 - error
111 // close() - fail
112 // stmt3 - ok
113 // close() - ok
114 //
115 // Case 6: Plain nested within plain: trivial, behaves like a flat plain block
116 //
117
118
119 static const unsigned EXPECT_NO_ERROR = 1;
120 //static const int EXPECT_SCHEMA_VERSION = 2; // not supported yet
121 //static const int EXPECT_GTID_EXECUTED_CONTAINS = 3;
122 //static const int EXPECT_GTID_WAIT_LESS_THAN = 4;
123
124
125
126 // if (pre_client_stmt(msgid))
127 // {
128 // error = ExecuteCommand()
129 // post_client_stmt(msgid, error)
130 // }
131
132
Expectation(const Expectation & other)133 Expectation::Expectation(const Expectation &other)
134 : m_failed(other.m_failed), m_fail_on_error(other.m_fail_on_error), m_gtid_wait_less_than(0) // this is instance specific data, don't copy it
135 {
136 for (std::list<Expect_condition*>::const_iterator cond = other.m_conditions.begin();
137 cond != other.m_conditions.end(); ++cond)
138 {
139 m_conditions.push_back((*cond)->copy());
140 }
141 }
142
143
swap(Expectation & one,Expectation & other)144 void Expectation::swap(Expectation &one, Expectation &other)
145 {
146 using std::swap;
147 swap(one.m_failed, other.m_failed);
148 swap(one.m_fail_on_error, other.m_fail_on_error);
149 swap(one.m_conditions, other.m_conditions);
150 }
151
152
operator =(const Expectation & other)153 Expectation &Expectation::operator =(const Expectation &other)
154 {
155 Expectation tmp(other);
156 swap(*this, tmp);
157 return *this;
158 }
159
160
~Expectation()161 Expectation::~Expectation()
162 {
163 for (std::list<Expect_condition*>::iterator cond = m_conditions.begin();
164 cond != m_conditions.end(); ++cond)
165 delete *cond;
166 }
167
168
check()169 ngs::Error_code Expectation::check()
170 {
171 for (std::list<Expect_condition*>::const_iterator cond = m_conditions.begin();
172 cond != m_conditions.end(); ++cond)
173 {
174 ngs::Error_code error((*cond)->check());
175 if (error)
176 return error;
177 }
178 return ngs::Error_code();
179 }
180
181
unset(uint32_t key)182 void Expectation::unset(uint32_t key)
183 {
184 if (key == EXPECT_NO_ERROR)
185 {
186 m_fail_on_error = false;
187 return;
188 }
189
190 for (std::list<Expect_condition*>::iterator cond = m_conditions.begin();
191 cond != m_conditions.end(); ++cond)
192 {
193 if ((*cond)->key() == key)
194 {
195 delete *cond;
196 m_conditions.erase(cond);
197 break;
198 }
199 }
200 }
201
202
add_condition(Expect_condition * cond)203 void Expectation::add_condition(Expect_condition *cond)
204 {
205 m_conditions.push_back(cond);
206 }
207
208
set(uint32_t key,const std::string & value)209 ngs::Error_code Expectation::set(uint32_t key, const std::string &value)
210 {
211 switch (key)
212 {
213 case EXPECT_NO_ERROR:
214 if (value == "1" || value.empty())
215 m_fail_on_error = true;
216 else if (value == "0")
217 m_fail_on_error = false;
218 else
219 return ngs::Error_code(ER_X_EXPECT_BAD_CONDITION_VALUE, "Invalid value '"+value+"' for expectation no_error");
220 break;
221 /*
222 case EXPECT_GTID_EXECUTED_CONTAINS:
223 {
224 Expect_gtid *gtid;
225 add_condition(gtid = new Expect_gtid(value));
226 gtid->set_key(key);
227 // timeout was set first
228 if (m_gtid_wait_less_than > 0)
229 gtid->set_timeout(m_gtid_wait_less_than);
230 break;
231 }
232 case EXPECT_GTID_WAIT_LESS_THAN:
233 m_gtid_wait_less_than = ngs::stoi(value);
234 for (std::list<Expect_condition*>::iterator cond = m_conditions.begin();
235 cond != m_conditions.end(); ++cond)
236 {
237 if ((*cond)->key() == EXPECT_GTID_EXECUTED_CONTAINS)
238 {
239 static_cast<Expect_gtid*>(*cond)->set_timeout(m_gtid_wait_less_than);
240 break;
241 }
242 }
243 break;
244 */
245 default:
246 return ngs::Error_code(ER_X_EXPECT_BAD_CONDITION, "Unknown condition key");
247 }
248 return ngs::Error_code();
249 }
250
251
252
253
Expectation_stack()254 Expectation_stack::Expectation_stack()
255 {
256 m_expect_stack.reserve(4);
257 }
258
259
open(const Mysqlx::Expect::Open & open)260 ngs::Error_code Expectation_stack::open(const Mysqlx::Expect::Open &open)
261 {
262 ngs::Error_code error;
263 Expectation expect;
264
265 // if we're in a failed expect block, push an empty failed expectation to the stack
266 // so that it can be popped when the matching close is seen.
267 // No other evaluations are done in a failed state.
268 if (!m_expect_stack.empty())
269 {
270 if (m_expect_stack.back().failed())
271 {
272 expect.set_failed(m_expect_stack.back().failed_condition());
273 m_expect_stack.push_back(expect);
274 return ngs::Error_code(ER_X_EXPECT_FAILED, "Expectation failed: "+expect.failed_condition());
275 }
276
277 if (open.op() == Mysqlx::Expect::Open::EXPECT_CTX_COPY_PREV)
278 expect = m_expect_stack.back();
279 }
280
281 for (int i = 0; i < open.cond_size(); i++)
282 {
283 const Mysqlx::Expect::Open::Condition &cond(open.cond(i));
284 switch (cond.op())
285 {
286 case Mysqlx::Expect::Open::Condition::EXPECT_OP_SET:
287 if (!cond.has_condition_value())
288 error = expect.set(cond.condition_key(), "");
289 else
290 error = expect.set(cond.condition_key(), cond.condition_value());
291 break;
292 case Mysqlx::Expect::Open::Condition::EXPECT_OP_UNSET:
293 expect.unset(cond.condition_key());
294 break;
295 }
296 if (error)
297 return error;
298 }
299 // we need to add the expectation block even if an error occurred,
300 // otherwise we'll get mismatched open/close blocks
301 // on_error should get called afterwards with this error, which should fail the rest of the block
302 m_expect_stack.push_back(expect);
303
304 // now check for the expected conditions
305 // this may block if a blocking condition is found
306 if (!error)
307 error = expect.check();
308
309 return error;
310 }
311
312
close()313 ngs::Error_code Expectation_stack::close()
314 {
315 if (m_expect_stack.empty())
316 return ngs::Error_code(ER_X_EXPECT_NOT_OPEN, "Expect block currently not open");
317
318 if (m_expect_stack.back().failed())
319 {
320 std::string cond = m_expect_stack.back().failed_condition();
321 m_expect_stack.pop_back();
322 return ngs::Error_code(ER_X_EXPECT_FAILED, "Expectation failed: " + cond);
323 }
324
325 m_expect_stack.pop_back();
326
327 return ngs::Error_code();
328 }
329
330
331 // called before executing client statements
pre_client_stmt(int8_t msgid)332 ngs::Error_code Expectation_stack::pre_client_stmt(int8_t msgid)
333 {
334 if (!m_expect_stack.empty())
335 {
336 if (m_expect_stack.back().failed())
337 {
338 // special handling for nested expect blocks
339 // if a block open or close arrives in a failed state, we let it through
340 // so that they can be pushed/popped on the stack and properly accounted for
341 if (msgid != Mysqlx::ClientMessages::EXPECT_OPEN && msgid != Mysqlx::ClientMessages::EXPECT_CLOSE)
342 return ngs::Error_code(ER_X_EXPECT_FAILED, "Expectation failed: " + m_expect_stack.back().failed_condition());
343 }
344 }
345 return ngs::Error_code();
346 }
347
348
349 // called after executing client statements
post_client_stmt(int8_t msgid,const ngs::Error_code & error)350 void Expectation_stack::post_client_stmt(int8_t msgid, const ngs::Error_code &error)
351 {
352 if (error && !m_expect_stack.empty() && m_expect_stack.back().fail_on_error())
353 m_expect_stack.back().set_failed("no_error");
354 }
355